推荐!可视化垃圾回收算法

标签: 可视化 垃圾回收 算法 | 发表时间:2014-09-28 18:41 | 作者:
出处:http://kb.cnblogs.com/

  英文原文: http://spin.atomicobject.com/2014/09/03/visualizing-garbage-collection-algorithms/

  大部分开发者都认为自动垃圾回收器是理所当然的。实际上,这只是语言运行时提供的一项实用功能,旨在简化我们的开发工作。

  但是如果尝试着了解垃圾回收器的内部原理,你会发现很难弄明白。除非熟悉它的工作流程和错误处理方式,否则内部成千上万的实现细节会让你不知所措。

  我编译了一个有五种不同的垃圾回收算法工具。程序运行时会创建一个动画界面。你可以从 github.com/kenfox/gc-viz上获取动画和代码来实现。非常让我惊讶的是,这个简单的动画显现出这些重要的算法。

  任务完成后清理: aka No GC

  清理垃圾最简单可行的方法就是等一项任务完成之后,一次性处理所有的垃圾。这项技术非常有用,特别是如果能将一项任务分解成许多小任务。例如,Apache网络服务器在每次请求时创建一个小内存池并在请求完成后将创建的整个内存池完全释放。

  上图的动画显示了一个正在运行的程序。整张图片代表程序的内存区。内存区在开始时是黑色,黑色表明内存尚未被使用。闪着鲜绿色和黄色的区域表明该内存区域正在读写。颜色随着时间变化,你可以观察内存的使用情况,也可以看到当前的活动情况。如果仔细观察,你会发现内存区域中开始出现一些程序执行过程中会忽略的区域。这些区域就成了所谓的垃圾——程序不能访问和使用。垃圾区域之外的的内存区域是可用的。

  该程序的内存充足,所以不必担心程序运行时垃圾的清理。在后面的例子中我将一直使用这个简单的程序。

  引用计数回收器

  另一个简单的解决方案是对你使用的资源(此处指内存中的对象)进行计数,当计数值变为0时,对其进行处理。这是一项广泛使用的技术,当开发者将垃圾回收添加到现有系统中时——这是唯一一个容易与其他资源管理器和现有代码库集成的垃圾回收器。苹果在为Objective-C发布了标志-擦除垃圾回收器后明白这个事实。发布产品出现很多问题以致于他们不得不废弃该项特性,取而代之的是性能良好的自动引用计数回收器。

  上面的动画显示了相同的程序,但是此时它将通过对内存中每一对象引用计数来处理垃圾。红色闪烁表示引用计数行为。引用计数的优势在于垃圾会被很快检测到——你可以看到红色闪烁过后紧接着该区域变黑。

  遗憾的是引用计数存在诸多问题。最糟糕的是,它不能处理循环结构。而循环结构非常常见——继承或反向引用都将建立一个循环,该结构将造成内存泄露。引用计数的开销也很大 ——从动画中可以看到即使当内存使用不在增长时,红色闪烁一直持续。CPU运算速度很快,但内存读写很慢,而计数器不断被加载并保存至内存。所有这些计数器的更新很难保证数据的只读或线程安全。

  引用计数是一种分摊算法(开销遍布整个程序运行时),但这是种分摊算法具有偶然性,不能保证反应时间。例如,程序中存在一个很大的树型结构。最后一段使用树的程序将触发对整个树的处理,墨菲说过事情如果有变坏的可能,不管这种可能性有多小,它总会发生。这里没有其他的分摊算法,所以分摊的偶然特征可能取决于数据。(所有这些算法有并发或部分并发的命令,但这些都是超出了程序可演示的范围。)

  标记-擦除回收器

  标记-擦除消除了引用计数存在的一些问题。它能够轻松解决循环结构在引用技术中存在的问题,由于不需维持计数,系统开销比较低。

  该算法舍弃垃圾检测的实时性。动画中,有一段运行时间没有任何红色的闪烁,然后突然出现许多红色闪烁表明当前正在标记活动对象。在标记完成后,程序要遍历整个内存空间并处理垃圾。在动画中你还将注意到—— 许多区域立刻变黑而不像引用计数方式那样随着时间慢慢变黑。

  标记-擦除比引用计数要求更高的一致性实现,而且很难移植到现有系统中。在标记阶段需要遍历所有活动数据,甚至是封装在对像中的数据。如果一个对象不支持遍历,那么尝试将标记-擦除移植到代码中风险太大。标记-擦除的另一个不足之处在于擦除阶段必须遍历整个内存来查找垃圾。对于一个产生垃圾较少的系统,这不是问题,但现在的函数式编程风格产生了大量的垃圾。

  标记-压缩回收器

  在前面的动画中你可能注意到一点,对象从不移动。一旦对象在内存中分配,该对象的存储位置就不会再改变,即使被散步在黑色区域的内存碎片包围。下面两种算法用完全不同的方式改变了这种现象。

  标记-压缩算法不是仅通过标记内存区域是否空闲来处理内存,而是通过将对象移动到空闲表来实现。对象通常按照内存顺序存储,先分配的对象在内存的低地址空间——但是处理对象造成的空缺将随着对象的移动变大。

  移动对象意味着新对象只能在已使用内存的末尾创建。这就是所谓的“bunp”分配器,和栈分配器一样,但不限制栈空间。有些使用bump分配器的系统甚至不用调用栈存储数据,他们只在堆中分配调用帧,像其他对象一样对待。

  有时理论高于实践,另一个优势是当对象被压缩后,程序能够像访问硬件高速缓存一样访问内存。不确定你能否看到这个好处——尽管引用计数和标记-擦除使用的内存分配器很复杂,但调试效果很好,效率也很高。

  标记-压缩是算法很复杂,需要多次遍历所有分配对象。在动画中可以看到紧随红色闪烁的活动对象其后的是大量读和写标记为目的地计算,对象被移动,最终引用固定指向移动后的对象。这个复杂程序背后最大的优点是内存开销非常小。Oracle的Hotspot JVM使用了多种不同垃圾回收算法。而全局对象空间使用标记-压缩回收算法。

  拷贝回收器

  最后使用动画显示的算法是大多数高性能垃圾收集系统的基础。它和标记-压缩是一样的移动回收器,但是相比之下实现却非常简单。它使用两块内存空间,在两个内存间交替复制活动对象。实际上,空间不止两块,这些空间用于不同代对象,新的对象在一个空间中创建,如果生命周期没有结束就会被复制到另一个空间,如果长期存在就会被复制到一个永久性空间。如果你听说一个垃圾收集器是分代的或短暂的,通常是多空间拷贝回收器。

  除了简单性和灵活性,该算法的主要优势在于只要在活动对象上花时间。没有独立的标记阶段必须被擦除或压缩。在遍历活动对象期间,对象会被立即复制,弥补了以往对象在引用计数时的不足。

  在动画中,你可以看到回收过程中乎所有的数据从一个空间复制到另一个空间。对该算法来说是个糟糕的情况,这是人们谈论优化垃圾收集器的一个原因。如果你能调整内存并有优化分配,使得在回收开始前大部分对象都废弃了,那么你就能兼顾安全函数式编程风格和高性能。

