打扫房间的各种方法 —— Java虚拟机的垃圾收集算法清单

原创 2017年08月20日 16:15:14

带着问题阅读

  • 垃圾收集时,除了效率,还要考虑什么?
  • 如果你是JVM设计者,在标记完可回收的对象后,你会怎么去收集垃圾对象?


导语

通过前面两讲的学习,我们已经知道JVM是如何判断对象是不是可以回收,以及回收是如何发起的。那么发起之后又是如何进行收集的呢,收集的算法有哪些,这是我们这一讲的内容。

本文是Effective Java专栏Java虚拟机专题的第六讲,如果你觉得看完之后对你有所帮助,欢迎订阅本专栏,也欢迎您将本专栏分享给你身边的工程师同学。

在学习本节课程之前,建议您了解一下以下知识点:


标记-清除算法

标记-清除算法是最基础的垃圾收集算法,这个算法在收集垃圾时分两步:

  1. 标记所有需要回收的对象;
  2. 标记完成后统一回收所有被标记的对象。

之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其进行改进而得到的。

它的不足主要有两个:

  • 一是效率问题,这种逐个进行清除的算法,效率固然不高;
  • 另一个是空间问题,标记清除后会产生大量不连续的内存碎片,导致以后需要分配较大对象时,无法找到足够大的连续内存而不得不提前触发一次垃圾收集动作。

注:《深入理解Java虚拟机》中,关于效率问题,周老师的原文是“标记和清除的效率都不高”,这里个人理解,标记是其他算法也需要的,所以标记效率不高不可以作为不选择这个算法的理由。

可以用下面这张图来理解标记-清除算法:



复制算法

针对标记-整理算法的不足,一种称为“复制”的算法出现了,它的收集步骤如下:

  1. 将内存划分为大小相等的两块;
  2. 当其中一块内存用完时,把存活对象复制到另一块上去,然后把原来的那一块全部清理掉。

这种算法实现简单,运行高效,同时也不会有内存碎片,代价是需要将内存缩小为原来的一半。

可以用下面这张图来理解“复制”算法:


现代虚拟机都采用这种算法来回收新生代根据IBM的专门研究,新生代中的对象,98%是“朝生夕死”的,所以没有必要按照1:1来划分成两个一样大小的内存,而是将内存划分为一块大的Eden区和两块小的Survivor区域每次使用Eden和其中一块Survivor

回收时,把Eden和使用的那块Survivor中存活的对象,一次性复制到那块未使用的Survivor中,然后清理掉Eden和刚刚使用的Survivor。

HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是说,每次新生代中可用内存为90%(80% + 10%),只有10%的内存会被浪费。

那么假如一次回收时,超过了10%的对象存活,Survivor内存不够存放时,该怎么办?

这时候就需要老年代来进行分配担保(Handle Promotion).

内存分配担保是指:如果另一块Survivor没有足够空间存放上一次新生代垃圾收集后留下来的存活对象,那么这些对象直接进入老年代。关于分配担保,后续课程还会详细介绍。


标记-整理算法

复制算法在对象存活率较高时就要做很多的复制操作,并且会经常需要分配担保,因此,这种算法是不适用于老年代的。

根据老年代存活率高的特点,有人提出了“标记-整理”算法,算法和“标记-清理”算法类似,但是标记之后,不直接对标记为可回收的对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界外的内存

可以用下面这张图来理解标记-整理算法



换个角度理解这几种算法

相信很多人看到这里,对于上面提到的几种算法的优劣,内存碎片那一块还是都可以理解的。比较难理解的是效率的问题,为什么“标记-整理”和“复制”算法就比“标记-清除”效率高呢?

我们可以这样理解,假设我们要清理一个文件夹中的文件:

“标记-清理”算法会这样做:先把要删除的文件,文件名前面加个“toDelete_”,表明是要删除的文件,然后对所有toDelete*的文件,逐个点击-右键-删除;在Linux上就是 rm -rf toDelete_file1.txt rm -rf toDelete_file2.txt rm -rf toDelete_file3.txt;......


