GC一篇全

257 阅读4分钟

什么是垃圾

  • 不在被其他对象引用的对象
  • 循环依赖

如何定位垃圾

  • 引用计数

  • 直接采用引用计数算法虽然可以更快,但是会导致碎片化空间,空间的清理很慢

  • 根可达算法

  • 可达性分析,是否有引用链与之相连

GC Root (可达性算法的起点)

  • 虚拟机栈中引用对象

  • 各个线程被调用的方法使用到的参数、局部变量等。

  • a是栈帧中的本地变量,在此充当gc root,当a=null时,a和实例new Test()断开链接,所以对象会被回收。

  •       publicclass Test {
          public static  void main(String[] args) {
      	Test a = new Test();
      	a = null;
          }
      }
      
    
  • 方法区中静态属性引用对象

  • 方法区中的常量引用对象

  • 本地方法栈中的JNI的引用对象

JAVA中的引用

  • 强引用:程序代码中普遍存在的引用,不会被回收,内存不足则OOM

  • 软引用:一般作为缓存来使用,垃圾回收时,虚拟机会根据当前内存来决定是否进行回收

  • new SoftReference<>()

  • 弱引用:与软引用类似,也是作为缓存使用,不同的是垃圾回收时必被回收

  • new WeakReference<>()

  • 虚引用:并不会决定对象的生命周期,等同于没有引用,随时会被回收

常见的垃圾回收器

年轻代

  • Serial(复制算法)

  • 单线程收集器

  • 简单高效

  • ParNew(复制算法)

  • Serial的多线程版本

  • Parallel Scavenge

  • 新生代多线程收集器,达到可控的吞吐量。

老年代

  • Serial Old(标记-整理算法)

  • 单线程收集器

  • Parallel Old(标记-整理算法)

  • 多线程收集器

  • CMS(标记-清除算法)

  • 虚拟机真正意义上的并发收集器,第一次实现了让用户线程和垃圾收集线程一起

  • 三色标记+增连更新

  • 当碰到漏标的时候,回在remark阶段从头描一遍以解决这个问题

新生代和老年代

  • G1

  • 三色标记+快照替换SATB

  • 当碰到漏标的时候,会把灰色对象指向白色对象引用消失的时候,会把这个引用放到一个堆栈里面

  • 当remark的时候,会检查堆栈,判断有没有新的引用指向他(会记录引用)

  • 将JAVA堆划分成多个大小相等的独立区域(Region),G1保留了年轻代和老年代的概念,但不再是物理隔离了,他们都是可以不连续的Region的集合。

  • 有专门的大对象区域(Humongous )

  • 空间整合

  • 有预测的整顿

  • 有计划的垃圾回收

  • ZGC

CMS运作过程

  • 初始标记,STW,标记和GC Root能直接关联的对象,速度很快
  • 并发标记,同事开启GC和用户线程,并行标记垃圾,可能有漏标常见
  • 重新标记,STW,修正并发标记期间因用户继续运作而导致标记产生变动的那一部分对象进行标记记录,停顿时间略长于初始标记
  • 并发清除,

CMS优缺点

优点

  • 并发收集、低停顿(STW时间短)

缺点

  • 垃圾碎片,标记清除算法导致(对比G1)

  • 有参数可以配置多少次full gc会对内存碎片进行整理

  • CMS的GC时间80%都在remark阶段

  • 解决方案:可以在remark之前执行一次YGC,减少年轻代对老年代的无效引用

  • concurrent mode failure 

  • 执行CMS GC的过程中业务线程也在运行,当年轻代空间满了,执行ygc时,需要将存活的对象放入老年代,而因此老年代空间不足,这时CMS还没有机会回收老年代,老年代也放不下而产生的问题。会退化成Serial Old 单线程GC。

  • 解决方案:设置参数在60%的时候进行GC

  • promotion failed

  • 在进行YGC时,Survivor空间不足,对象只能放入老年代,多数时由于老年代有足够的空间,但是碎片校多,放不下年轻代的大对象导致的。这个时候ygc会停止,直接开始fullgc

G1的运作过程

  • 标记阶段

  • 初始标记,STW,标记和GC Root直接管理的对象

  • 并发标记,开对堆中的对象进行可达性分析,找出存活对象

  • 再标记,STW,重新标记那些再并发标记阶段发生变化的对象

  • 清理阶段

  • STW,清点出存活对象的分区和没有存活对象的分区,该阶段不会清理垃圾对象

  • 复制阶段

  • STW,复制算法中的转移阶段需要分配新内存和复制对象的成员变量

JVM优化

  1. jmap 看堆内对象情况
  2. jstack 看线程堆栈情况配合top -hp 看高cpu线程情况
  3. cms 参数调整60的时候开始ygc解决concurrent mode failure问题,reamrk之前进行ygc,建设年轻代堆老年代的引用,参数调整多少次fullgc堆内存碎片进行整理,减少垃圾碎片

GC选择

吞吐量优先:Parallel Scavenge + Parallel Old(多线程并行)

响应时间优先:CMS + par new

参考:

CMS和G1和ZGC的对比