深入理解java虚拟机—垃圾回收机制(2)

本文详细介绍了HotSpot虚拟机中的几种垃圾回收器,包括Serial、ParNew、ParallelScavenge、CMS、SerialOld、ParallelOld及G1收集器的特点与适用场景,并探讨了内存分配与回收策略。

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

垃圾回收机制(2)

垃圾收集器

虚拟机中会维护很多种垃圾回收器,在HotSpot虚拟机中,有:Serial、ParNew、Paraller Scavenge、CMS、Serial Old(MSC)、Parallel Old,G1这些垃圾回收器。
(1)Serial收集器(新生代,复制算法)
Serial的意思是串行,会让我们想到是用单线程去做垃圾收集,但是最重要的意义是在于它工作时需要暂停所有的线程。
虽然Serial收集器停止所有线程给用户带来的体验不佳,但是它仍然是虚拟机Client模式下默认的新生代的收集器。
(2)ParNew收集器(新生代,复制算法)
ParNew收集器是Serial收集器的多线程版本,新生代采用复制算法。它是在Server模式下首选的新生代收集器,其中一个重要原因是除Serial收集器外,只有它能与CMS收集器(jdk1.5时期具有划时代意义的,真正意义上并发的收集器,能让垃圾收集线程和用户线程一起工作)配合工作。
ParNew适合多个cpu的环境,Serial适合单个cpu的环境。
(3)Parallel Scavenge收集器(新生代,复制算法,更关注吞吐量)
这是一款新生代收集器,采用了复制算法和多线程。虽然看上去跟ParNew收集器一样,但是Paraller Scavenge收集器的关注点和其他收集器不同,CMS等收集器关注的是尽量缩短用户线程的停顿时间,而Parallel Scavenge收集器关注的是系统的吞吐量。吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。
Parallel Scavenge收集器通过控制最大垃圾收集停顿时间MaxGCPauseMills参数和在直接设置吞吐量大小GCTimeRatio参数来控制吞吐量。
当然MaxGCPauseMills参数不是越小越小,停顿时间短就意味着内存小,吞吐量小。
GCTimeRatio是最大垃圾收集时间比率,

GCTimeRatio=<N>,(0<N<100);GC时间比率=1/(1+N);默认N=99

