系列七、JVM的内存结构【堆(Heap)】

系列七、JVM的内存结构【堆(Heap)】_第1张图片

一、概述

        一个JVM实例只存在一个堆内存,堆内存的大小是可以手动调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三个部分,即:新生区、养老区、永久区(Java7)/元空间(Java8)

系列七、JVM的内存结构【堆(Heap)】_第2张图片

1.1、新生区

        新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为2部分,即:伊甸区(Eden space)和幸存者区(Survivor space),所有的类都是在伊甸区被new出来的。幸存区有2个:0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸区的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸区的垃圾进行回收(Minor GC),将伊甸区中的不再被其他对象所引用的对象进行销毁。
   如果出现了java.lang.OutOfMemoryError:Java heap space异常,说明java虚拟机的堆内存不够,原因有二:
   1、java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整;
   2、代码中创建了大量大对象,并且长时间不能被垃圾回收器收集(该对象还被引用着);

1.1.1、Minor GC的过程

复制===>清空===》交换 

1、eden、from区中的对象复制到to区,年龄+1

        首先,当eden区满的时候会触发第一次GC,把还活着的对象拷贝到from区,当eden区再次满的时候会扫描eden区和from区,对这两个区域的对象进行垃圾回收,经过这次回收后还存活着的对象,则直接复制到to区(如果有对象的年龄已经达到了老年的标准,则复制到老年代),同时把这些对象的年龄+1;

2、清空eden、from区

        然后清空eden区和from区中的的对象,原来的from区变to区,to区变为新一轮的from区,也即复制之后有交换,谁空谁是to;

3、进入老年代

        部分对象会在from区和to区中复制来复制去,如此交换15次(由JVM的参数MaxTenuringThreshold决定,这个参数默认值为15)之后,如果还活着将进入老年代。

1.2、养老区

存放的是经历过15次完整垃圾回收的对象。

1.3、元空间

         实际而言,方法区(Method Area)和堆一样,是各个线程共享的内存区域,用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器编译后的代码等等,虽然JVM规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做Non-Heap(非堆),目的就是要和堆分开。   


        对于HotSpot虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)” ,但严格本质上说两者不同,或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口interface)的一个实现,jdk1.7的版本中,已经将原本放在永久代的字符串常量池移走。

        永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。

系列七、JVM的内存结构【堆(Heap)】_第3张图片 

二、堆内存参数调优

2.1、堆内存参数调优调哪些参数

-Xms 堆空间的最小值,默认为物理内存的1/64
-Xmx 堆空间的最大值,默认为物理内存的1/4
-XX:+PrintGCDetails 输出详细的GC处理日志

 

 

 

2.2、堆内存的默认物理内存 

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/16 14:50
 * @Description: 获取堆内存的默认物理内存大小
 */
public class HeapMainApp {
 
    public static void main(String[] args) {
        // Java虚拟机试图使用的最大内存
        long maxMemory = Runtime.getRuntime().maxMemory();
        // 当前Java虚拟机中的内存总量
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println("【-Xmx】最大内存 = " + maxMemory + " (字节)、" + (maxMemory / (double) 1024 / 1024) + " (MB)");
        System.out.println("【-Xms】总内存 = " + totalMemory + " (字节)、" + (totalMemory / (double) 1024 / 1024) + " (MB)");
    }
 
}

系列七、JVM的内存结构【堆(Heap)】_第4张图片

2.3、修改堆内存大小

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

系列七、JVM的内存结构【堆(Heap)】_第5张图片

系列七、JVM的内存结构【堆(Heap)】_第6张图片 

三、OOM异常演示

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/16 15:31
 * @Description: 修改堆内存的最大值和最小值为10m,方便观察GC的回收过程
 * -Xms10m -Xmx10m -XX:+PrintGCDetails
 */
public class OomMainApp {
 
    public static void main(String[] args) {
        String message = "欧耶,今天星期五!";
        while (true) {
            message += message + new Random().nextInt(88888888) + new Random(99999999);
        }
    }
 
}

系列七、JVM的内存结构【堆(Heap)】_第7张图片

四、获取本机的cpu核数

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/11/16 14:39
 * @Description: 获取本机的电脑核数
 */
public class CpuMainApp {
 
    public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
 
}

系列七、JVM的内存结构【堆(Heap)】_第8张图片

你可能感兴趣的:(JVM系列,jvm)