堆体系结构

前面提到的虚拟机栈主管Java程序的运行,而这里的堆(Heap)则是主管程序内存。Java堆是Java虚拟机管理内存中的最大一块,是所有线程共享的一块内存管理区域。 此内存区域唯一目的就是存放对象的实例,几乎所有对象实例都在堆中分配内存。方法区(Mathod Area)其实也是Java堆的一部分,但是为了从逻辑上区分方法区才出现方法区这一很小的内存空间。因为几乎所有的对象实例都存放在堆中,所以堆是垃圾收集器管理的主要区域。Java内存(不包括方法区)从逻辑上可以分为三块区域,其中第1和2店是堆内存,如下所示:

  • Young Generation Space,新生区、新生代
  • Tenure Generation Space,老年区、老年代
  • Permanent Space,永久区、元空间

其中新生区还可以细分为:伊甸园区(Eden)、幸存区(0区、1区,又叫from区和to区);
image.png
下面是堆内存的内存大小分布图:image.png

对象在堆中的生命周期

新生区是对象诞生、成长、消亡的区域,一个对象在这里产生、应用,最后被垃圾回收器回收,结束生命。所有的对象在伊甸园区被new出来,如果伊甸园区的空间用完了,但程序有还需要创建对象的话,JVM的垃圾回收器就是使用 Minor GC(轻GC)来对伊甸园区中的不再被其他对象所引用的对象进行销毁,而未被轻GC销毁的对象就会进入到幸存区0区,如果幸存区0区也满了就会再使用轻GC对此区域进行清理,还存活下来的对象就进入幸存区1区。如果幸存区1区也满了,最后就会进入养老区。如果最后连养老区都满了,这时候就是使用Major GC(Full GC,也叫重GC)来清理养老区。如果使用重GC清理之后内存还是满的,就会产生OOM错误( OutOfMemoryError )。
如果出现java.lang.OutOfMemoryError:Java heap space 异常,说明虚拟机的堆内存不够,原因可能有:

  • java虚拟机的堆内存设置不够,可以通过参数 -Xms、-Xmx来调整
  • 代码中创建了大量大对象,并且长时间不能被垃圾回收器收集(存在被引用)

复制—>清空—>互换

  • eden、From Survivor复制到 To Survivor,年龄+1
    • 首先,当Eden区满的时候,会触发第一次GC,把还活着的对象拷贝到From Survivor 幸存0区,当Eden区再次触发GC的时候会扫描Eden区和From区域,对这两个区域进行垃圾回收,经过第二次回收后,还存活的对象,则直接复制到To区(如果有对象的年龄已经达到老年的标准,则赋值到养老区),同时把这些对象的年龄+1
  • 清空eden、From Survivor
    • 清空Eden和From Survivor中的对象,也即复制之后有交换,谁空谁是to
  • To Survivor和 FromSurvivor互换

    • 最后,To Survivor和From Survivor互换,原To Survivor成为下一次的GC时的From Survivor区,部分对象会在From和To区域复制来复制去,如此交换15次(由JVM参数MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到养老区。

      堆参数调优

      可以使用Java代码获取虚拟机的内存:

      1. public class Test {
      2. public static void main(String[] args) {
      3. //返回Java虚拟机试图使用的最大内存量
      4. long maxMemory = Runtime.getRuntime().maxMemory();
      5. //返回Java虚拟机中的内存总数
      6. long totalMemory = Runtime.getRuntime().totalMemory();
      7. System.out.println("-Xmx:MAX_MEMORY =" + maxMemory + "(字节) 即约" + (maxMemory / (double) 1024 / 1024) + "MB");
      8. System.out.println("-Xms:TOTAL_MEMORY =" + totalMemory + "(字节) 即约" + (totalMemory / (double) 1024 / 1024) + "MB");
      9. }
      10. }

      image.png

参数 说明
-Xms 设置初始内存分配大小,默认为物理内存的 1/64
-Xmx 最大分配内存,默认为物理内存的 1/4
-XX:+PrintGCDetails 输出详细的GC处理日志

image.png
image.png
虚拟机的堆内存是有固定大小的,当堆内存不够就会出现OOM。