热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

垃圾回收器的算法,垃圾回收算法与实现

引用计数算法有着诸多的优点,但它的缺点也是很明显的。整个过程分为两个阶段:标记阶段找到所有存活对象;清除阶段清除所有垃圾对象。它的整个过程可以描述为:标记所有的存活对象;通过重新调


1.引用计数算法


“参照计数”(Reference Counting )算法计算每个对象指向它的指针数,并在一个指针指向自己时将计数值加1。 删除指向自己的指针后,计数值将减少1。 如果计数值减少到0,则指向该对象的指针将不再存在,因此可以安全地丢弃。 可以直观地用下图表示。


引用计数算法的优点是内存管理开销在APP应用程序运行时分散,非常“平滑”,不需要因为垃圾回收而停止APP应用程序的运行。 另一个优点是空间参考的局部性很高。 当一个对象的引用计数值变为0时,系统不需要访问堆中其他页面上的设备。 此外,后面介绍的一些垃圾回收算法在回收之前遍历所有生存单元。 这可能会引起页面转换操作。 最后的引用计数算法提供了类似堆栈分配的方式,丢弃就回收。 接下来看到的一些垃圾回收算法是对象废弃后生存一段时间再回收。


引用计数算法有很多优点,但其缺点也很明显。 首先,您可以看到的是时间开销,每次创建或释放对象时都会计算引用计数值,从而产生额外的开销。 第二种空间开销,每个对象必须留出额外的空间来存储参考计数值,以保持自己参考的数目; 引用计数算法的最大缺点是无法处理环引用,如下图所示。


这里蓝色的两个对象既不能到达也不能回收。 因为它们互相参照,各自的计数值不是0。 这对引用计数算法来说是无能为力的,但其他垃圾回收算法能很好地处理环引用。


引用算法最有名的运用莫过于微软的COM技术,著名的IUnknown接口:


interfaceiunknown { virtual hresult _ stdcallqueryinterface,void**PPV}=0; virtualulong_stdcalladdref(=0; virtualulong_stdcallrelease(=0; }


这里的AddRef和Release是为了让组件本身能够管理生命周期,客户端程序只关心接口,而不需要关心组件的生命周期。 以下是一个简单的使用案例。


int main () { IUnknown* pi=CreateInstance ); IX* pix=NULL; hresult HR=pi-query接口(iid _ IX,) void * (pix ); if (安全(HR ) ) { pix-DoSomething; pix-Release (; (} pi-Release ); }上的客户端程序已经在CreateInstance中调用了AddRef,因此不需要再次调用,而是在使用接口后调用Release。 这将改变组件自身维护的计数值。 以下代码显示了一个简单的实现AddRef和Release的示例。


ULONG _stdcall AddRef () { return m_cRef; }ULONG _stdcall Release () if(-m_cref==0) { delete this; 返回0; } return m_cRef; }


在编程语言Python中,使用也是一种引用计数算法,如果对象的引用计数值为0,则调用__del__函数。 至于为什么Python选择引用计数算法,我读的文章说,因为Python是脚本语言,所以经常与C/C等语言进行交互,通过使用引用计数算法避免对象在内存中的更改因为Python还引入了gc模块来解决环引用问题,所以本质上python GC的方案是混合引用计数和跟踪(后面介绍的三种算法)两种垃圾回收机制。


2.标记-清除算法


“标记-清除”(朴素的黑猫-Sweep )算法依赖于确定可以通过一次全局遍历回收所有存活对象的对象。 遍历过程从根中找到所有可到达的对象,其他不可到达的对象是垃圾对象,可以回收。 整个过程分为两个阶段。 标记阶段会找到所有的生存对象。 逐步清除所有垃圾对象。


标记阶段


清除舞台

lign="left"> 

      相比较引用计数算法,标记-清除算法可以非常自然的处理环形引用问题,另外在创建对象和销毁对象时时少了操作引用计数值的开销。它的缺点在于标记-清除算法是一种“停止-启动”算法,在垃圾回收器运行过程中,应用程序必须暂时停止,所以对于标记-清除算法的研究如何减少它的停顿时间,而分代式垃圾收集器就是为了减少它的停顿时间,后面会说到。另外,标记-清除算法在标记阶段需要遍历所有的存活对象,会造成一定的开销,在清除阶段,清除垃圾对象后会造成大量的内存碎片。

3.标记-缩并算法

     标记-缩并算法是为了解决内存碎片问题而产生的一种算法。它的整个过程可以描述为:标记所有的存活对象;通过重新调整存活对象位置来缩并对象图;更新指向被移动了位置的对象的指针。

标记阶段:

 

清除阶段:

 

标记-压缩算法最大的难点在于如何选择所使用的压缩算法,如果压缩算法选择不好,将会导致极大的程序性能问题,如导致Cache命中率低等。一般来说,根据压缩后对象的位置不同,压缩算法可以分为以下三种:

1. 任意:移动对象时不考虑它们原来的次序,也不考虑它们之间是否有互相引用的关系。
2. 线性:尽可能的将原来的对象和它所指向的对象放在相邻位置上,这样可以达到更好的空间局部性。
3. 滑动:将对象“滑动”到堆的一端,把存活对象之间的自由单元“挤出去”,从而维持了分配时的原始次序。

