JVM分带回收机制以及垃圾回收算法

  JVM垃圾回收主要是回收堆上的内存,新生代使用的是复制回收算法,老年代使用标记清除(CMS)或标记整理算法。

一:垃圾回收算法

1:复制算法
  复制算法是新生代使用的算法,主要的实现原理是将内存分为两块区域,一块用于存放数据另一块用于将发生垃圾回收后的存活对象整体复制。复制算法回收垃圾后的内存空间是没有内存碎片的。目前新生代使用的是Appel回收方式,大家都知道新生代的对象很多都是朝生夕死的根本活不到下一次GC,所有现在是将新生代分成了3个区域Eden区,From区,To区且按照8:1:1分配大小。新创建的对象(大对象除外)都是存放在Eden区,From区和To区主要用于复制算法的垃圾回收
2:标记删除
  标记删除主要是回收老年代,目前标记删除只有CMS算法在使用。CMS会两遍扫描,效率略低,最主要的是用标记删除方式垃圾回收后留下的内存区域是不连续会形成内存碎片
3:标记整理
  标记整理也是用来回收老年代的同样会两遍扫描且效率偏低,但是标记整理在删除垃圾后会将内存整理成没有内存碎片。整理内存碎片需要移动指针,所以也就可以解释为什么G1等垃圾回器在清除垃圾时要暂停用户线程了。

二:JVM中常见的垃圾回收器

1:单线程垃圾回收器
  单线程的垃圾回收器主要有Serial(新生代复制算法),Serial Old(老年代标记整理算法)。单线程垃圾回收器是Java刚出来时的回收算法,由于是单线程可想而知当发生垃圾回收时停顿时间STW(Stop The World)是非常高的所以让人感觉非常卡顿。
2:多线程垃圾回收器
  多线程垃圾回收器主要有ParNew(回收新生代复制算法一般和CMS配合使用),Parallel Scavenge(回收新生代复制算法),Parallel Old(回收老年代标记整理),目前我们自己开发环境用的HotSpot采用的是Parallel Scavenge + Parallel Old。多线程回收速度比单线程有所提升,但是在垃圾回收时仍然要暂停用户线程,效率仍然需要提升。

JVM分带回收机制以及垃圾回收算法

3:并发垃圾回收器
3.1:CMS(Concurrent Mark Sweep)
CMS垃圾回收主要有如下几步

  1. 初始标记:暂停用户线程标记GCRoots对象以及其直接关联的对象,由于只标记GCRoots以及其关联的所以STW很短。
  2. 并发标记:标记线程和用户线程同时执行,标记所有存活的对象。
  3. 最终标记:由于上一步是标记线程和用户线程同时进行可能会产生垃圾,此步主要是针对没有被标记的对象。
  4. 并发清理:与用户线程并发执行删除内存垃圾。
  5. 重置线程:清除内部状态为下次垃圾回收做准备。

JVM分带回收机制以及垃圾回收算法

CMS中的问题

  1. CPU敏感:CMS的清除垃圾时并发清除,如果CPU核不高的话会造成很大的负担。
  2. 浮动垃圾:CMS垃圾的清理线程是和用户线程并发执行的,此时用户线程也可能会产生垃圾,这些垃圾就是所谓的浮动垃圾。
  3. 内存碎片:CMS是采用的标记删除算法,会造成内存碎片。

3.2:G1(Garbage First)
  G1是新生代和老年代都会回收。G1主要是将内存分为多个Region区域,每一个区域标记成Eden区,Survivor区,Old区,Humongous(存放大对象),回收器能够对扮演不同角色的 Region 采用不同的策略去处理,这样无论是 新创建的对象还是已经存活了一段时间、熬过多次收集的旧对象都能获取很好的收集效果。G1的回收并不是一定会整个内存全部回收而是筛选回收,而且各个区域可以相互转换只需要将区域对于的标记修改,例如将Eden改为Old。G1的垃圾回收再标记阶段和CMS类似,只是在筛选回收的时候回暂停用户线程。

JVM分带回收机制以及垃圾回收算法

TAMS 是什么?
  要达到 GC 与用户线程并发运行,必须要解决回收过程中新对象的分配,所以 G1 为每一个 Region 区域设计了两个名为 TAMS(Top at Mark Start)的指针, 从 Region 区域划出一部分空间用于记录并发回收过程中的新对象。这样的对象认为它们是存活的,不纳入垃圾回收范围

G1垃圾回收的步骤如下:

  1. 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,并且修改 TAMS 指针的值,让下一阶段用户线程并发运行时,能正确地在可用的 Region 中分配新对象。 这个阶段需要停顿线程,但耗时很短,而且是借用进行 Minor GC 的时候同步完成的,所以 G1 收集器在这个阶段实际并没有额外的停顿。
  2. 并发标记:标记线程和用户线程同时执行,标记所有存活的对象。
  3. 最终标记:针对没有被标记的对象再次标记。
  4. 筛选回收:负责更新 Region 的统计数据,对各个 Region 的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以*选择任意多个 Region 构 成回收集,然后把决定回收的那一部分 Region 的存活对象复制到空的 Region 中,再清理掉整个旧 Region 的全部空间。这里的操作涉及存活对象的移动, 是必须暂停用户线程,由多条收集器线程并行完成的。。

JVM分带回收机制以及垃圾回收算法

由于G1采用的是复制算法和标记整理,所以是不会出现内存碎片的。

最后用图表将集中垃圾回收器做对比:
JVM分带回收机制以及垃圾回收算法
JVM分带回收机制以及垃圾回收算法