相关 [可视化 垃圾回收 算法] 推荐:

推荐!可视化垃圾回收算法

- - 博客园_知识库
  英文原文: http://spin.atomicobject.com/2014/09/03/visualizing-garbage-collection-algorithms/.   大部分开发者都认为自动垃圾回收器是理所当然的. 实际上,这只是语言运行时提供的一项实用功能,旨在简化我们的开发工作.

JVM 垃圾回收算法

- - 码蜂笔记
《深入理解Java虚拟机:JVM高级特性与最佳实践》-笔记. 垃圾回收,Garbage Collection,简称GC. 判断对象是否存活一般有两种方式:. 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收. 此方法简单,无法解决对象相互循环引用的问题.

HotSpot 垃圾回收算法实现

- - 码蜂笔记
《深入理解Java虚拟机:JVM高级特性与最佳实践》-笔记. 在可达性分析期间整个系统看起来就像被冻结在某个时间点上,不可以出现分析过程中对象引用关系还在不断变化的情况. 一致性要求导致GC进行时必须停顿所有Java执行线程. 即使在号称不会发生停顿的CMS收集器中,枚举根节点时也是必须停顿的. HotSpot使用的是准确式GC,当执行系统停顿下来后,并不需要一个不漏地检查完所有执行上下文和全局的引用位置,这是通过一组称为OopMap的数据结构来达到的.