Parallel Scavenge收集器通过设置-XX:+UseAdaptiveSizePolicy,可以让收集器自动调控新生代中区域比例和最合适的停顿时间和吞吐量,这叫自适应策略。
(4)Serial Old收集器(老年代,标记-整理算法)
这个收集器是Serial收集器的老年代版本,同样是单线程的,使用标记-整理算法。这个收集器主要也是给Client模式下的虚拟机使用,如果在Server模式下,它有两个作用:一个作用是和jdk1.5以及之前的Parallel Scavenge收集器搭配使用。另一个作用是作为CMS收集器的后备预案。
(5)Parallel Old收集器(老年代,标记-整理算法)
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。这个垃圾回收器是藏jdk1.6后开始使用,在此之前Parallel Scavenge只能和Serial Old搭配,在服务器环境(多cpu)下,Serial Old会拖累性能。现在有了Parallel Old,可以让Parallel Scavenge和Parallel Scavenge组合,性能可以大大提高,特别是注重吞吐量和cpu资源的场合。
(6)CMS收集器(老年代,标记-清除算法)
CMS主要为了缩短线程停顿时间,适合注重响应速度快的场合。
它的运作过程稍微有点复杂,分为:
- 初始标记
- 并发标记
- 重新标记
- 并发清除
初始标记:会暂停全部线程,但是很快,只是标记一下GC roots能直接关联到的对象。
并发标记:和用户线程并发,进行GC Roots Tracing的过程,就是找出Gc roots能关联到的对象,在这个时候是并发执行的。
重新标记:会暂停全部线程,修正并发标记期间程序运作而变动的对象记录。
并发清除:和用户线程并发清除死亡对象。
由于时间较长的并发标记和并发清除和用户线程是同时运行的,所以能大大降低停顿时间。
CMS有以下三个缺点:
- 对cpu资源敏感:在cpu少,占用cpu资源比较多。
- 无法处理浮动垃圾:在并发清理阶段还有垃圾出现,这些垃圾无法被回收,所以需要需要预留一部分空间留给并发收集时程序使用。在jdk1.5时,CMS的启动阈值时老年在68%的空间被占用。在jdk1.6后,阈值提升到了92%,如果预留的空间不足,那么就会启动Serial Old来清理。
- 标记-清除会产生大量碎片。
(7)G1收集器(适用于新生代和老年代)
G1收集器是当今收集器最前沿的技术,它可以独立管理整个GC堆(新生代,老年代)。主要是面向服务器端应用。
G1有如下优点:
- 并发与并行:利用多cpu缩减线程停顿时间,利用并发使程序继续运行。
- 分代收集:独立管理新生代和老年代。
- 空间整合:从整体上看是“标记-整理”算法,从局部上看是“复制算法”,减少了内存碎片。
- 可预测的停顿:可建立可预测的时间停顿模型,根据每个空间区域的垃圾堆积的价值大小,建立优先列表,先回收价值大的区域。
由于新生代和老年代之间的对象会互相引用,难免涉及到全堆扫描的问题。如果新生代进行GC要扫描老年代。那么Minor GC的效率会下降很多。虚拟机通过remenbered set来避免全堆扫描。虚拟机发现程序对reference类型数据进行写操作时,是检查reference对象和被引用对象是否处于不同的region(区域)中,如果是,那么将引用信息记录到被引用对象区域的remembered set中,在被引用对象区域进行gc时,在枚举GC ROOTs根节点时将remembered set加入枚举范围就可以保证不对全堆扫描。
收集步骤:
- 初始标记
- 并发标记
- 最终标记
- 筛选回收
初始标记:停止所有用户线程,标记一下GC roots能关联到的对象,并修改TAMS值,
并发标记:从GC ROOTS做可达性分析,和用户程序并行执行。
最终标记:修正因之前程序运行而改变的标记记录,将这段变化的记录记录在remembered set logs里面,然后合并到remembered set中,这段时间可以停顿线程,可以并行执行,注意!这里是并行不是并发,只有在多个cpu下才能并行执行,意味着单个cpu只能停顿线程。
筛选回收:根据区域的回收价值指定回收计划,进行回收,一般情况下会停顿线程。
这里写图片描述

内存分配和回收策略

新生代分为Eden区和两个Survivor区。对象会优先分配到新生代的Eden区,当Eden区满时,还存活的对象会被复制到其中一个Survivor区。如果Survivor区也满了,那么将之前存活的对象担保到老年区。
大对象会直接进入老年代。(不怕大对象,就怕死的快的大对象),我们知道老年代的major gc比新生代的minor gc慢了大约10倍。
长期存活的对象也将会进入老年代。对象会有一个年龄计数器,每次gc年龄就会+1,如果一个对象在新生代中待了好长时间,等到一定的年龄,就会被送到老年代,默认阈值年龄是15。当然这不是硬性规定,如果相同年龄的对象大小大于Survivor区的一半,那么大于等于该年龄的都可能进入老年代。

空间分配担保

在新生代发生Minor GC 之前,虚拟机会检查老年代可用的最大连续空间是否大于新生代所有对象的的总大小。为啥要这么做?Minor GC是对新生代发起的垃圾回收,将活着的对象复制到其中一个Survivor区,如果Survivor区的大小不够,那么要通过担保机制把对象担保到老年代。所以在Minor GC之前要确保安全性,如果老年代空间足够,那么就进行Minor GC,如果不够,那么要检车是否允许担保失败,如果允许,那么进行Minor GC,如果不允许,那么进行一次Full GC。

总结

了解虚拟机的垃圾回收机制,我们可以使程序响应更快 ,更稳定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值