JVM(5)——垃圾回收器

本文深入解析JVM中的ParNew和CMS垃圾回收器的工作原理,包括ParNew的多线程垃圾回收机制,以及CMS的标记-清理算法,探讨了它们在实际应用中的优势与局限,如内存碎片问题和ConcurrentModeFailure现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

四 垃圾回收器

1JVM的年轻代垃圾回收器ParNew是如何工作的?

最常用的新生代垃圾回收器:ParNew

新生代的ParNew垃圾回收器主打的就是多线程垃圾回收机制,另外一种Serial垃圾回收器主打的是单线程垃圾回收,他们俩都是回收新生代的,唯一的区别就是单线程和多线程的区别,但是垃圾回收算法是完全一样的。

ParNew垃圾回收器如果一旦在合适的时机执行Minor GC的时候,就会把系统程序的工作线程全部停掉,禁止程序继续运行创建新的对象,然后自己就用多个垃圾回收线程去进行垃圾回收

2如何为线上系统指定使用ParNew垃圾回收器?

使用“-XX:+UseParNewGC”选项,只要加入这个选项,JVM启动之后对新生代进行垃圾回收的,就是ParNew垃圾回收器了。

3ParNew垃圾回收器默认情况下的线程数量

指定了使用ParNew垃圾回收器之后,他默认给自己设置的垃圾回收线程的数量就是跟CPU的核数是一样的

比如我们线上机器假设用的是4核CPU,或者8核CPU,或者16核CPU,那么此时ParNew的垃圾回收线程数就会分别是4个线程、8个线程、16个线程

4CMS垃圾回收的基本原理

老年代我们选择的垃圾回收器是CMS,CMS采取的“标记-清理”算法。这种方法其实最大的问题,就是会造成很多内存碎片。

CMS垃圾回收器采取的是垃圾回收线程和系统工作线程尽量同时执行的模式来处理的

5CMS如何实现系统一边工作的同时进行垃圾回收?

CMS在执行一次垃圾回收的过程一共分为4个阶段:

  1. 初始标记
    标记出来所有GC Roots直接引用的对象,要造成“Stop the World”暂停一切工作线程,但是其实影响不大,因为他的速度很快,仅仅标记GC Roots直接引用的那些对象罢了。
  2. 并发标记
    这个阶段会让系统线程可以随意创建各种新对象,继续运行,在运行期间可能会创建新的存活对象,也可能会让部分存活对象失去引用,变成垃圾对象。第二个阶段,就是对老年代所有对象进行GC Roots追踪,其实是最耗时的
  3. 重新标记
    重新标记下在第二阶段里新创建的一些对象,还有一些已有对象可能失去引用变成垃圾的情况,这个重新标记的阶段,是速度很快的,他其实就是对在第二阶段中被系统程序运行变动过的少数对象进行标记,所以运行速度很快。接着重新恢复系统程序的运行,
  4. 并发清理
    这个阶段就是让系统程序随意运行,然后他来清理掉之前标记为垃圾的对象即可。

第二阶段和第四阶段,都是和系统程序并发执行的,所以基本这两个最耗时的阶段对性能影响不大。

只有 第一个阶段和第三个阶段是需要“Stop the World”的,但是这两个阶段都是简单的标记而已,速度非常的快,所以基本上对系统运行响应也不大。

6 CMS垃圾回收器缺陷
  1. 第一个问题就是会消耗CPU资源,在并发标记和并发清理两个最耗时的阶段,垃圾回收线程和系统工作线程同时工作,会导致有限的CPU资源被垃圾回收线程占用了一部分。但是因为老年代里存活对象是比较多的,这个过程会追踪大量的对象,所以耗时较高。并发清理,又需要把垃圾对象从各种随机的内存位置清理掉,也是比较耗时的。所以在这两个阶段,CMS的垃圾回收线程是比较耗费CPU资源的。CMS默认启动的垃圾回收线程的数量是(CPU核数 + 3)/ 4。

  2. 第2个问题就是采用标记清楚会导致垃圾碎片。

7 Concurrent Mode Failure问题

​ 在并发清理阶段,CMS是回收之前标记好的垃圾对象,但是系统一直在运行,可能会随着系统运行让一些对象进入老年代,同时还变成垃圾对象,这种垃圾对象是“浮动垃圾”。但是CMS只能回收之前标记出来的垃圾对象,不会回收他们,需要等到下一次GC的时候才会回收他们。所以为了保证在CMS垃圾回收期间,还有一定的内存空间让一些对象可以进入老年代,一般会预留一些空间。CMS垃圾回收的触发时机,其中有一个就是当老年代内存占用达到一定比例了,就自动执行GC。“-XX:CMSInitiatingOccupancyFaction”参数可以用来设置老年代占用多少比例的时候触发CMS垃圾回收,JDK 1.6里面默认的值是92%。

