我们知道,垃圾回收在内存无限大的理想情况下是不需要的,正是因为内存存在的瓶颈,我们才需要垃圾回收。在《垃圾回收算法之引用计数算法》《垃圾回收算法之引用跟踪算法》两篇文章中,我们了解了垃圾回收算法的基本原理,并介绍了两种垃圾回收算法。本篇是在垃圾回收的前提下,通过代的机制更进一步地提升程序的性能。

一.程序局部性原理

程序局部性原理是一种总结性的原理,它是指在程序执行时呈现局部性规律,即在一段时间内,整个程序的执行仅限于程序中的某一部分。相应地,执行所访问的存储空间也局限于某个内存区域。

这个局部性原理有两层含义:一层是时间局部性,如果一个变量正在被访问,那么它近期很有可能被再次访问;一层是空间局部性,马上要使用的信息与正在使用的信息在空间地址上是临近的。

通俗地讲:对象越新,生存期越短,对象越老,生存期越长。

二.代

C#的GC把托管堆分为3代,依次为G0、G1、G2,G0总是用来分配新对象,垃圾回收中G0幸存的对象放到对象生存期更长的G1中,在下一次的回收G1中垃圾后,G1中垃圾的对象放到对象生存期更长的G2中;CLR初始化时会为G0、G1、G2分配空间预算,每一代的垃圾回收只有在本代的空间预算用完后才会启动垃圾回收。

下面,我们通过《CLR via C#》中的例子来理解代的运行机制:

在托管堆初始化时不包含对象,添加到堆中的对象称为0代对象。

在step1中,G0中新进了3个对象,过了一会了,对象c变得不可达了,现在我要分配一个新的对象d、e、f,这时G0超过了预算了。

前面我们说过,G0总是用来分配新对象,因此虽然这时G1和G2空间是足够的,但也不能用来分配给新对象;另外,前面我们也说过,每一代的垃圾回收只有在本代的       空间预算用完后才会启动垃圾回收。

因此,我们需要启动G0的垃圾回收,将c回收掉,将a、b幸存,并升级到G1中,经过第1次的垃圾回收后如下:

回收后的G0是空的,正好可以用来分配新进的d、e、f,如下所示:

过了一段时间后,f变成可达了,现在我们要分配新的对象g、h、i,发现G0被占满了,于是回收G0(注意这时不回收G1,因为G1的空间预算还没有用完),并将d和e加入G1,经过这次的回收后如下图所示:

回收后的G0又变空了,可以容纳新的对象,如下所示:

现在,我们要分配新的对象,j、k、l,这时发现G0和G1的空间预算都用完了,于是回收G0和G1,并将G1中幸存的对象升至G2,G0中幸存的对象升至G1,经过这次的回收后如下图所示:

回收过后的G0又变空了,可以容纳新的对象j、k、l。依此往复。如果没有回收到足够多的内存时,垃圾回收器就会执行一次完整的垃圾回收,如果还是不够,就抛出OutOfMemoryException异常。

三.程序局部性原理在代机制中的应用

前面,我们谈到了程序局部性原理,其中说对象越新生存期越短,具体到代机制中,G0总是存着最新的对象,因此G0中包含垃圾的可能性也就更大,能回收更多的内存,对于G0的回收也更频繁。

同理,对于G1的回收就没有那么频繁了,因为根据程序局部性原理,G1中的对象活得更长,在回收G0且G1的内存没有超出预算时,如果也去回收G1中的垃圾,有可能找不出多少垃圾,因此对G1的垃圾回收有可能是浪费时间。

也就是说G0回收时,如果G1没有超出预算,是不会回收G1的,如果G1有垃圾,它将留在G1当中,,这样就加快了回收的速度。

四.另一个提升性能的重要因素:不必遍历所有根

另一个提升性能的重要因素,在于我们不必遍历所有的根,如果根或对象引用了老一代的对象,垃圾回收器就可以忽略老对象内部的所有引用,能更快地构建对象可达图。

但问题来了,如果老对象在原来的程序中是不可达的,在G0引入新对象后,又引用了新对象呢?那如何更新老对象的引用呢?

垃圾回收器利用了JIT编译器内部的一个机制,在对象的引用字段发生变化时,会设置一个相应的位标志,这样垃圾回收器就知道自上一次垃圾回收以来,哪些老对象已被写入,只有字段发生变化的老对象才需要是否引用了G0中的任何新对象。

五.大对象

到目前为止,前面我们说的都是小对象,CLR认为超过85000字节的即是大对象。针对大对象:

  1. 大对象不在小对象的地址空间分配,而在进程地址空间的其他地方分配
  2. 目前,GC不压缩,因为在内存中移动它代价太高,但这可能造成大对象之间造成碎片化
  3. 大对象总是G2,所以只能为需要长时间存活的资源创建大对象,分配短时间存活的大对象会导致G2被频繁地回收,会损害性能。

六.内存预算的调节—自调节

垃圾回收器会在执行垃圾回收的过程中了解应用程序的行为,同时根据行为来动态调整G0、G1、G2的预算。

假如应用程序构建了相当多的短时间使用的对象,造成G0的垃圾回收会回收大量内存,就可能减少G0的预算,这样G0的回收将更加频繁,但垃圾回收器每次做的事情也就更少了,这减少了进程的工作集。如果G0中全是垃圾,则不需要经过压缩内存,直接让NextObjPrj指针指回G0的开始处即可。这样回收起来更快!

