前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >G1和CMS垃圾收集器详解

G1和CMS垃圾收集器详解

作者头像
小土豆Yuki
发布2021-02-01 10:44:40
1.5K0
发布2021-02-01 10:44:40
举报
文章被收录于专栏:洁癖是一只狗洁癖是一只狗

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。很大一部分是应用在互联网网站或者浏览器的B/S系统的服务端。

CMS是基于标记清除算法实现的,他的整个过程的步骤如下

  1. 初始化标记
  2. 并发标记
  3. 重新标记
  4. 并发清除

初始化标记是标记一下GC Roots能直接关联的对象,速度很快,并发标记就是从GC Roots中直接关联的对象开始遍历整个对象的过程,这个过程耗时比较长,但是用户线程不用停顿,重新标记是因为在并发标记阶段会有用户线程运行,会导致标记产生变动,这个阶段停顿时间比初始化阶段要长,但是远比并发标记停顿时间要短,而并发清除,清除那些被判断死亡的对象,此阶段不需要移动对象,因此不需要停顿用户线程

CMS的优点和缺点

优点并发收集,低停顿,但是他的缺点也是不可忽略的。

  1. CMS收集器对处理器资源非常敏感,因为在并发阶段他虽然不会停顿用户线程,但是会因为占用一部分线程而导致应用线程变慢,降低吞吐量,如果应用程序负载本来就很高,好要分出一部分处理能力去执行垃圾收集,就会导致应用的执行速度大幅降低
  2. 产生浮动垃圾,CMS在并发阶段和并发清除阶段由于用户线程一直在运行,会导致新的对象产生,而此时垃圾收集已经结束,而这部分对象只能在下次被回收,而且还要预留部分内存空间给用户线程使用,导致他并不能和其他垃圾收集器一样,等到老年代快满的时候采取垃圾收集,而是在CMS是在达到68%的时候就会激活垃圾收集,可以使用参数改变CMS的触发百分比,降低内存回收频率获取到更好的性能(-XX:CMSInitiatingOcc-pancyFraction).
  3. CMS是基于标记-清除算法,因此在垃圾回收结束之后,会产生大量空间碎片产生,其实还有许多的剩余空间,但是当一个大对象产生的时候,没有办法找到可以存放大对象,就会产生一次Full GC

Garbage First收集器(G1)

G1收集器是垃圾收集器技术历史上的一个里程碑,他开创了面向局部的设计思路和Region的内存布局形式,他是最为CMS收集器的替代者和继承人,而设计者希望可以设计出一款能够建立起停顿时间模型的收集器,停顿时间模型是指可以指定一定长度为M毫秒的时间段内,消耗在垃圾收集器上的时间大概率不能超过N毫秒的目标。

之前的垃圾收集器都是在新生代,老年代或者java整个堆进行垃圾收集,而G1并不是这样,他可以面向堆内存中任何部分组成回收集进行回收,衡量的标准不再是属于那个分代,而是那块内存存放的垃圾数量多,且回收的收益更大,这个就是G1收集器的Mixed GC模式

G1是基于Region的堆内存布局实现,G1不再坚持固定大小以及固定数量的分代区域划分,而是把连续Java堆划分成多个大小相等独立区域,每个Region扮演的角色可以是新生代Eden,Survivor空间,或者老年代,这样无论是新创建的对象还是已经被熬过了多次垃圾回收的对象都能获得很多的效果

且他还有一个Humongous对象,专门存储大对象,当一个对象的大小是Humongous大小的一半就认为是大对象,如果超过整个Region容量的超级大对象,会用N个连续的Humongous存放,Humongous Regions看做老年代的一部分,而Region是可以用参数设置大小的(-XX:G1HeapRegionSize设置,范围是1M-32M,且是2的幂次方).

