JVM 学习笔记20180206之二 分代回收算法

我们一般讨论的垃圾回收主要针对Java堆内存中的新生代和老年代,也正因为新生代和老年代结构上的不同,所以产生了分代回收算法,即新生代的垃圾回收和老年代的垃圾回收采用的是不同的回收算法。
针对新生代,主要采用复制算法
针对老年代,通常采用标记-清除算法或者标记-整理算法来进行回收

复制算法

复制算法的思想是将内存分成大小相等的两块区域,每次使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块区域上,然后对该块进行内存回收。示例图如下所示:

原图颜色无法区分,故重新画了一张图。

这个算法实现简单,并且也相对高效,但是代价就是需要将牺牲一半的内存空间用于进行复制。有研究表明,新生代中的对象98%存活期很短,所以并不需要以1:1的比例来划分整个新生代,通常的做法是将新生代内存空间划分成一块较大的Eden区和两块较小的Survivor区,两块Survivor区域的大小保持一致。每次使用Eden和其中一块Survivor区,当回收的时候,将还存活的对象复制到另外一块Survivor空间上,最后清除Eden区和一开始使用的Survivor区。假如用于复制的Survivor区放不下存活的对象,那么会将对象存到老年代。

HotSpot虚拟机默认Eden和Survivor的大小比例是8:1:1,也就是说新生代中牺牲掉10%的空间而不是一半的空间。

标记-清除算法

标记-清除(Mark-Sweep)算法分为两个阶段:标记阶段、清除阶段;

标记阶段将标记出需要回收的对象空间,然后在下一个阶段清除阶段里面,将这些标记出来的对象空间回收掉。这种算法有两个主要问题:一个是标记和清除的效率不高,另一个问题是在清理之后会产生大量不连续的内存碎片,这样会导致在分配大对象时候无法找到足够的连续内存而触发另一次垃圾收集动作。标记-清除算法示例图如下所示:

标记-整理算法

标记-整理(Mark-Compact)算法有效预防了标记-清除算法中可能产生过多内存碎片的问题。在标记需要回收的对象以后,它会将所有存活的对象空间挪到一起,然后再执行清理。示例图如下所示:

标记-整理算法图解

标记-整理通常会在标记-清除算法里面作为备选方案,为了防止标记-清除后产生大量内存碎片而无法为大对象分配足够内存的情况,如后面所讲的Serial Old收集器(基于标记-整理算法实现)将会作为CMS收集器(基于标记-清除算法实现)的备选方案。

猜你喜欢

转载自my.oschina.net/mengzhang6/blog/1619301
今日推荐