垃圾(Garbage)就是程序需要回收的对象,如果一个对象不在被直接或间接地引用,那么这个对象就成为了「垃圾」,它占用的内存需要及时地释放,否则就会引起「内存泄露」。有些语言需要程序员来手动释放内存(回收垃圾),有些语言有垃圾回收机制(GC)。本文就来讨论GC实现的三种基本方式。

其实这三种方式也可以大体归为两类:跟踪回收,引用计数。美国IBM的沃森研究中心David F.Bacon等人发布的「垃圾回收统一理论」一文阐述了一个理论:任何垃圾回收的思路,无非以上两种的组合,其中一种的改善和进步,必然伴随着另一种的改善和进步。

跟踪回收

跟踪回收的方式独立于程序,定期运行来检查垃圾,需要较长时间的中断。

标记清除

标记清除的方式需要对程序的对象进行两次扫描,第一次从根(Root)开始扫描,被根引用了的对象标记为不是垃圾,不是垃圾的对象引用的对象同样标记为不是垃圾,以此递归。所有不是垃圾的对象的引用都扫描完了之后。就进行第二次扫描,第一次扫描中没有得到标记的对象就是垃圾了,对此进行回收。

复制收集

复制收集的方式只需要对对象进行一次扫描。准备一个「新的空间」,从根开始,对对象进行扫,如果存在对这个对象的引用,就把它复制到「新空间中」。一次扫描结束之后,所有存在于「新空间」的对象就是所有的非垃圾对象。

这两种方式各有千秋,标记清除的方式节省内存但是两次扫描需要更多的时间,对于垃圾比例较小的情况占优势。复制收集更快速但是需要额外开辟一块用来复制的内存,对垃圾比例较大的情况占优势。特别的,复制收集有「局部性」的优点。

在复制收集的过程中,会按照对象被引用的顺序将对象复制到新空间中。于是,关系较近的对象被放在距离较近的内存空间的可能性会提高,这叫做局部性。局部性高的情况下,内存缓存会更有效地运作,程序的性能会提高。

对于标记清除,有一种标记-压缩算法的衍生算法:

对于压缩阶段,它的工作就是移动所有的可达对象到堆内存的同一个区域中,使他们紧凑的排列在一起,从而将所有非可达对象释放出来的空闲内存都集中在一起,通过这样的方式来达到减少内存碎片的目的。

引用计数

引用计数是指,针对每一个对象,保存一个对该对象的引用计数,该对象的引用增加,则相应的引用计数增加。如果对象的引用计数为零,则回收该对象。

优点:引用计数最大的优点就是容易实现,C++程序员应该都实现过类似的机制。二是成本小,基本上引用计数为0的时候垃圾会被立即回收,而其他方法难以预测对象的生命周期,垃圾存在的时间都会比这个方法高。另,这种垃圾回收方式产生的中断时间最短。

缺点:最著名的缺点就是如果对象中存在循环引用,就无法被回收。例如,下面三个对象互相引用,但是不存在从根(Root)指向的引用,所以已经是垃圾了。但是引用计数不为0.

还有一个缺点就是,引用计数不适合在并行中使用,多个线程同时操作引用计数,会引起数值不一样的问题从而导致内存错误。所以引用计数必须采用独占方式,如果引用操作频繁,那么加锁等并发控制机制的开销是相当大的。

Perl和Python采用了这种GC机制。

它们的衍生算法

分代回收

这种回收方式用了程序的一种特性:大部分对象会从产生开始在很短的时间内变成垃圾,而存在的很长时间的对象往往都有较长的生命周期。高频对新生成的对象进行回收,称为「小回收」,低频对所有对象回收,称为「大回收」。每一次「小回收」过后,就把存活下来的对象归为「老生代」,「小回收」的时候,遇到老生代直接跳过。大多数分代回收算法都采用的「复制收集」方法,因为小回收中垃圾的比例较大。

这种方式存在一个问题:如果在某个新生代的对象中,存在「老生代」的对象对它的引用,它就不是垃圾了,那么怎么制止「小回收」对其回收呢?这里用到了一中叫做写屏障的方式。

程序对所有涉及修改对象内容的地方进行保护,被称为「写屏障」(Write Barrier)。写屏障不仅用于分代回收,也用于其他GC算法中。

在此算法的表现是,用一个记录集来记录从新生代到老生代的引用。如果有两个对象A和B,当对A的对象内容进行修改并加入B的引用时,如果①A是「老生代」②B是「新生代」。则将这个引用加入到记录集中。「小回收」的时候,因为记录集中有对B的引用,所以B不再是垃圾。

增量回收

上面的算法缩短了「GC平均中断时间」,但是在对实时性要求很高的程序中,对「GC最高中断时间」的要求更高。比如,自动驾驶软件,如果某次GC中断了0.1s,那么损失可能是致命的。