假如垃圾回收器在回收G0时发现还有很多对象存活,就会增大G0的预算,垃圾的回收次数将减少,但每次垃圾回收时,回收的内存就会多得多。

参考

《CLR via C#》(第4版)

基于代的垃圾回收机制--《CLR via C#》读书笔记的更多相关文章

  1. 垃圾回收算法简单介绍——JVM读书笔记<二>

    垃圾回收的过程主要包含两部分:找出已死去的对象.移除已死去的对象. 确定哪些对象存活有两种方式:引用计数算法.可达性分析算法. 方案一:引用计数算法 给对象中加入一个引用计数器.每当有一个地方引用它时 ...

  2. CLR via C# 读书笔记-21.托管堆和垃圾回收

    前言 近段时间工作需要用到了这块知识,遂加急补了一下基础,CLR中这一章节反复看了好多遍,得知一二,便记录下来,给自己做一个学习记录,也希望不对地方能够得到补充指点. 1,.托管代码和非托管代码的区别 ...

  3. [转载收藏]C#基础知识梳理系列十一:垃圾回收机制

    摘 要 基于.NET平台的开发语言中,最让开发人员爽的一点就是垃圾回收处理机制,在编码过程中,终于可以解放你的双手来关注更重要的事情.很多的资料中在讲到.NET中的垃圾回收机制时都说"CLR ...

  4. Python垃圾回收机制--完美讲解!

    转自: http://www.jianshu.com/p/1e375fb40506 先来个概述,第二部分的画述才是厉害的. Garbage collection(GC) 现在的高级语言如java,c# ...

  5. python垃圾回收机制(Garbage collection)

    由于面试中遇到了垃圾回收的问题,转载学习和总结这个问题. 在C/C++中采用用户自己管理维护内存的方式.自己管理内存极其自由,可以任意申请内存,但也为大量内存泄露.悬空指针等bug埋下隐患. 因此在现 ...

  6. 浅谈 JavaScript 垃圾回收机制

    github 获取更多资源 https://github.com/ChenMingK/WebKnowledges-Notes 在线阅读:https://www.kancloud.cn/chenmk/w ...

  7. Python的内存管理和垃圾回收机制

    内存管理 Python解释器由c语言开发完成,py中所有的操作最终都由底层的c语言来实现并完成,所以想要了解底层内存管理需要结合python源码来进行解释. 1. 两个重要的结构体 include/o ...

  8. 成为JavaGC专家(1)—深入浅出Java垃圾回收机制

    转载自:http://www.importnew.com/1993.html 对于Java开发人员来说,了解垃圾回收机制(GC)有哪些好处呢?首先可以满足作为一名软件工程师的求知欲,其次,深入了解GC ...

  9. 转:成为JavaGC专家Part I — 深入浅出Java垃圾回收机制

    文章来自于:http://www.importnew.com/1993.html 对于Java开发人员来说,了解垃圾回收机制(GC)有哪些好处呢?首先可以满足作为一名软件工程师的求知欲,其次,深入了解 ...

随机推荐

  1. Android项目实战(三十):Fresco加载gif图片并播放

    前言: 项目中图文混合使用的太多太多了,但是绝大部分都是静态图片. 然而项目开发中有这么一个需求:显示一个出一个简短的动画(一般都不超过3秒)演示 比如说:一个功能提供很多步骤来教用户做广播体操,那么 ...

  2. Visual Studio Code必备插件

    HTML Snippets: 超级实用且初级的 H5代码片段以及提示 HTMLHint: html代码检测 HTML CSS Support : 让 html 标签上写class 智能提示当前项目所支 ...

  3. 2018-10-18 22:15:32 c language

    2018-10-18 22:15:32 c language 在屏幕上输出各种类型的数据 我们使用 puts 来输出字符串.puts 是 output string 的缩写,只能用来输出字符串,不能输 ...

  4. QQ运动步数&自定义ProgressBar

    效果如下 gif图展示效果不好,实际体验无卡顿 1.自定义属性 早Values目录下New-values resource file,命名为attrs.xml(命名随意,但规范命名为attrs.xml ...

  5. JAVA将list转化为xml文件

    pojo类: public class TreeNode { private int id; private String nodeId; private String parentId; priva ...

  6. 用JS实现控制浏览器F12与右键功能

    本文出至:新太潮流网络博客 用JS实现控制浏览器F12与右键功能,防止恶意窃取代码,或其他直接复制进去就好 //禁用右键 document.oncontextmenu = function () { ...

  7. 在web中如何调整上传过的图片方向 (exif)

    前提: 相机中拍的照片放到web上不会自动识别方向,如有些竖向显示的照片放到web上横向显示.这些照片在windows上是正确显示的.但是web不会自动旋转照片到正确方向.下面我们通过两种方法来实现这 ...

  8. NPOI 导出Excel 数据方式

    使用NPOI的库进行Excel导出操作 公共帮助类: using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using System; using S ...

  9. teradata 数据定义

    teradata 数据定义 创建表的可选项 是否允许记录重复 set 不允许记录重复 multiset 允许记录重复 数据保护 fallback       fallback    使用fallbac ...

  10. python 多进程 Event的使用

    Event事件  多进程的使用 通俗点儿讲  就是 1.  Event().wait()    插入在进程中插入一个标记(flag)  默认为 false  然后flag为false时  程序会停止运 ...