JVM(二) :垃圾回收机制

一、怎么判断是垃圾

  • 引用计数
  • 可达性分析

二、如何回收

1. 分带收集算法

1.1 标记-清除(老年代)

该算法会有两个问题:
1. 效率问题,标记和清除效率不高。
2. 空间问题: 标记清除后会产生大量不连续的内存碎片, 空间碎片太多可能会导致在运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集。

所以它一般用于"垃圾不太多的区域,比如老年代"。

1.2 复制(新生代)

该算法的核心是将可用内存按容量划分为大小相等的两块, 每次只用其中一块, 当这一块的内存用完, 就将还存活的对象(非垃圾)复制到另外一块上面, 然后把已使用过的内存空间一次清理掉.
优点:不用考虑碎片问题,方法简单高效。
缺点:内存浪费严重。
当发生MinorGC时, 将Eden和Survivor中还存活着的对象一次性地拷贝到另外一块Survivor上, 最后清理掉Eden和刚才用过的Survivor的空间. 当Survivor空间不够用(不足以保存尚存活的对象)时, 需要依赖老年代进行空间分配担保机制, 这部分内存直接进入老年代。

1.3 标记-整理算法(老年代)

标记清除算法会产生内存碎片问题, 而复制算法需要有额外的内存担保空间, 于是针对老年代的特点, 又有了标记整理算法. 标记整理算法的标记过程与标记清除算法相同, 但后续步骤不再对可回收对象直接清理, 而是让所有存活的对象都向一端移动,然后清理掉端边界以外的内存.

1.4 方法区回收(永久代)

在方法区进行垃圾回收一般”性价比”较低, 因为在方法区主要回收两部分内容: 废弃常量和无用的类. 回收废弃常量与回收其他年代中的对象类似, 但要判断一个类是否无用则条件相当苛刻:
1. 该类所有的实例都已经被回收, Java堆中不存在该类的任何实例;
2. 该类对应的Class对象没有在任何地方被引用(也就是在任何地方都无法通过反射访问该类的方法);
3. 加载该类的ClassLoader已经被回收.

2. 分区收集算法

面介绍的分代收集算法是将对象的生命周期按长短划分为两个部分, 而分区算法则将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收. 这样做的好处是可以控制一次回收多少个小区间. 在相同条件下, 堆空间越大, 一次GC耗时就越长, 从而产生的停顿也越长. 为了更好地控制GC产生的停顿时间, 将一块大的内存区域分割为多个小块, 根据目标停顿时间, 每次合理地回收若干个小区间(而不是整个堆), 从而减少一次GC所产生的停顿.

三、7种垃圾收集器

1. Serial 收集器

Serial收集器是一个单线程的收集器,只会使用一个CPU或者一条收集线程去完成垃圾收集工作,当收集器进行垃圾收集的时候会暂停其他所有工作线程,直到收集完毕。
优点:简单而高效,对于单核环境来说,Serial收集器由于没有线程交互的开销,可以获得更高的单线程收集效率
对于运行在Client模式下的虚拟机是一个很好的选择
适用新生代收集,采用复制算法

2. Serial Old 收集器

Serial Old是Serial收集器的老年代版本。同样是单线程收集器,采用标记-整理算法

3. ParNew 收集器

ParNew收集器是Serial收集器的多线程版本,目前只有它可以跟CMS收集器配合工作
适用新生代收集,采用复制算法

4. Parallel Old 收集器

Parallel Old是Parallel Scavenge收集器的老年代版本。使用多线程和标记-整理算法

5. Parallel Scavenge 收集器

之前的收集器目的在于尽可能的缩短垃圾收集的时候用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。
适用新生代收集,采用复制算法

6. CMS收集器

CMS收集器是一种以获取最短回收停顿时间为目标的收集器。基于标记-清除算法。CMS内存回收过程是与用户线程一起并发执行的。

  • 初始标记:STW,单线程,由于是从GCRoot寻找直达的对象,速度快
  • 并发标记:与应用线程一起运行,是CMS最主要的工作阶段,通过直达对象,扫描全部的对象,进行标记
  • 重新标记:STW,修正并发标记时由于应用程序还在并发运行产生的对象的修改,多线程,速度快,需要全局停顿
  • 并发清除:与应用程序一起运行,为何采用清除算法?CMS主要关注低延迟,因而采用并发方式,清理垃圾时,应用程序还在运行,如何采用压缩算法,则涉及到要移动应用程序的存活对象,此时不停顿,是很难处理的,一般需要停顿下,移动存活对象,再让应用程序继续运行,但这样停顿时间变长,延迟变大,所以CMS采用清除算法。

缺点:

  • 对于CPU资源非常敏感
  • 无法处理浮动垃圾
  • 标记-清除算法导致内存碎片产生

7. G1收集器

G1收集器是一款面向服务端应用的垃圾收集器,具备以下特点:

  • 并行与并发
  • 分代收集:G1不需要其它垃圾收集器配合就能独立管理整个GC堆
  • 空间整合:整体采用标记-整理算法,从分区上面来看基于复制算法
  • 可预测的停顿

G1将整个堆划分为多个大小相等的独立区域(Region),避免在整个堆中进行全区域的垃圾收集。
建立可预测的时间模型
Region之间的对象引用以及其它收集器中的新人代与老年代之间的对象引用,虚拟机都是使用Remembered Set.
在CMS中,也有RSet的概念,在老年代中有一块区域用来记录指向新生代的引用。这是一种point-out,在进行Young GC时,扫描根时,仅仅需要扫描这一块区域,而不需要扫描整个老年代。

四、几种GC概念

minor gc:清理新生代
major gc:清理老年代
full gc:清理整个堆

对于Minor GC,其触发条件非常简单,当Eden区空间满时,就将触发一次Minor GC
Full GC触发条件:
1、调用System.gc()
2、老年代空间不足
3、空间分配担保失败
4、JDK1.7及以前的永久代空间不足
5、Concurrent Mode Failure

五、问题

1.大多数老年代收集器使用了标记-复制算法,为什么CMS收集器使用了标记-清除算法
CMS收集器主要关注低延迟,尽可能减少GC时间。如果采用标记-整理方法,移动存活对象的时候需要停顿,导致延迟变大。

2.FUll GC会触发哪些区域进行回收
老年代的垃圾回收(又称Major GC)通常使用“标记-清理”或“标记-整理”算法。整堆包括新生代和老年代的垃圾回收称为Full GC。

3.什么时候触发FULL GC

  1. Perm空间不足;
  2. CMS GC时出现promotion failed和concurrent mode failure(concurrent mode failure发生的原因一般是CMS正在进行,但是由于老年代空间不足,需要尽快回收老年代里面的不再被使用的对象,这时停止所有的线程,同时终止CMS,直接进行Serial Old GC);
  3. 统计得到的Young GC晋升到老年代的平均大小大于老年代的剩余空间;
  4. 主动触发Full GC(执行jmap -histo:live [pid])来避免碎片问题。

你可能感兴趣的:(学习心得)