JVM内存概念

1. JVM内存区域分配


  • Java堆是各线程共享的内存区域,在JVM启动时创建,这块区域是JVM中最大的, 用于存储应用的对象数组,也是GC主要的回收区,一个 JVM 实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分:新生代、老年代、永久代。
    说明:
    Jdk1.6及之前:常量池分配在永久代 。
    Jdk1.7:有,但已经逐步“去永久代” 。
    Jdk1.8及之后:无永久代,改用元空间代替(java.lang.OutOfMemoryError: PermGen space,这种错误将不会出现在JDK1.8中)。

  • 栈(线程栈)
    Java栈是线程私有的,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一致。基本类型的变量和对象的引用变量都是在函数的栈内存中分配。
    每个方法执行的时候都会创建一个栈帧,栈帧中主要存储3类数据:
    局部变量表:输入参数和输出参数以及方法内的变量;
    栈操作:记录出栈和入栈的操作;
    栈帧数据:包括类文件、方法等等。

  • 栈帧
    一个方法对应一个栈帧内存空间 每个方法都有独立的栈帧内存空间,
    栈数据结构: 先进后出销毁。
    栈帧内部细节结构: 局部变量表、操作数栈、动态链接、方法出口
    栈帧 就是每个方法需要的运行时内存空间

  • 每个运行时所需要的内存,称作为虚拟机栈

  • 每个栈有多个栈帧组成,对应着每次方法调用时占用的内存

  • 每个线程只能有一个活动的栈,对应着当前正在执行的方法。

  1. 程序计数器
    记录当前线程执行下一行指令的执行地址.
  2. 本地方法栈
    本地方法栈和JVM栈发挥的作用非常相似,也是线程私有的,区别是JVM栈为JVM执行Java方法(也就是字节码)服务,而本地方法栈为JVM使用到的Native方法服务。它的具体做法是在本地方法栈中登记native方法,在执行引擎执行时加载Native Liberies.有的虚拟机(比如Sun Hotpot)直接把两者合二为一。
    也就是 java调用c语言代码 jni技术

2. 栈内存溢出

栈帧调用过多内存溢出 (方法递归调用)

/**
 * 栈内存溢出
 *
 * -Xss256k
 * @author tostyle
 * @date 2022-05-17 9:15
 */
public class StackTest {
    private static int count;
    public static void main(String[] args) {
        stack01();
    }
    private static void stack01() {
        count++;
        System.out.println("count:" + count);
        stack01();
    }
}

3.堆内存溢出

在申请内存的时候,内存不足 产生堆内存溢出

/**
 *
 * -Xmx8m
 * @author tostyle
 * @date 2022-05-17 9:20
 */
public class HeapTest {
    public static void main(String[] args) {
        int i = 0;
        try {
            ArrayList<String> strings = new ArrayList<>();
            while (true) {
                strings.add("mayikt");
                i++;
            }
        } catch (Exception e) {
        }
    }
}

4.堆内存泄漏

java.lang.OutOfMemoryError: GC overhead limit exceeded,这种机制也会有一些问题,就是被占用的内存,经过多次长时间的GC操作都无法回收,导致可用内存越来越少,俗称内存泄露,JVM就会报java.lang.OutOfMemoryError: GC overhead limit exceeded错误。

/**
 * 堆内存泄漏
 * -Xmx3M -Xms3M
 * @author tostyle
 * @date 2022-05-17 9:25
 */
public class HashMapMemoryLeak {

    public static void main(String[] args) {

        HashMap<HashKey2, Integer> map = new HashMap<HashKey2, Integer>(1000);
        int counter = 0;
        while (true) {
            //循环插入新对象 new出很多很多内存地址不等的对象但是
            HashKey2 p = new HashKey2("mayikt", "644064779");
            map.put(p, 1);
            counter++;
            if (counter % 1000 == 0) {
                System.out.println("map size: " + map.size());
                System.out.println("运行" + counter
                        + "次后,可用内存剩余" + Runtime.getRuntime().freeMemory() / (1024 * 1024) + "MB");
            }
        }
    }
}

class HashKey2 {
    private final String id;
    private String name;

    public HashKey2(String name, String id) {
        this.name = name;
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    /**
     * hashMap 不管怎么new 多少次 只会key 只会引入一次 不会继续添加。
     *
     * @param obj
     * @return
     */
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof HashKey2) {
            return name.equals(((HashKey2) obj).name) && id.equals(((HashKey2) obj).id);
        }else {
            return false;
        }
    }

}

5. 堆内存诊断工具

  • Jps工具 - 查看当前系统有那些java进程
Jps
jmap -heap 进程id
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值