java虚拟机面试干货(五)— 垃圾回收:回收算法

上篇文章我们分析了GC中对要回收对象的判断条件,这篇文章我们说说GC回收算法。

标记-清除算法

即mark-sweep算法。通过两个阶段完成,一是标记阶段,筛选出已经没有关联到GC Roots调用链的对象;二是清除,将这些标记处的对象删除。示意图如下:
java虚拟机面试干货(五)— 垃圾回收:回收算法_第1张图片
但这个算法有2个问题:一是效率太低;二是内存碎片。产生的大量内存碎片因空间不连续,会导致再分配大对象时重新触发一次GC。

复制算法

将内存空间分为两块,每次只使用其中一块的空间。当进行GC回收时,将存活的对象复制到另一块上,然后将使用过的这块全部清理。示意图如下:
java虚拟机面试干货(五)— 垃圾回收:回收算法_第2张图片
适用于对象存活率较高的情景,HotSpot的老年代即使用该算法。

分代回收算法

即分代收集(Generational Collection),基本思想就是根据对象存活周期的不同将内存划分为几块。在JVM的实现中,就把java堆划分为了新生代和老年代,其中新生代还包括Eden区和Survivor区。下面尝试分析:

Eden区

希腊语伊甸园,位于新生代,大多数对象被声明时保存在这个区域。当Eden区空间不足时,触发minor GC,将Eden区的存活对象移动到新的Survivor区。由此可见,Eden区采用复制算法。

Survivor区

意为幸存者,是新生代和老年代的缓冲区域。分为Survivor0(S0)和Survivor1(S1)两部分,和Eden区大小比例默认是1:1:8。当发生Minor GC时,会将存活的对象移动到S0内存区域,并清空Eden区域,当再次发生Minor GC时,将Eden和S0中存活的对象移动到S1内存区域。
存活对象会反复在S0和S1之间移动,当对象从Eden移动到Survivor或者在Survivor之间移动时,对象的GC年龄自动累加,当GC年龄超过默认阈值15时,会将该对象移动到老年代,可以通过参数-XX:MaxTenuringThreshold 对GC年龄的阈值进行设置。

老年代

老年代的空间大小即-Xmx 与-Xmn 两个参数之差,用于存放经过几次Minor GC之后依旧存活的对象。当老年代的空间不足时,会触发Major GC/Full GC,速度一般比Minor GC慢10倍以上。老年代采用标记-整理算法。
“当老年代的空间不足时”的判断时间在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

三种垃圾回收

很多书上都说了垃圾回收分为minor GC、major GC和full GC三种,区别如下:
minor GC:新生代回收。
major GC:老年代回收。
full GC:整个堆空间回收。
其实后来整理发现,major GC和full GC的界限并不是那么明显,比如上文整理的full GC就是由minor GC触发的,而目的也是为了老年代的回收。相比于对这些概念性的较真,不如更关心下实际生产中的使用。
full GC的触发条件还有:
1.System.gc();
2.老年代空间不足。若full GC后仍不足,则抛出OOM:Java Heap Space;
3.永久代空间不足。若full GC后仍不足,则抛出OOM:Perm Gen;
4.CMS GC时出现Promotion Fail和Concurrent Mode Failer,即minor GC后,servivor放不下,老年代也放不下;
5.统计得到的minor GC晋升老年代的平均大小大于老年代剩余空间;

下文我们说说常见的GC回收器

你可能感兴趣的:(jvm)