虽然G1还保留着新生代和老年代的概念,但是他们不再是固定的了,而是一系列区域的动态集合,由于Region是单次回收的最小单位,每次回收的内存空间都是Region大小的整数倍,这样就可以避免整个java堆中全区域垃圾回收,而G1收集器去跟踪各个Region里面的垃圾收集的价值大小,价值即回收所获得空间大小以及回收所需要的时间经验值,而后台维护一个优先级队列类表,每次根据用户设置允许的手机停顿时间,优先回收价值收益最大的那些Region,使用Region划分内存空间,以及具有优先级的区域回收方式,保证了G1收集器在有限时间内尽可能获取高的收益效率。

Region如何解决对象引用

首先我们要知道一个东西就是卡表,我们知道如果老年代引用新生代的引用,是不是要把整个老年代加进GC Roots里面,当然不是这样的,为了解决这个问题引入了记忆集的数据结构,而记忆集的具体实现就是卡表

卡表是一个字节数组,且每一个元素的对应一个内存区域的特定大小的内存块,这个内存块就做卡页,卡页的内存包含着一个或多个对象,只要卡页有一个对象存在跨代指针,就标记对应卡表元素的值为1,称之为脏的,没有则标记为0,当发生Minor GC的时候我们直接在卡表中找到脏的,添加到GC Roots中扫描,就不必进行扫描整个老年代,扫描完之后就直接再标记卡表的元素为0.

而G1卡表是一种双向卡表的结构(我指向谁,谁指向我,进行记录),要比原来的卡表实现起来更加复杂,同时由于Region数量比传统收集器的分代数量明显要多很多,因此G1收集器要比传统的收集器更高的内存占用负担。

如何解决用户线程和收集线程互不干扰

增量更新和原始快照

如何解决可靠的停顿预测模型

用户可以通过参数-XX:MaxGCpauseMillis参数指定停顿时间仅仅意味着垃圾收集器发生之前的期望值,但是具体G1怎么做才能做到用户的预期呢.

G1是以衰减均值为理论基础来实现的,在垃圾收集的时候,G1收集器会收集每个Region的回收耗时,每个Region记忆卡集的脏卡的数量以及花费的成本,并分析平均值,标准偏差,置信度等统计信息,且衰减均值是最近的平均状态,Region的统计状态越新越能决定其回收的价值,然后通过这些信息预测现在开始回收的化,那些Region是最有价值的。

G1垃圾收集器的步骤如下

  1. 初始化标记 仅仅标记一下GC Roots关联的对象且修改TAMS指针的值,让下一次并发运行时候,能直接找到新分配的对象,这个阶段需要停顿线程,但耗时很短.
  2. 并发标记 从GC Roots开始对堆中对象进行可达性分析,递归整个堆里的对象图,找到回收的对象,这个阶段耗时长,但可以和用户线程并行运行.
  3. 最终标记 对用线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后少量的SATB记录
  4. 筛选回收 负责更新Region的统计信息,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来指定回收计划,可以自由选择任意个Region进行回收,然后把回收的那一部分存活的对象进行复制到空的Region,再清理掉旧的Region全部的空间。

G1优点和缺点

G1可以指定最大停顿时间,分Region的内存布局,按收益动态确定回收这些创新性设计带来的红利,也不会产生内存空间碎片.

但是G1也有许多弱项,就内存来说虽然G1和CMS都是使用卡表处理跨代指针,但是G1的卡表实现更复杂,且是每一个Region都会有一个卡表,而CMS只需要唯一一份卡表,因此就会导致G1记忆集可能会占整个堆内存的20%甚至更多的内存空间。

还有就是负载的角度上,G1和CMS虽然都是使用写后屏障进行卡表的维护工作,但是G1还要用写前屏障来跟踪并发时的指针变化情况,此时由于跟踪引用变化带来的额外负担。且G1对写屏障的复杂操作要比CMS消耗更多的运算资源,所以CMS的写屏障和写后屏障实现是直接的同步操作,而G1就不得不将其实现为类似消息队列的结构,把写前屏障和写后屏障中要做的事情放到队列里面,然后一步处理。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-01-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档