JVM内存分配与回收

1、堆的概念

堆(heap):Java中的堆是JVM管理的最大的一块内存空间,主要用于存放各种类的实例对象

clipboard.png
  • 新生代(Young)

  • Eden区(伊甸园区)

  • Survivor区(幸存者区)

  • From Survivor区

  • To Survivor区

  • 老年代(Old)

  • 新生代与老年代 比例值为 1 : 2(通过–XX:NewRatio设置),即:新生代占1/3,老年代占2/3

  • 新生代又分为Eden区和两个Survivior区,这两个Survivior区分别命名为From区和To区,Eden区默认比例值为Eden:From:To=8:1:1(通过 –XX:SurvivorRatio设置),即Eden区占8/10,From区占用1/10,To区占用1/10

2、对象进入老年代的场景

  • 大对象直接进入老年代

大对象就是需要连续存储空间的对象(-XX:PretenureSizeThreshold 可以设置大对象的大小),如果对象的大小超过这个设定的空间,那么该对象会直接进入老年代,不会进入年轻代。这个参数只在Serial和ParNew两个收集器下有效。

作用:为了避免大对象在Eden区和两个Survivior区发生大量的内存复制,JVM默认PretenureSizeThreshold值为0,意味着任何对象都会在新生代创建

-XX:PretenureSizeThreshold=1000000 -XX:+UseSerialGC

  • 如果在新生代分配失败,且对象是一个不含任何引用的空数组,那这个大的空数组会直接进入老年代
  • 长期存活的对象将进入老年代

虚拟机采取分代收集的思想来管理内存,对象在Eden区出生并经历过第一次Minor GC后仍然能够存活并且可以被Survivor区容纳的话,将对象移动到Survivor区空间后,该对象的年龄为1。对象在Survivor区每熬过一次Minor GC则年龄会+1,当年龄大小等于最大年龄时(默认为15岁,-XX:MaxTenuringThreshold设置),则会被晋升到老年代。

-XX:MaxTenuringThreshold=10

  • 对象动态年龄判断

当前存放和对象的某一个Survivor区的所有对象,如果对象的大小总和大于Survivor区内存大小的50%,那么大于等于这批对象年龄最大值的对象全部进入老年代。比如Survivor区有一批对象,年龄1+年龄2+年龄3+年龄n的多个年龄对象大小综合大于Survivor区内存大小的50%,那么年龄>=n的对象全部进入老年代

作用:希望那些长期存活的对象尽早的进入老年代,对象动态年龄判断机制一般在Minor GC之后触发

  • Minor GC后存活的对象在Survivor区存不下,会把存放不下的对象分配到老年代

3、触发Minor GC的条件

大量对象分配到了Eden区,当Eden区存满时触发一次Minor GC,可能99%的对象会被垃圾回收,剩下存活的地方会被移动到Survivor区的From区,再下一次Eden区存满的时候,会再一次触发Minor GC,将Eden区和From区存活的对方移动到Survivor区的To区,依次循环。

4、触发Full GC的场景

  • 老年代空间分配担保机制
clipboard.png
  • 老年代空间不足时,会触发Full GC,-XX:CMSInitiatingOccupancyFaction=92% 参数设置比例,当老年代内存占比超过该比例时则会触发一次Full GC
  • Metaspace空间不足时,会触发一次Full GC
  • 调用System.gc()方法,可能会触发一次Full GC

5、判断对象可回收方法

  • 引用计数法:给对象添加一个引用计数器,新增一个引用时+1,引用失效时-1,当引用计数器值为0时,则表示对象可被回收

弊端:循环引用的对象无法被回收,不被使用

  • 可达性分析法:通过一系列“GC Roots”的对象作为起点,从这些起点开始向下查找,找到的对象标记为可用对象,其余为标记的对象为可回收对象。

GC Roots根节点有:线程栈的局部变量、静态变量、成员变量、本地方法栈的变量等

6、方法区判断一个类是否可回收条件

  • 该类的所有实例均已被回收,在Java堆中不存在该类的任何实例
  • 加载该类的类加载器对象已经被回收
  • 该类的class对象没有任何对方被引用,无法通过反射访问该对象的方法

7、垃圾收集算法

  • 标记-清除算法:首先标记出所有需要删除的对象,标记完成后统一对这些标记对象进行垃圾回收

优点:效率高

缺点:空间问题,标记清除后产生大量的不连续的内存碎片

clipboard.png
  • 复制算法:将内存划分为两个相等大小的区域,每次只使用其中的一块,当一块内存使用完后,将该内存的存活对象转移到另一块空白区域中,然后在一次性清理掉当前使用的那块区域内存。
clipboard.png
  • 标记-清理算法:首先标记出所有需要删除的对象,将存活的对象向一端移动,覆盖掉需删除的对象,再清理掉边界以外的内存
clipboard.png
  • 分代收集算法:虚拟机采用分代收集算法,针对对象存活周期的不同,将内存区域分为新生代和老年代,根据不同的区域选择不同的垃圾收集算法,比如在新生代中绝大多数对象都是朝生夕死,所以选择了复制算法,这样可以通过对少量对象的复制达到垃圾收集的效果,而老年代的存活的对象时间较长,采用标记-清除算法或者标记-整理算法

推荐阅读更多精彩内容