打扫房间的各种方法 —— 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虚拟机的垃圾收集器清单

关于Java垃圾收集器的知识整理,请收藏好。

不改变正负数相对顺序,重排数组,使负数在正数之前

  最近看到了来自 v_JULY_v 的博客一个关于这个问题的解决(原文链接 :nhttp://blog.csdn.net/v_JULY_v/article/details/732...

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

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

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

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

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

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

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

标记-清除算法最基础的收集算法是“标记-清除” (Mark-Sweep ) 算法,如同它的名字一样 ,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的...

Java虚拟机(四)垃圾收集算法

在本系列上一篇文章中我讲到了垃圾标记算法,垃圾被标记后,GC就会对垃圾进行收集,垃圾收集有很多种算法,这篇文章就来介绍常用的垃圾收集算法的思想。

垃圾收集算法与内存分配策略--《深入理解Java虚拟机》学习笔记

垃圾收集算法与内存分配策略–《深入理解Java虚拟机》学习笔记一、对象存活算法判定1、何为引用 如果reference类型的数据中存储的数值代表另一块内存的起始地址,则称这块内存代表着一个引用;有一...

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

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

Java虚拟机(四)垃圾收集算法

1.标记-清除算法 标记-清除算法(Mark-Sweep)是一种常见的基础垃圾收集算法,它将垃圾收集分为两个阶段: 标记阶段:标记出可以回收的对象。清除阶段:回收被标记的对象所占用的空间。 ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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