玩转Java面试.07.CMS垃圾回收器_哔哩哔哩_bilibili
面试官:谈谈JVM中的G1垃圾回收器,三色标记,垃圾回收机制,内存模型,避坑建议看这个视频就够了_哔哩哔哩_bilibili
1. 对象什么时候会变成垃圾?
当前对象,使用任何方式都不能访问它,那么此刻它就是一个垃圾
2. GC root
3. 古典的垃圾回收算法
4. GC的分代假设
年轻代:用于存储刚创建出来的对象
老年代:年轻代数据经过清理后的提升
5. GC 的古典时代
Serial:用一个线程回收年轻代
SerialOld:用一个线程回收老年代
对于Serial而言:停止用户线程,然后使用一个线程近线回收
但是有一个问题:gc的时候,用户线程停止了,延时会高,因此有了Paraller并行版本
同样的:Parallel还是不能避免用户线程的停顿.
有个问题,那是不是线程越多,效率越高?当然不是:有一个平衡,同时线程的切换有一定开销,还牵扯到上下文context
6. GC 中古时代
CMS(并发收集低停顿),核心是用于处理老年代
对象加入到扫描栈中。
并发标记的场景下:根据扫描栈中的对象,进行递归扫描遍历。【根据引用的关系得到对象图】,但是这个过程说并发执行的,用户线程仍旧在处理自己的事情
重新标记:因为用户线程和收集线程同时执行,所以极大可能并发标记的数据是不准确的【如:对象消失】。因为总需要STW
并发清理:清理标记阶段标记的哪些死亡对象
CMS采用的是标记清除【删除掉标记的哪些对象】,这个阶段是可以和用户线程同时执行的,干掉的是垃圾,而用户线程使用的都是存活对象
缺点:
1. 并发场景下,会占用更多的cpu。会使得用户线程变慢;但是cms规定了垃圾回收线程只占用cpu计算资源的25%
2. 无法清除浮动垃圾。并发清除阶段,用户线程继续运行,会产生新的垃圾,那这些垃圾对象就没有办法在这次回收过程中回收掉,这部分对象称之为浮动垃圾
3. 并发清除,使用标记清除来清除,会产生垃圾碎片,full gc的时候可以根据配置参数进行顺带压缩,整理需要STW。为避免每次都进行整理,cms效率低,可以使用参数决定几次full gc之后开始整理
4. 并发清除意味着和用户线程一起运行,所以老年代就不可能等到完全填充满了之后再去执行清除算法 ,需要给用户线程留一部分空间满足他分配对象这样的一个需求。JDK6的时候这个阈值是92%。百分之8可以解决一起问题吗?显然不可以
那如果这个时候内存不足,会导致cms并发失败,会立即将用户线程进行冻结,Serial Old就会登场,替代cms来收拾残局
7. G1(现代垃圾回收)
串行、并行、cms要么不回收,要么回收整个老年代 ,会造成很高的延时
可以设定目标,让每一次GC的时间不超过指定的时间,G1无法保证,但是会尽最大可能达到这个目标
G1之后的垃圾回收算法,优点都是性能很高,缺点是算法非常复杂
G1也是没有办法解决STW的问题。也是朝着去减少STW时间方向努力的
G1是一个全年代的垃圾回收,可以负责回收新生代和老年代
G1直接将对空间切割成一块块小内存,不像之前一样切成两刀
每一个小内存大小是相等的,可以想象成把一个豆腐切成一个个小块儿 。同时依然保存了新生代和老年代这两个概念【也就是每个Regional都是属于新生代或者老年代】
每一个regional都有一个隐藏属性,称之为回收价值
G1通过跟踪每一个regional的回收价值,这样就可以把垃圾回收造成的影响控制在一个可控的范围里面,尽量在有限的时间内回收更多的垃圾
可以选择组合一些高价值的regional(都是新生代),对他们进行回收。所以说,这种时间优势是建立在逻辑分代的基础上
每一个regional可能属于新生代,也可能属于老年代。他是动态变化的,刚开始的时候就是regional,分配对象过程中,变成了新生代(采用垃圾回收的时候,采用复制算法),经历一次垃圾回收清理之后,可能后续会分配给老年代,所以整个过程都是变化的
G1的缺点:
1M的话会分成2000张卡片
当对应的操作发生改变,对卡片进行标记
并行标记,如何在应用程序不断运行过程中,标记出来所有活的对象
三色标记发:
1. 问题1
然后gc让出了cpu,应用程序开始了
然后并发线程恢复了。
紧接着会带来一个活对象被gc的问题
解决:
cms不进行compact,会产生碎片化的问题
但是g1不会,cleanup会回收里面全是垃圾的老年代区