Java/JVM垃圾回收机制和算法总结

- - ITeye博客
Java垃圾回收器是Java虚拟机(JVM)的三个重要模块(另外两个是解释器和多线程机制)之一,为应用程序提供内存的 自动分配(Memory Allocation)、自动回收(Garbage Collect)功能,这两个操作都发生在Java堆上(一段内存快). 某一个时点,一个对象如果有一个以上的引用(Rreference)指向它,那么该对象就为活着的(Live),否则死亡(Dead),视为垃圾,可被垃圾回收器回收再利用.

JVM垃圾回收算法 总结及汇总

- - 编程语言 - ITeye博客
先看一眼JVM虚拟机运行时的内存模型:. 1.方法区 Perm(永久代、非堆). 3.本地方法栈 (Native方法). 1 首先的问题是:jvm如何知道那些对象需要回收. 两种标识算法、三种回收算法、两种清除算法、三种收集器. 每个对象上都有一个引用计数,对象每被引用一次,引用计数器就+1,对象引用被释放,引用计数器-1,直到对象的引用计数为0,对象就标识可以回收.

jvm垃圾回收

- Cano - 淘宝共享数据平台 tbdata.org
在jvm中堆空间划分为三个代:年轻代(Young Generation)、年老代(Old Generation)和永久代(Permanent Generation). 年轻代和年老代是存储动态产生的对象. 永久带主要是存储的是java的类信息,包括解析得到的方法、属性、字段等等. 我们这里讨论的垃圾回收主要是针对年轻代和年老代.

Java中的垃圾回收

- - Java译站
前文中对标记删除算法的介绍更多还是偏理论性质的. 实践中,为了更好地满足现实的场景及需求,还需要对算法进行大量的调整. 举个简单的例子,我们来看下JVM需要记录哪些信息才能让我们得以安全地分配对象空间. 碎片及整理(Fragmenting and Compacting). JVM在清除不可达对象之后,还得确保它们所在的空间是可以进行复用的.

Java垃圾回收调优

- - 编程语言 - ITeye博客
在Java中,通常通讯类型的服务器对GC(Garbage Collection)比较敏感. 通常通讯服务器每秒需要处理大量进出的数据包,需要解析,分解成不同的业务逻辑对象并做相关的业务处理,这样会导致大量的临时对象被创建和回收. 同时服务器如果需要同时保存用户状态的话,又会产生很多永久的对象,比如用户session.

谈谈ActionScript垃圾回收(下)

- Tomyail - Kevin Cao's Blog
前文我们介绍了GC的工作机制和帮助GC更好工作的最佳实践. 其实只要我们遵守谁创建谁清理的原则来管理对象,就能基本上避免回收失败,也就是我们通常说的内存泄漏问题. 但是在实际项目中我们还会看到各种原因引起的内存泄漏,接下来就让我们一起来找出病因. 首先我们需要观察症状,也就是内存的使用曲线. 排查的方法是反复执行一些创建和删除对象的方法、反复加载和卸载子文件.

谈谈ActionScript垃圾回收(上)

- Jia - Kevin Cao's Blog
在《给AS程序员的一点建议一文》中我提到了释放资源的重要性. 最近在一些项目过程中我又对这方面有了更多的理解,在此希望能够分享给大家. 首先让我们来回顾一下关于垃圾回收(Garbage Collection,下文简称GC)的一些知识. 要阅读本文,你需要对GC机制有些基本认识. 在ActionScript中,我们没有API可以直接删除一个对象,也不能控制Player进行GC.