(1)G1_FullGC的概念辨析
SerialGC:串行的,ParallelGC:并行的 ,CMS和G1都是并发的
这几种垃圾回收器的新生代回收机制时相同的,SerialGC和ParalledGC:老年代内存不足触发的叫FullGC
CMS和G1老年代的内存不足分两种情况,以G1垃圾回收器为例:它本来是没有Full GC的,但是:
G1垃圾回收器内存不足时有一个阈值,当你的老年代内存跟整个堆内存占比达到45%以上的时候 触发并发标记阶段和混合收集的阶段,这两个阶段,当你的回收速度高于新的用户线程产生的垃圾的时候,回收的速度快于产生的垃圾速度的时候,这个时候还不叫Full GC还出于并发垃圾收集阶段
当垃圾的回收速度跟不上垃圾的产生速度的时候,这个时候并发收集就是失败了,就会退化为串行的收集,这个时候就叫Full GC了,并发失败了就叫Full GC,可以在GC日志里面查看是否有Full GC 字样
(2)G1_新生代_跨代引用
新生代垃圾回首先找到根对象,根对象可达性分析,找存活的对象,存活对象进行一个复制到幸存区,有一个问题根对象有一部分来自老年代的老年代存活对象非常的多,如果我们去遍历整个老年代去找根对象效率也是非常的低的,采用的是一种卡表的技术,把老年的技术再进行一个细分,分成一个个Cart,每个大约512k,如果有一个对象引用了新生代的对象,那么对应的Cart标记为脏Card,这样的好处是将来我们不用去找整个老年代了,只需要关注哪些脏Cart的对象了,脏Cart的区域减少搜索范围提高效率
新生代这边会有一个Rememberd Set可以记录对外部对我的引用,记录有哪些脏Cart,将来对新生代做垃圾回收的时候通过Remembered Set知道有哪些脏Cart,然后再到这个脏Cart区域遍历这些Root对象
标记脏Cart需要通过post-write barrier写屏障当每次对象发生变更时去更新这个脏Cart,是一个异步操作,可能不会立刻更新 ,会把更新的指令放到脏Cart的一个队列当中:dirty cart queue,将来由一个线程完成脏Cart的更新操作
(3)G1_remark
下面学习重标记的知识:
在CMS和G1垃圾回收器的时候有这么两个阶段,并发标记跟重新标记阶段
下面是并发标记阶段时的对象的一个处理状态,黑色的表示已经处理完成的,有引用子在引用它的,黑的的表示在结束时被保留下来,存活下来的对象
灰色:正在处理当中的
白色:尚未处理的
灰色的如果有人在强引用它将来会变为黑色的,后面的那个白色最后也会变成黑色存下来,上面的白色没有引用将来还是白色
垃圾结束时会根据黑白状态来区分应该存活还是应该当成垃圾
下面当处理到B,发现有强引用把它变为黑色:
处理到C,并发表标记,此时有用户的线程对这个对象的引用做一个修改比如把这个引用断了,当处理完B处理C,发现它的引用已经断了,处理到C的时候说C是白色的
还有另外一种情况可能当C被处理完以后并发标记没有结束,用户线程改变了C的引用地址把C对象作为A对象的一个属性做一个赋值操作
因为A处理完了,此时如果把C回收掉,是不行的因为有强引用它,需要做进一步检查重标记
当这个对象的引用发生改变时,JVM会给他加入一个写屏障,会把C加入一个队列,把C变为一个灰色,表示还没有处理完
当整个并发标记结束了,进入重新标记阶段重新标记阶段会Stop the World,重新标记的线程从队列中把一个个取出来,发现有强引用引用着它把它变为黑色这样呢不会被错误当错垃圾回收掉
(4)G1_字符串去重
下面介绍G1垃圾回收器的优化,介绍的是jdk8-jdk9的优化
跟String.intern()不一样,它关注的是char数组,而String.intern()关注的是字符串对象,是让字符串本身不重复,用的是StringTable来去重
(5)G1_类卸载
jdk之前的版本中,类是没有办法卸载的,只要加载后会一直占用内存 尤其自定义的类加载器, 创建和加载类使用一段时间后就没有人再用了,这个时候还占用的内存对垃圾回收时不利的,从jdk8u40后
类卸载的条件比较苛刻,类的实例都被回收掉了,类所在的类加载器其中的所有类也不再使用了,就会把所有类卸载掉
对很多框架来说都是用了自定义的类加载器,这种情况还是会发生的
JDK的类加载器他们加载的类一般不会卸载,比如启动类加载器,扩展类加载器,应用程序类加载器他们使用都是会存在的,不会被卸载
(6)G1_巨型对象
G1垃圾回收器的区划分伊甸园区,幸存区,老年代区,还有一种巨型对象区
巨型对象的回收时是不会对对象进行拷贝,回收时会优先回收
(7)G1_动态调整阈值
在现在的G1垃圾回收器老年代即使是Full GC也变成了多线程
Full GC是影响效率,可以减少Full GC的几率:通过上面参数设定
当老年代跟堆内存占比超过阈值时,并发得垃圾回收就开始了阈值默认是45% jdk9以后把这个固定了 就不太好了定义的大了容易产生Full GC,定义的下了,频繁的进行并发标记和混合收集
jdk9里可以动态的调整这个阈值,通过下面的参数来设定
它会添加一个安全的空挡空间,让堆得空间足够大,容纳哪些浮动的垃圾,这样呢可以避免并发垃圾回收退回到Full GC