.NET垃圾回收器(GC)原理浅析

作为.NET进阶内容的一部分,垃圾回收器(简称GC)是必须了解的内容。本着“通俗易懂”的原则,本文将解释CLR中垃圾回收器的工作原理。

基础知识

托管堆(Managed Heap)

先来看MSDN的解释:初始化新进程时,运行时会为进程保留一个连续的地址空间区域。这个保留的地址空间被称为托管堆。

“托管堆也是堆”,为什么这样说呢?这么说是希望大家不要被“术语”迷惑,这个知识点的前提是“值类型和引用类型的区别”。这里假设读者已经知道“值类型存储在栈中,引用类型存储在堆中。(引用类型的引用存储在栈中)”这一重要概念。所以,根据这个理论,除值类型外,CLR要求所有资源都从托管堆分配。

托管堆维护着一个指针,这里命名为NextObjPtr,它指向下一个对象在堆中的分配位置。

CPU寄存器(CPU Register)

这个是计算机基础知识,这里复习一下,有助于对下面“根”概念的理解。

CPU寄存器是CPU自己的”临时存储器”,比内存的存取还快。按与CPU远近来分,离得最近的是寄存器,然后缓存(计算机一、二、三级缓存),最后内存。

根(Roots)

类中定义的任何静态字段,方法的参数,局部变量(仅限引用类型变量)等都是根,另外cpu寄存器中的对象指针也是根。根是CLR在堆之外可以找到的各种入口点。

对象可达与不可达(Objects reachable and unreachable)

如果一个根引用了堆中的一个对象,则该对象为“可达”,否则即是“不可达”。

垃圾回收的原因

从计算机组成的角度来讲,所有的程序都是要驻留在内存中运行的。而内存是一个限制因素(大小)。除此之外,托管堆也有大小限制。如果托管堆没有大小限制,那C#的执行速度要优于c了(托管堆的结构让它有比c运行时堆更快的对象分配速度)。因为地址空间和存储的限制因素,托管堆要通过垃圾回收机制,来维持它的正常运作,保证对象的分配,不会“内存溢出”。

垃圾回收的基本原理

回收分为两个阶段:  标记 –> 压缩

标记的过程,其实就是判断对象是否可达的过程。当所有的根都检查完毕后,堆中将包含可达(已标记)与不可达(未标记)对象。

标记完成后,进入压缩阶段。在这个阶段中,垃圾回收器线性的遍历堆,以寻找不可达对象的连续内存块。并把可达对象移动到这里以压缩堆。这个过程有点类似于磁盘空间的碎片整理。

如上图所示,绿色框表示可达对象,黄色框为不可达对象。不可达对象清除后,移动可达对象实现内存压缩(变得更紧凑)。

压缩之后,“指向这些对象的指针”的变量和CPU寄存器现在都会失效,垃圾回收器必须重新访问所有根,并修改它们来指向对象的新内存位置。这会造成显著的性能损失。这个损失也是托管堆的主要缺点。

基于以上特点,垃圾回收引发的回收算法也是一项研究课题。因为如果真等到托管堆满才开始执行垃圾回收,那就真的太“慢”了。

垃圾回收算法 – 分代(Generation)算法

代是CLR垃圾回收器采用的一种机制,它唯一的目的就是提升应用程序的性能。分代回收,速度显然快于回收整个堆。

CLR托管堆支持3代:第0代,第1代,第2代。第0代的空间约为256KB,第1代约为2M,第2代约为10M。新构造的对象会被分配到第0代。

如上图所示,当第0代的空间满时,垃圾回收器启动回收,不可达对象(上图C、E)会被回收,存活的对象被归为第1代。

当第0代空间已满,第1代也开始有很多不可达对象以至空间将满时,这时两代垃圾都将被回收。存活下来的对象(可达对象),第0代升为第1代,第1代升为第2代。

实际CLR的代回收机制更加“智能”,如果新创建的对象生存周期很短,第0代垃圾也会立刻被垃圾回收器回收(不用等空间分配满)。另外,如果回收了第0代,发现还有很多对象“可达”,

并没有释放多少内存,就会增大第0代的预算至512KB,回收效果就会转变为:垃圾回收的次数将减少,但每次都会回收大量的内存。如果还没有释放多少内存,垃圾回收器将执行

完全回收(3代),如果还是不够,则会抛出“内存溢出”异常。

也就是说,垃圾回收器会根据回收内存的大小,动态的调整每一代的分配空间预算!达到自动优化!

总结

垃圾回收背后有这样一个基本的观念:编程语言(大多数的)似乎总能访问无限的内存。而开发者可以一直分配、分配再分配——像魔法一样,取之不尽用之不竭。

.NET垃圾回收器的基本工作原理是:通过最基本的标记清除原理,清除不可达对象;再像磁盘碎片整理一样压缩、整理可用内存;最后通过分代算法实现性能最优化。

(0)

