java-对jvm垃圾收集器的想法

本文深入探讨了Java垃圾收集器的优化,重点关注CMS和G1收集器。CMS作为首个并发收集器,提升了效率但存在内存碎片和浮动垃圾问题。G1则引入Region和SATB等优化,降低停顿时间并提高吞吐量,解决了CMS的部分问题。然而,即使有ZGC等新技术,垃圾扫描依然是性能瓶颈。文章激发思考如何进一步减少扫描开销。
摘要由CSDN通过智能技术生成

jvm跟java开发工作紧密相关的,相信大家在工作或多或少都对其进行过一系列的优化改进啥的,这里呢主要讲一件其中最重要的一个优化方向:垃圾收集器(因为更新迭代的原因,这里主要讲G1,CMS垃圾收集器)。
最开始的垃圾收集器都是串行的,也就是说当垃圾收集器运行的时候,其他的业务工作线程是全部停止等待的,当垃圾收集器工作结束的时候,其他业务线程才会开始工作,这有什么问题呢,问题很大,会严重影响效率,导致请求响应慢,影响用户体验等等(特别是垃圾多的时候),所以当发现问题之后就会出现解决问题的方法,这个时候CMS垃圾收集器就应运出现了。
CMS也是首个并发的收集器,可以说是把垃圾收集器带到了一个新的时代,他的工作步骤主要分为:初始标记,并发标记,重新标记,并发清理,使用的算法是三色标记(这里就不多讲了)。顾名思义,并发标记与并发清理是与工作线程一起运行的,虽然初始标记跟重新标记还是串行的,但也极大的提高了效率。CMS是开创了并发收集器的先河,但是为什么JDK在1.9版本没有选择CMS为默认收集器呢,这里就要说说CMS的所带来的问题了。
第一个就是CMS在处理垃圾的时候会产生内存碎片(回收不完全),导致老年代空间不足,此时会使用SerialOld收集器(串行的)清理,效率极差,处理的话就是降低CMS触发的阈值或者代码定时主动调用gc方法。第二个就是会产生浮动垃圾(并发清理的时候产生的新的垃圾,只能等待)。接下来就说一说G1垃圾收集器。
G1垃圾收集器是JDK1.9版本及之后的默认垃圾收集器,那么他肯定是有过人之处的,相对于CMS而言,他虽然也是用到了三色标记算法,但是其他方面已经发生了翻天覆地的改变,首先他物理上已经脱离了新生代与老年代的划分,而是把所有的空间分为了若干个Region区(默认2048个),虽然Region区的定义还是分为新生代跟老年代,但是这些个Region区并不是固定的,比如每一个Region区前一秒可能是新生代,下一秒就是老年代了,为什么会这样呢,这里就要说一下G1的停顿时间与吞吐量自定义了,他会通过你设置的停顿时间(一个时间段内应用程序线程让与GC线程执行而完全暂停)与吞吐量(吞吐量是指应用程序线程用时占程序总用时的比例)控制新生代跟老年代的大小。当然还有其他的改动,其他的GC都是分为YGC(新生代GC)与FGC(老年代GC),G1也有这两个GC,但是在其中又加入了一个新的GC:MixedGC(相当于一个CMS三次标记然后清除)。这里要说下:FGC在java10之前是串行,之后是并行。所以大家如果用的是java10以下的版本的话,一定要注意避免FGC的发生,怎么避免的,最关键的一点就是降低YGC或者MixedGC的阈值。
当然,除了上面的之外G1在算法上也加入了新的改动:三色标记+SATB。三色标记的话大家可以自行查一下,这里不多说了,主要还是讲一讲SATB,他主要解决了什么问题呢,首先先大概说一说他是个啥玩意:A对象中用到了B对象,那么这两者的关系就是引用与被引用的关系,而当一个对象没有引用的时候就说明可以回收这个对象了,那么当发生GC时都要全局的去扫描每个对象,那么有一个问题,如果一个垃圾对象是在重新标记的时候产生的呢,这个时候如果不全局扫描的话就会漏掉(三色标记的问题:漏标),全局扫描的话就会影响性能,因为初始标记的时候已经全局扫描了~当你项目非常大的时候,这个耗时是非常慢的,所以SATB做的事情就是把每个对象的引用加入到GC堆栈中(记录着对象的引用),当重新扫描时只需要扫描GC堆栈,这个时候扫描到GC堆栈中已经没有引用了就证明这个对象是要被回收的,而不需要再次的全局扫描看这个对象是否有存在引用。那么如果一个Region区都需要被清除的时候呢,怎么去判断这一个Region区没有被其他Region区引用呢,这里就要说一下G1的另外一个特殊之处:RSet,CSet。
RSet:记录着其他Region中在本Region中的引用
CSet:标记着可回收的Region
这里就简单说一下了,大家会发现G1在性能优化上会把对象之间的引用抽离出来,这样就避免了再次的全局扫描,极大的提高了性能,那么有没有更好的方式呢,有的!那就是不抽离了,每个数据自身就保存引用关系(ZGC),这样当扫描到这个数据时就可以直接知道其有没有引用而不需要再一次的去扫描GC堆栈与RSet集合了,虽然这样提高了性能,但是其本身还是没有改变垃圾收集器的本质:扫描垃圾,回收垃圾。不知道大家发现没有,耗时主要是在垃圾扫描的过程中那么有没有一种可能可以把扫描的步骤节省掉呢
发挥思维想一下,如果要节省到这部分时间,就是说当请求结束的时候就要把对应的JVM空间数据回收掉这样就不需要等到GC发生再去扫描再去回收了,可是这该怎么做呢,我天马行空的设想一下,当请求进入到栈中时,而外的保存一份数据,这份数据是什么呢,就是对堆中空间的引用,当请求出栈时起一个异步的线程直接把对象的引用空间给清理掉
这里就是发挥一下想象,,如果有说的不对的地方,各位大佬轻喷。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值