系列七、JVM的内存结构【堆(Heap)】
一、概述
一个JVM实例只存在一个堆内存,堆内存的大小是可以手动调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三个部分,即:新生区、养老区、永久区(Java7)/元空间(Java8)
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 才会释放此区域所占用的内存。
二、堆内存参数调优
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)");
}
}
2.3、修改堆内存大小
-Xms1024m -Xmx1024m -XX:+PrintGCDetails
三、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);
}
}
}
四、获取本机的cpu核数
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/16 14:39
* @Description: 获取本机的电脑核数
*/
public class CpuMainApp {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
}
}