增量回收就是将GC分成几部分来执行。设置「GC最多中断10ms」这样的条件限制来使GC的终端时间视作可预测的。

但是,在两段的GC程序之间,引用关系可能发生了变化。所以,这种GC算法也要写屏障,来记录引用关系的变化。虽然这种方式控制了中断最高时间,但是由于中断次数增加,GC总时间是增加的。

并行回收

基本原理是,在程序运行的同时进行GC工作,最大化CPU的性能。但是这种方式也要面对增量回收的问题,所以也要进行写屏障操作。

然而这种方式也并未做到完全不暂停原程序的运行,在某些特定的GC阶段还是要暂停原程序。多核化迅速发展的今天,这种算法也在不断优化。不间断原程序实现并行回收这个领域是相当值得期待的。

垃圾回收(GC)的三种基本方式的更多相关文章

  1. 几种垃圾回收GC概述

    垃圾回收机制 引用计数回收器(Reference Counting Collector) 原理是在每个对象内部维护一个整数值,叫做这个对象的引用计数,当对象被引用时引用计数加一,当对象不被引用时引用计 ...

  2. 性能测试三十五:jvm垃圾回收-GC

    垃圾回收-GC 三个问题 哪些内存需要回收? 什么时候回收? 如何回收? YoungGC和FullGC: 新生代引发的GC叫YoungGC 老年代引发的GC叫FullGC FullGC会引起整个Jvm ...

  3. 修改Tomcat的jvm的垃圾回收GC方式为CMS

    修改Tomcat的jvm的垃圾回收GC方式 cp $TOMCAT_HOME/bin/catalina.sh $TOMCAT_HOME/bin/catalina.sh.bak_20170815 vi $ ...

  4. 垃圾回收GC:.Net自己主动内存管理 上(三)终结器

    垃圾回收GC:.Net自己主动内存管理 上(三)终结器 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主 ...

  5. Java 垃圾回收(GC) 泛读

    Java 垃圾回收(GC) 泛读 文章地址:https://segmentfault.com/a/1190000008922319 0. 序言 带着问题去看待 垃圾回收(GC) 会比较好,一般来说主要 ...

  6. C# 三种打印方式含代码

    一:C#代码直接打印pdf文件(打印质保书pdf文件) 引用: 代码注释很详细了. private void btn_pdf_Click(object sender, RoutedEventArgs ...

  7. 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配

    垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

  8. java代码中init method和destroy method的三种使用方式

    在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等等. ...

  9. 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法

    垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

随机推荐

  1. hdu 3927 Math Geek

    纯数论题,不解释!!!! 代码如下: #include<stdio.h> int main(){ ,m; scanf("%d",&t); while(t--){ ...

  2. SaaS系列介绍之八: SaaS的运营模式

    1 引言 软件的核心是它为用户解决领域相关问题的能力.               ________Eric Evans,<领域驱动设计> 传统的软件生命周期中,软件的维护占整个过程的70 ...

  3. 统计学习方法笔记--EM算法--三硬币例子补充

    本文,意在说明<统计学习方法>第九章EM算法的三硬币例子,公式(9.5-9.6如何而来) 下面是(公式9.5-9.8)的说明, 本人水平有限,怀着分享学习的态度发表此文,欢迎大家批评,交流 ...

  4. 中断服务程序不用interrupt关键字也可实现中断,该关键字是否必须?

    2013-06-20 11:13:35 中断服务程序不用interrupt关键字也可实现中断,该关键字是否必须? 使用tools->pin connect,将INT5与pin.txt关联,模拟外 ...

  5. 在Vim里使用gtags-cscope

    用Vundle安装好gtags-cscope后,要在vimrc里添加如下设置: " cscopeset cscopetag                  " 使用 cscope ...

  6. SpringMVC中对Controller使用AOP

    转自http://usherlight.iteye.com/blog/1306111 正确配置spring aop,在controller中使用AOP 在controller中使用AOP的问题主要在于 ...

  7. C++ 类的内存分布

    C++类内存分布 转自:http://www.cnblogs.com/jerry19880126/p/3616999.html   先写下总结,通过总结下面的例子,你就会明白总结了. 下面总结一下: ...

  8. 两个STL网址 总结的很好 && c++堆的网址

    http://www.cnblogs.com/bigcat814/ http://blog.sina.com.cn/s/blog_7065a9de010154ve.html 堆 http://www. ...

  9. 函数buf_ptr_get_fsp_addr

    #define FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID  34 /****************************************************** ...

  10. 设计模式 - observer

    简单来讲,就是observer依赖于subject,当subject发生变化时,observer得到通知,并将状态与subject同步,常来用于维护对象间状态的一致性. observer的工作过程大体 ...