Java 二、垃圾回收算法与思想

beijingniuniu · 2019年10月14日 · 最后由 陈子昂 回复于 2019年10月15日 · 1275 次阅读

一、标记 - 清除算法

标记 - 清除算法是现代垃圾回收算法的思想基础。标记 - 清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。标记阶段,标记出所有需要回收的对象,在标记完成后,统一回收所有被标记的对象。后续的算法都是基于这种思路并对其不足进行改进的。它的主要缺点有两个:1、一个是效率问题,标记和清除两个过程的效率都不高。2、另一个是空间问题,标记清除后,空间是不连续,的,产生大量的内存碎片,空间碎片太多会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而导致提前触发垃圾回收动作。

二、复制算法

与标记 - 清除算法相比,复制算法是一种相对高效的回收方法。它的核心思想是:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用中的内存块的所有对象,交换两个内存的角色,完成垃圾回收。如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量并不会太大。因此,在真正需要垃圾回收的时刻,复制算法的效率还是很高的。又因为对象是在垃圾回收过程中统一被复制到新的内存空间中,因此,可确保回收后的内存空间没有碎片。但是仍然有个缺点,就是这个算法,要将系统内存折半。

       在 java 的新生代串行垃圾回收器中,使用了复制算法的思想。新生代分为 eden 空间,from 空间和 to 空间三个部分,其中 from 和 to 空间可以视为用于复制的两块大小相同、地位相等,且可以进行角色互换的空间块。from 和 to 空间也称为 survivor 空间,即幸存者空间,用于存放未被回收的对象。

        在垃圾回收时,Eden 空间中的存活对象会被复制到未被使用的 survivor 空间中(假设 to,如图 s1),正在使用的 survivor 空间(假设 from,如图 s0)中的年轻对象也会被复制到 to 空间中(如图 s1)(大对象,或者老年对象会直接进入老年代,如果 to 空间已经满了,对象也会直接进入老年代),此时 eden 空间和 from 空间中的剩余对象就是垃圾对象,可以直接清空,to 空间则存放此次回收后的存活对象。这种复制算法,既可以保证空间的连续性,又避免了大量的内存空间浪费。

        复制算法比较适合新生代,因为新生代,存活对象比较少,复制算法会 比较好。如果存活对象较多的时候,就要进行较多的复制操作,效率会变低,更关键是,如果不想浪费 50% 的空间,就需要额外的空间进行分配。所以老年代一般不适合复制算法,接下来说的标记 - 整理算法,适合老年代进行回收。

三、标记 - 整理算法,也叫标记 - 压缩算法,这种算法适用于老年代回收,它是在标记 - 清除算法基础上进行了优化,首先也是需要从根节点开始,对所有要回收的对象进行标记,但是之后,不是清理要回收的对象,而是将所有存活的对象压缩到内存的一端,之后,清理边界外所有的所有的空间。这种方法既避免了碎片的发生,又不需要两块相同的内存空间,因此性价比高。

四、分代收集算法

当前虚拟机垃圾收集都采用分代收集算法,这种算法不是什么新的思想,只是根据对象存活周期的不同将内存进行分块,新生代每次垃圾收集时发现大批对象死去,只有少量存活,所以只要付出少量存活对象的复制成本就可以完成收集,所以适用于复制算法。老年代中因为对象存活率高、没有额外空间对它进行分配担保,所以适合于标记 - 清除算法或是标记 - 整理算法来回收。
偶尔发布些技术文章,欢迎大家关注交流

共收到 3 条回复 时间 点赞

题外说一句,关注了很多这种前篇一律不负责任的 “技术公众号”,从这里转到那里,没有原创,没有质量保证,连续性都很弱。时长还发一些有的没的职场鸡汤,求职技巧,导致现在谁再推所谓技术公众号都是一笑了之。

Karaser 回复

你说的确实是,我也关注了很多公众号,也进行了一些选择性的看,但是如果都不关注的话,选择性的机会都没有了

没有代码例子得吗?比如 static 方法区,内部类,中生代在什么样得引用形式下返回新生代,都没说啊。。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册