秒懂JVM的垃圾回收机制( 二 )


还有一种情况 , 就是Young GC后存活的对象太多 , Survivor区放不下了 , 这个时候就会把这些对象直接转移到老年代中 。
这里我们就要思考一个问题了 , 如果老年代也放不下了怎么办呢?
老年代空间分配担保原则首先 , 在执行任何一次Young GC之前 , JVM都会先检查一下老年代可用的内存空间是否大于新生代所有对象的总大小 。
【秒懂JVM的垃圾回收机制】为啥要检查这个呢?因为在极端情况下 , Young GC后 , 新生代中所有的对象都存活下来了 , 那就会把所有新生代中的对象放入老年代中 。
如果说老年代可用内存大于新生代对象总大小 , 那么就可以放心的执行Young GC了 。
但是如果老年代的可用内存小于新生代对象的总大小 , 这个时候就会看一个参数“-XX:HandlePromotionFailure”是否设置为true了(可以认为jdk7之后 , 默认设置为true) 。
如果设置为true , 那么进入下一步判断 , 就是看看老年代可用的内存 , 是否大于之前每次Young GC后进入老年代对象的平均大小 。
如果说老年代的可用内存小于平均大小 , 或者说参数没有设置成true , 那么就会直接触发“Full GC” , 就是对老年代进行垃圾回收 , 腾出空间后 , 再进行Young GC 。
如果上边两种情况判断成功 , 没有执行Full GC , 进行了Young GC , 有以下几种可能:
1.如果Young GC后 , 存活的对象大小小于Survivor区域的大小 , 那么直接进入Survivor区域即可 。
2.如果Young GC后 , 存活的对象大小大于Survivor区域的大小 , 但是小于老年代可用内存大小 , 那就直接进入老年代 。
3.很不幸 , 老年代可用空间也放不下这些存活对象了 , 那就会发生“Handle Promotion Failure”的情况 , 触发Full GC 。
如果Full GC后 , 老年代可用内存还是不够 , 那么就会导致OOM内存溢出了 。
这段内容可能比较繁琐 , 结合内存模型 , 多看两遍相信小伙伴们是可以读懂的 。
老年代的垃圾回收算法接下来我们就来介绍一下老年代的垃圾回收算法 , 标记整理算法 , 理解起来还是比较容易的 。
秒懂JVM的垃圾回收机制文章插图
秒懂JVM的垃圾回收机制文章插图
?
开始时我们的对象是胡乱分布的 , 经过垃圾回收后 , 会标记出哪些是存活对象 , 哪些是垃圾对象 , 而后会把这些存活对象在内存中进行整理移动 , 尽量都挪到一边去靠在一起 , 然后再把垃圾对象进行清除 , 这样做的好处就是避免了垃圾回收后产生大片的内存碎片 。
但是这一过程其实是比较耗时的 , 至少要比新生代的垃圾回收算法慢10倍 。
所以如果系统频繁出现Full GC , 会严重影响系统性能 , 出现频繁卡顿 。
所以JVM优化的一大问题就是减少Full GC频率 。
垃圾回收器新生代和老年代进行垃圾回收的时候是通过不同的垃圾回收器进行回收的 。
Seral和Seral Old垃圾回收器:分别用于回收新生代和老年代 。
工作原理是单线程运行 , 垃圾回收的时候会停止我们系统的其他线程 , 让系统卡死不动 , 然后执行垃圾回收 , 这个现在基本已经不会使用了
ParNew和CMS垃圾回收器:分别用于回收新生代和老年代 。
它们都是多线程并发的 , 性能更好 , 现在一般是线上生产系统的标配 。
G1垃圾回收器:统一收集新生代和老年代 , 采用了更加优秀的算法机制 。
这里只是给大家做一下简单的介绍 , 更详细的内容以后文章会单独解析 。
Stop the WorldJVM最大的痛点就是Stop the World了 。
在垃圾回收的时候 , 尽可能的要让垃圾回收器专心的去做垃圾回收的操作(防止垃圾回收的时候还在创建新对象 , 那不就乱套了吗) , 所以此时JVM会在后台进入Stop the World状态 。


推荐阅读