相关推荐

  • 详谈.net中的垃圾回收机制

    1. 自动内存管理和GC 在原始程序中堆的内存分配是这样的:找到第一个有足够空间的内存地址(没被占用的),然后将该内存分配.当程序不再需要此内存中的信息时程序员需要手动将此内存释放.堆的内存是公用的,也就是说所有进程都有可能覆盖另一进程的内存内容,这就是为什么很多设计不当的程序甚至会让操作系统本身都down掉.我们有时碰到的程序莫名其妙的死掉了(随机现象),也是因为内存管理不当引起的(可能由于本身程序的内存问题或是外来程序造成的).另一个常见的实例就是大家经常看到的游戏的Trainer,他们通过

  • .NET微信公众号 用户分组管理

    本文实例为大家分享了.NET微信用户分组管理代码,供大家参考,具体内容如下 Model层实体类: public class UserList { public string total { get; set; } public string count { get; set; } public userlistopenid data { get; set; } public string next_openid { get; set; } } public class userlistopeni

  • .NET微信公众号查看关注者接口

    本文实例为大家分享了java获取不同路径的方法,供大家参考,具体内容如下 实体类: public class userlist { public string total { get; set; } public string count { get; set; } public userlistopenid data { get; set; } public string next_openid { get; set; } } public class userlistopenid { pub

  • .Net 垃圾回收机制详细介绍

    析构函数 析构函数不能有修饰符,如public.不能接受任何参数. 编译器自动将一个析构函数转换成对Object.Finalize方法的一个override版,如下. class Test { protected override void Finalize() { try {-} finally { base.Finalize(); } } } 垃圾回收器 .NET垃圾回收器会保证: l  每个对象都会被摧毁,它的析构函数一定会被运行.当一个程序结束后,所有对象都会被销毁. l  每个对象只被

  • ASP.NET访问共享文件夹的详细步骤

    假设找找看的ASP.NET程序在A服务器,索引文件在B服务器的ZzkIndex共享文件夹中,访问地址是\\192.168.18.18\ZzkIndex\.要实现就是在A服务器的ASP.NET程序中能读写共享文件夹\\192.168.18.18\ZzkIndex\中的文件. 具体操作步骤: (注:A服务器为ASP.NET程序所在服务器,B服务器为共享文件夹所在服务器) ①在两台服务器上建立相同用户名.相同密码的Windows帐户(A与B服务器都要建),比如假设这里用户名是ZzkIndexer,密码

  • asp.net保存网上图片到服务器的实例

    本文讲述的是根据一个图片的url地址,保存图片到asp.net服务器端的实现方法. 建立GetImage.aspx页面,代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetImage.aspx.cs" Inherits="KeleyiTestWeb.KImage.GetImage" %> <!DOCTYPE html PUBLI

  • ASP.NET中制作各种3D图表的方法

    前言 大家都知道通过图表控件,我们即能表示数据又能比较各种图表的数据,例如比较去年和今年的收入.图表的类型也有很多,如柱状图.折线图.条形图.组合图等等. 首先,我将展示如何绘制出一个简单的图表. 简单图表的步骤 步骤1 新建一个"ASP.NET Empty Web Site". 步骤2 在Solution Explorer 中添加新项目,添加new form. 步骤3 工具栏-标准-项目.我们会看到在.Net Framework Component.中有两个图表选项,打勾并保存..N

  • 详述ASP.Net中页面之间传参方法

    ASP.NET提供了卓越的事件驱动编程模型,让开发者简化了应用程序的总体设计,但是这个也造成了它固有的一些问题,例如,使用传统的ASP里,我们可以通过使用POST方法很容易地实现页面间传递值,同样的事情,在使用事件驱动编程模型的ASP.NET就不是那么容易了,当然了,我们仍然有一些方法可以实现同样的功能. 本文将试着使用不同的可能的方法来解决这个问题,但可以预见是,本文将包含使用querystring,session变量以及server.Transfer方法来实现页面间的值传递. 使用Query

  • .NET微信公众号客服接口

    本文实例为大家分享了微信公众号客服接口.NET代码,供大家参考,具体内容如下 Kf_account.cs代码: public partial class Kf_account : Form { private readonly DataTable adt_user = new DataTable(); private readonly string as_INIFile = Application.StartupPath + "\\user.ini"; public Kf_accoun

  • .NET垃圾回收器(GC)原理浅析

    作为.NET进阶内容的一部分,垃圾回收器(简称GC)是必须了解的内容.本着"通俗易懂"的原则,本文将解释CLR中垃圾回收器的工作原理. 基础知识 托管堆(Managed Heap) 先来看MSDN的解释:初始化新进程时,运行时会为进程保留一个连续的地址空间区域.这个保留的地址空间被称为托管堆. "托管堆也是堆",为什么这样说呢?这么说是希望大家不要被"术语"迷惑,这个知识点的前提是"值类型和引用类型的区别".这里假设读者已经知

  • Java中垃圾回收器GC对吞吐量的影响测试

    在看内存管理术语表的时候偶然发现了"Pig in the Python(注:有点像中文里的贪心不足蛇吞象)"的定义,于是便有了这篇文章.表面上看,这个术语说的是GC不停地将大对象从一个分代提升到另一个分代的情景.这么做就好比巨蟒整个吞食掉它的猎物,以至于它在消化的时候都没办法移动了. 在接下来的这24个小时里我的头脑中充斥着这个令人窒息的巨蟒的画面,挥之不去.正如精神病医生所说的,消除恐惧最好的方法就是说出来.于是便有了这篇文章.不过接下的故事我们要讲的不是蟒蛇,而是GC的调优.我对天

  • 垃圾回收器的相关知识点总结

    垃圾回收器是一把十足的双刃剑.其好处是可以大幅简化程序的内存管理代码,因为内存管理无需程序员来操作,由此也减少了(但没有根除)长时间运转的程序的内存泄漏.对于某些程序员来说,它甚至能够提升代码的性能. 另一方面,选择垃圾回收器也就意味着程序当中无法完全掌控内存,而这正是移动终端开发的症结.对于JavaScript,程序中没有任何内存管理的可能--ECMAScript标准中没有暴露任何垃圾回收器的接口.网页应用既没有办法管理内存,也没办法给垃圾回收器进行提示. 严格来讲,使用垃圾回收器的语言在性能

  • 简单了解Java垃圾回收器的种类

    在这篇教程中我们将学习几种现有的垃圾回收器.在Java中,垃圾回收是一个自动的进程可以替代程序员进行内存的分配与回收这些复杂的工作.这篇是垃圾回 收教程系列的第三篇,在前面的第2部分我们看到了在Java中垃圾回收是如何工作的,那是篇有意思的文章,我推荐你去看一下.第一部分介绍了Java的垃圾回收,主要有JVM体系结构,堆内存模型和一些Java术语. Java有四种类型的垃圾回收器: 串行垃圾回收器(Serial Garbage Collector) 并行垃圾回收器(Parallel Garbag

  • 浅析Java中的GC垃圾回收器的意义及与GC的交互

    对象是使用new创建的,但是并没有与之相对应的delete操作来回收对象占用的内存.当我们完成对某个对象的使用时,只需停止对该对象的引用:将我们的引用改变为指向其他对象或指向null;或者从方法中返回,使得该方法的局部变量不复存在,从而使得对这些局部变量的引用变为不指向任何对象.不再被引用的对象被称为垃圾(garbage),查找并回收这些对象的过程叫做垃圾回收(garbage collection) o Java虚拟机利用垃圾回收来保证被引用的对象将会在内存中保留,同时会释放在执行代码中通过任何

  • 浅谈关于Java的GC垃圾回收器的一些基本概念

    一.基本回收算法 1. 引用计数(Reference Counting) 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的对象.此算法最致命的是无法处理循环引用的问题. 2. 标记-清除(Mark-Sweep) 此算法执行分两阶段.第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除.此算法需要暂停整个应用,同时,会产生内存碎片. 3. 复制(Copying) 此算法把内存空间划为两个相等的区域

  • 浅析JVM的垃圾回收器

    JVM的GC经过多年的发展,大家对Minor GC.major GC的理解并不完全一致,所以我不打算在本文中使用这个概念.我把GC大概分为一下4类: Young GC:只是负责回收年轻代对象的GC: Old GC:只是负责回收老年代对象的GC: Full GC:回收整个堆的对象,包括年轻代.老年代.持久带: Mixed GC:回收年轻代和部分老年代的GC (G1): 因为笔者目前使用G1还是比较少的,所以本文不打算将G1. 垃圾回收器算法 目前主流垃圾回收器都采用的是可达性分析算法来判断对象是否

  • .NET垃圾回收器原理及使用

    .NET 应用程序中的垃圾回收器是什么? 垃圾收集器只不过是 CLR 提供的一个功能,可帮助我们清理或销毁未使用的托管对象.通过清理或销毁这些未使用的托管对象,它基本上回收内存. 当DotNet应用程序运行时,它会创建多个对象,并且在给定时刻,应用程序可能不使用其中一些对象. 因此,对于这些对象,垃圾回收器作为后台线程连续运行,并在特定的时间间隔时间,它会检查是否有任何未使用的托管对象,以及它是否发现它只是清理这些对象并回收内存. 注:垃圾回收器将仅销毁未使用的托管对象.它不清理非托管对象. .

  • Java垃圾回收器的方法和原理总结

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

  • python垃圾回收机制(GC)原理解析

    这篇文章主要介绍了python垃圾回收机制(GC)原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 今天想跟大家分享的是关于python的垃圾回收机制,虽然本人这会对该机制没有很深入的了解, 但是本着热爱分享的原则,还是囫囵吞枣地坐下记录分享吧, 万一分享的过程中开窍了呢.哈哈哈. 首先还是做一下概述吧: 我们都知道, 在做python的语言编程中, 相较于java, c++, 我们似乎很少去考虑到去做垃圾回收,内存释放的工作, 其实是p

随机推荐