4.节点拷贝算法

节点拷贝算法是把整个堆分成两个半区(From,To), GC的过程其实就是把存活对象从一个半区From拷贝到另外一个半区To的过程,而在下一次回收时,两个半区再互换角色。在移动结束后,再更新对象的指针引用,GC开始前的情形:

GC结束后的情形:

      节点拷贝算法由于在拷贝过程中,就可以进行内存整理,所以不会再有内存碎片的问题,同时也不需要再专门做一次内存压缩。,而它最大的缺点在于需要双倍的空间。

5.总结

      本文总共介绍了四种经典的垃圾回收算法,其中后三种经常称之为跟踪垃圾回收,因为引用计数算法能够平滑的进行垃圾回收,而不会出现“停止”现象,经常出现于一些实时系统中,但它无法解决环形问题;而基于跟踪的垃圾回收机制,在每一次垃圾回收过程中,要遍历或者复制所有的存活对象,这是一个非常耗时的工作,一种好的解决方案就是对堆上的对象进行分区,对不同区域的对象使用不同的垃圾回收算法,分代式垃圾回收器正是其中一种,CLR和JVM中都采用了分代式垃圾回收机制,但它们在处理上又有些不同,后面的文章再详细介绍这两种垃圾回收器的区别。

 更加详细请参见于:http://www.cnblogs.com/Terrylee/

 

更多内容:http://blog.csdn.net/wallwind


推荐阅读
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 最近团队在部署DLP,作为一个技术人员对于黑盒看不到的地方还是充满了好奇心。多次咨询乙方人员DLP的算法原理是什么,他们都以商业秘密为由避而不谈,不得已只能自己查资料学习,于是有了下面的浅见。身为甲方,虽然不需要开发DLP产品,但是也有必要弄明白DLP基本的原理。俗话说工欲善其事必先利其器,只有在懂这个工具的原理之后才能更加灵活地使用这个工具,即使出现意外情况也能快速排错,越接近底层,越接近真相。根据DLP的实际用途,本文将DLP检测分为2部分,泄露关键字检测和近似重复文档检测。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 本文探讨了在Java多线程环境下,如何确保具有相同key值的线程能够互斥执行并按顺序输出结果。通过优化代码结构和使用线程安全的数据结构,我们解决了线程同步问题,并实现了预期的并发行为。 ... [详细]
  • 作者:守望者1028链接:https:www.nowcoder.comdiscuss55353来源:牛客网面试高频题:校招过程中参考过牛客诸位大佬的面经,但是具体哪一块是参考谁的我 ... [详细]
  • 信用评分卡的Python实现与评估
    本文介绍如何使用Python构建和评估信用评分卡模型,涵盖数据预处理、模型训练及验证指标选择。附带详细代码示例和视频教程链接。 ... [详细]
  • 解决C++编译错误C3867的方法
    本文详细介绍了在不同版本的Visual Studio中,如何正确处理成员函数指针以避免编译错误C3867。同时,提供了一个具体的代码示例及其优化方案。 ... [详细]
  • 深入理解Redis的数据结构与对象系统
    本文详细探讨了Redis中的数据结构和对象系统的实现,包括字符串、列表、集合、哈希表和有序集合等五种核心对象类型,以及它们所使用的底层数据结构。通过分析源码和相关文献,帮助读者更好地理解Redis的设计原理。 ... [详细]
  • 本文探讨了MariaDB在当前数据库市场中的地位和挑战,分析其可能面临的困境,并提出了对未来发展的几点看法。 ... [详细]
  • 深入了解 Windows 窗体中的 SplitContainer 控件
    SplitContainer 控件是 Windows 窗体中的一种复合控件,由两个可调整大小的面板和一个可移动的拆分条组成。本文将详细介绍其功能、属性以及如何通过编程方式创建复杂的用户界面。 ... [详细]
  • dotnet 通过 Elmish.WPF 使用 F# 编写 WPF 应用
    本文来安利大家一个有趣而且强大的库,通过F#和C#混合编程编写WPF应用,可以在WPF中使用到F#强大的数据处理能力在GitHub上完全开源Elmis ... [详细]
  • 本文总结了Java程序设计第一周的学习内容,涵盖语言基础、编译解释过程及基本数据类型等核心知识点。 ... [详细]
  • 本文介绍如何使用布局文件在Android应用中排列多行TextView和Button,使其占据屏幕的特定比例,并提供示例代码以帮助理解和实现。 ... [详细]
  • 本文深入探讨了计算机网络的基础概念和关键协议,帮助初学者掌握网络编程的必备知识。从网络结构到分层模型,再到传输层协议和IP地址分类,文章全面覆盖了网络编程的核心内容。 ... [详细]
  • 配置Windows操作系统以确保DAW(数字音频工作站)硬件和软件的高效运行可能是一个复杂且令人沮丧的过程。本文提供了一系列专业建议,帮助你优化Windows系统,确保录音和音频处理的流畅性。 ... [详细]
author-avatar
xlenny
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有