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

一.程序局部性原理

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

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

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

二.代

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. Ajax 小实例

    1.urls.py url(r'^jiafa', views.jiafa), 2.views.py def jiafa(request): if request.method == "GET ...

  2. 使用ES6+Vue+webpack+gulp构建新一代Web应用

    1.推荐学习网站:Vue.js中国 2.Demo环境搭建: 2.1环境配置 安装nodejs环境,具体内容可以百度: 新建一个文件夹: mkdir VUE-ES6-WebPack 全局安装gulp: ...

  3. Python Django框架笔记(三):django工作方式简单说明和创建用户界面

    (一)  说明 简单说明下django的工作方式,并举2个例子. (二)  Django工作方式 假定我们有下面这些文件 ,这里在前2篇的基础上增加了 templates目录(存放html文件) 和s ...

  4. windows 服务器MYSQL 数据库安装配置

    一.到官网下载MYSQL 打开官网地址:www.mysql.com, 选择 DOWNLOADS,进入到MySQL的下载页面,在页面的底部有一个MySQL Community Edition, 并且下面 ...

  5. 从零自学Java-1.编写第一个Java程序

    编写第一个Java程序 完成工作:1.在文本编辑器中输入一个Java程序. 2.使用括号组织程序. 3.保存.编译和运行程序. package com.Jsample;//将程序的包名称命名为com. ...

  6. linux下安装jdk安装及环境变量配置

    1.默认是在windows下载,linux下安装 2.在jdk官网下载相应版本的jdk,这次下载为 jdk-8u161-linux-x64.tar.gz 3.将下载好的文件上传到指定目录,我这次把它放 ...

  7. c#中的数据类型简介(委托)

    什么是委托? 委托是一种类型,它封装了一类方法,这些方法具有相同的方法签名(signature)和返回类型.定义听起来有点拗口,首先可以确定委托是一种数据类型,那么什么是方法签名,其实就是指方法的输入 ...

  8. CSS| background_属性总结

    Property List 属性 描述 CSS background 在一个声明中设置所有的背景属性. 1 background-attachment 设置背景图像是否固定或者随着页面的其余部分滚动. ...

  9. MySQL索引设计不可忽视的知识点

    本文主要讨论MySQL索引的部分知识.将会从MySQL索引基础.索引优化实战和数据库索引背后的数据结构三部分相关内容,下面一一展开. 一.MySQL——索引基础 首先,我们将从索引基础开始介绍一下什么 ...

  10. Year 2038 problem (2038年问题)

    From Wikipedia, the free encyclopedia       Animation showing how the date would reset, represented ...