“复制”算法也是标记哪些文件要删除,然后把不需要删除的文件,拷贝到另一个文件夹,接着在原来的文件,ctrl+A,右键-删除,一把全部删了。在Linux上就是 rm -rf *:


而“标记-整理”算法,也是先标记,接着把文件列表按照文件名排序,然后按住shift,选中从第一个“toDelete_”的文件,到最后一个文件之间的全部文件,然后右键-删除。在Linux上就是,rm -rf toDelete_*


这样就能理解为什么“标记-清理”会比其他两种算法慢了吧 :)


总结

这一讲讲解了虚拟机进行垃圾收集的几种常用算法,下一讲,我们就要看看,这几种算法,或者说方法论,是如何在各种垃圾收集器中具体实现的,同时我们也将一起了解JVM中,到底有哪些垃圾收集器。


参考资料

版权声明:本文为博主原创文章,未经博主允许不得转载。

用空间换时间 —— Java虚拟机的算法实现

HotSpot虚拟机是如何实现可达性分析算法的?

Java OutOfMemory异常清单 —— 在自己的机器上制造内存溢出

既然我们知道各个内存区域存储的内容,那么只要在代码上做一些手脚,就可以制造出OutOfMemory异常,这就是我们这一讲要做的事。...

常用垃圾收集算法——《深入理解Java虚拟机》笔记

概述垃圾收集器(Garbage Collection, GC)的历史要比Java久远,且并非Java独有,GC主要完成以下三件事情: 哪些内存需要回收 什么时候回收 如何回收 对于Java内存运行时区...

Java虚拟机学习笔记(2)——垃圾收集算法

上一篇文章学习了JVM基本的内存模型,JVM内存区域可以分为:方法区、堆区、虚拟机栈、本地方法栈、程序计数器。方法区和堆区属于线程共享区域, 而虚拟机栈、本地方法栈、程序计数器则属于线程隔离区域。其中...

深入学习Java虚拟机之——垃圾收集算法与垃圾收集器

今天我们将一起学习Java虚拟机使用垃圾收集算法和常见的垃圾收集器。Java虚拟机内存区域的程序计数器、虚拟机栈和本地方法栈3个区域是随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出出栈和入栈。...
  • jcncsdn
  • jcncsdn
  • 2016年05月06日 11:36
  • 517

[深入理解Java虚拟机]第三章 HotSpot的垃圾收集算法实现

枚举根节点从可达性分析中从GC Roots节点找引用链这个操作为例,可作为GC Roots的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)中 ,现在很多应用仅仅方...

Java虚拟机学习 - 垃圾收集算法

跟踪收集器 跟踪收集器采用的为集中式的管理方式,全局记录对象之间的引用状态,执行时从一些列GC  Roots的对象做为起点,从这些节点向下开始进行搜索所有的引用链,当一个对象到GC  Roots...

深入理解Java虚拟机笔记---垃圾收集算法

当对象判定为"已死"状态,虚拟就要采取一定的手段将这些对象从内存中移除,即回收垃圾,回收过程有采用一定的算法。如下是一些主要的垃圾收集算法: 1.标记-清除算法    该算法是最基础的算法,分为“...

Java虚拟机学习 - 垃圾收集算法

跟踪收集器 跟踪收集器采用的为集中式的管理方式,全局记录对象之间的引用状态,执行时从一些列GC  Roots的对象做为起点,从这些节点向下开始进行搜索所有的引用链,当一个对象到GC  Roots...

深入浅出java虚拟机系列:(二)GC&垃圾收集算法

1. 概述      GC需要完成的三件事:           哪些内存需要回收?           什么时候回收?           如何回收?      其中程序计算器、虚拟机栈、...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:打扫房间的各种方法 —— Java虚拟机的垃圾收集算法清单
举报原因:
原因补充:

(最多只允许输入30个字)