垃圾收集器与内存分配策略 -- 内存分配与回收策略


Java堆的结构分布如下图所示

垃圾收集器与内存分配策略 -- 内存分配与回收策略_第1张图片
Java堆的结构分布.png

新生代与老年代GC回收如图所示

垃圾收集器与内存分配策略 -- 内存分配与回收策略_第2张图片
Java-Memory-Model.png

图片来源 https://www.journaldev.com/2856/java-jvm-memory-model-memory-management-in-java

新生代

新生代主要用来存放新生的对象,一般占据堆1/3的空间。
新生代分为 Eden区、FromServivor、ToServivor三个区,三者的空间比例默认是8:1:1。

Eden区:Java新对象分配地,该区域的对象生命周期一般来讲都很短,当该区域没有足够的空间分配对象时,将会进行Minor GC,该区域采用复制算法收集内存。若该对象需要大量连续内存空间,比如很长的字符串或者数组等,存入Eden区会导致在还有不少空间时提前进行 Minor GC,因此虚拟机提供一个参数:-XX: PretenureSizeThreshold 参数,令大于这个参数值的对象会直接进入到老年代分配。

FromServivor区:保留上一次GC的幸存者

ToServivor区:保存Minor GC中存活的对象(采用复制算法将Eden区与FromServivor区中非垃圾对象复制到该区域中保存,若对象过大就会直接放到老年代)

Minor GC:从新生代空间(包括 Eden 和 Survivor 区域的FromServivor)回收内存称为 Minor GC

触发条件:当Eden区空间不足时,触发Minor GC。

Minor GC过程如下图所示

垃圾收集器与内存分配策略 -- 内存分配与回收策略_第3张图片
Minor GC过程

图片来源 https://www.javacodegeeks.com/2015/03/minor-gc-vs-major-gc-vs-full-gc.html

在没有发生GC时,新生代中Eden区与FromServivor区有object,ToServivor区为空。
Eden区不足时,触发Minor GC,进行以下过程

复制:采用复制算法将Eden区与FromServivor区中非垃圾对象复制到ToServivor区中保存
(若对象过大,超过 tenuring threshold 值,就会直接放到老年代)
此过程还涉及到对象年龄计数的问题。

清空:清空Eden区与FromServivor区中的垃圾对象

互换: FromServivor区与 ToServivor区互换,这样上一次Minor GC保留下来的对象就可以在下一次进行Minor GC了。

计算:每次MinorGC后,都会计算一个合理的tenuring threshold和各年代区的size,以及适时地调整size

Minor GC特点:发生的非常频繁,回收速度快。

老年代

老年代:主要存放应用程序中生命周期长的内存对象。

大对象直接进入老年代
一般情况下,新对象都分配在新生代,但是如果该对象需要内存空间过大或者大小超过-XX: PretenureSizeThreshold 参数值,就会被直接存放到老年代。

长期存活的对象将进入老年代
对象年龄计数器:在Eden出生的对象,如果经过第一次 Minor GC 后仍然存活,并且还被Survivor容纳的话,将被移动到Survivor空间,并将年龄设定为1。对象每在Survivor空间经过一次Minor GC,年龄就增加1,当增加到一定年龄限制(默认为15)就会被移动到老年代中。这个年龄限制参数为:-XX:MaxTenuringThreshold 设置。但是并不是说设置了该参数就代表对象年龄要超过该值才会被移动到老年代,若在Servivor区中相同年龄的所有对象的大小总和超过了Servivor区大小的一半,那么大于或等于该年龄的所有对象都可以直接进入老年代。

空间分配担保
以前我们说过如果 ToServivor 空间没有足够的空间将剩下的存活对象保存,那么就会找其他内存(比如老年代)帮忙保存。因此在每次发生Minor GC时,虚拟机都会检查老年代最大连续可用空间是否大于新生代所有对象的总空间,若大于,那么发生Minor GC时可以确保是安全的,反之则是不安全的,这时候就可能会出现老年代担保失败的情况。通过参数-XX:HandlePromotionFailure 参数设定是否允许老年代担保失败,若允许,则虚拟机就会继续查看老年代最大连续可用空间是否大于之前每次进入老年代的对象大小的平均值,若大于,那么就会尝试进行一次Minor GC;若不允许担保失败或者允许担保失败但是老年代最大连续可用空间小于之前每次进入老年代的对象大小的平均值,那么就会进行一次 Full GC。

Major GC: 采用标记-清除算法,清理老年代,耗时较长,比Minor GC慢十倍以上。出现一次 Major GC,至少会伴随一次的Minor GC。因老年代对象稳定,所以不会频繁发生。
Full GC: 是清理整个堆空间—包括年轻代和老年代。

Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

参考文章:https://blog.csdn.net/yhyr_ycy/article/details/52566105

你可能感兴趣的:(垃圾收集器与内存分配策略 -- 内存分配与回收策略)