那么如果CMS垃圾回收期间,系统程序要放入老年代的对象大于了可用内存空间,此时会如何?

​ 会发生Concurrent Mode Failure,就是说并发垃圾回收失败了,我一边回收,你一边把对象放入老年代,内存都不够了。此时就会自动用“Serial Old”垃圾回收器替代CMS,强行把系统程序“Stop the World”,重新进行长时间的GC Roots追踪,标记出来全部垃圾对象,不允许新的对象产生,然后一次性把垃圾对象都回收掉,完事儿了再恢复系统线程。

所以在生产实践中,这个自动触发CMS垃圾回收的比例需要合理优化一下,避免“Concurrent Mode Failure”问题

8内存碎片问题

CMS采用“标记-清理”算法,每次都是标记出来垃圾对象,然后一次性回收掉,这样会导致大量的内存碎片产生。如果内存碎片太多,会导致后续对象进入老年代找不到可用的连续内存空间了,然后触发Full GC。

CMS有一个参数是“-XX:+UseCMSCompactAtFullCollection”,默认就打开了,意思是在Full GC之后要再次进行“Stop the World”,停止工作线程,然后进行碎片整理,就是把存活对象挪到一起,空出来大片连续内存空间,避免内存碎片

还有一个参数是“-XX:CMSFullGCsBeforeCompaction”,这个意思是执行多少次Full GC之后再执行一次内存碎片整理的工作,默认是0,意思就是每次Full GC之后都会进行一次内存整理。

9为啥老年代的Full GC要比新生代的Minor GC慢很多倍,一般在10倍以上?
  1. 在并发标记阶段,他需要去追踪所有存活对象,老年代存活对象很多,这个过程就会很慢,
  2. 其次并发清理阶段,他不是一次性回收一大片内存,而是找到零零散散在各个地方的垃圾对象,速度也很慢;
  3. 最后还得执行一次内存碎片整理,把大量的存活对象给挪在一起,空出来连续内存空间,这个过程还得“Stop the World”,那就更慢了。

万一并发清理期间,剩余内存空间不足以存放要进入老年代的对象了,引发了“Concurrent Mode Failure”问题,那更是麻烦,还得立马用“Serial Old”垃圾回收器,“Stop the World”之后慢慢重新来一遍回收的过程,这更是耗时了。

10几个触发老年代GC的时机吗?

第一是老年代可用内存小于新生代全部对象的大小,如果没开启空间担保参数,会直接触发Full GC,所以一般空间担保参数都会打开

第二是老年代可用内存小于历次新生代GC后进入老年代的平均对象大小,此时会提前Full GC;

第三是新生代Minor GC后的存活对象大于Survivor,那么就会进入老年代,此时老年代内存不足。

第四就是“-XX:CMSInitiatingOccupancyFaction”参数,如果老年代可用内存大于历次新生代GC后进入老年代的对象平均大小,但是老年代已经使用的内存空间超过了这个参数指定的比例,也会自动触发Full GC。

11内存到底该如何分配?
  1. 新生代垃圾回收优化之一:Survivor空间够不够
    避免Minor GC后对象放不下Survivor进入老年代,或者是动态年龄判定之后进入老年代,给新生代里的Survivor充足的空间,那么Minor GC一般就没什么问题。
  2. 新生代对象躲过多少次垃圾回收后进入老年代?
  3. 多大的对象直接进入老年代?
  4. 指定垃圾回收器
JVM中的老年代垃圾回收是指对老年代进行垃圾回收的过程。老年代是堆内存的一部分,用于存放生命周期较长的对象。老年代垃圾回收的触发条件与空间分配担保有关。在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。如果大于,则此次Minor GC是安全的;如果小于,则虚拟机会根据HandlePromotionFailure设置值来决定是否进行空间分配担保。 空间分配担保是指在发生Minor GC时,如果老年代的连续空间小于新生代所有对象的总空间,虚拟机会根据HandlePromotionFailure的设置值来决定后续的操作。如果HandlePromotionFailure为true,虚拟机会进一步检查老年代最大可用连续空间是否大于历次晋升到老年代的对象的平均大小。如果大于,则尝试进行一次Minor GC,但仍然有风险;如果小于或者HandlePromotionFailure为false,则会进行一次Full GC。 总结一下,老年代垃圾回收是在发生Minor GC时对老年代进行的垃圾回收过程。空间分配担保的目的是确保老年代有足够的空间来容纳新生代的对象。如果空间不足,根据HandlePromotionFailure的设置值,虚拟机会决定是继续进行Minor GC还是进行一次Full GC。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [JVM垃圾回收——对象进入老年代](https://blog.csdn.net/weixin_39555954/article/details/130042958)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值