.NET垃圾回收器(GC)原理浅析
作者:junjie 发布时间:2023-11-24 13:50:56
作为.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垃圾回收器的基本工作原理是:通过最基本的标记清除原理,清除不可达对象;再像磁盘碎片整理一样压缩、整理可用内存;最后通过分代算法实现性能最优化。


猜你喜欢
- 初次使用IDEA,创建一个maven工程,发现在目录结构中产生了两个不一样的东西——.iml文件和.idea文件夹。非常好奇,所以立刻上网查
- 一、cancel()无效当协程任务被取消的时候,它的内部是会产生一个 CancellationException 的。而协程的结构化并发,最
- 前章知识: 点此跳转HTML简介:超文本是一种组织信息的方式,它通过超级链接方法将文本中的文字、图表与其他信息媒体相关联。这些相互关联的信息
- 什么是AOPAOP是 Aspect Oriented Programming 的缩写,即面向切面编程,和平常遇到的面向对象OOP编程不一样的
- 最近帮朋友做了一个动画菜单,感觉有一定的实用价值,就在此给大家分享一下,先看看效果:实现思路:从图中可以看出,这三个(或更多,需要自己再实现
- 不知道有人做没做过对日外包,如果做过的话,那么对vb.net应该非常熟悉了,当年我刚毕业的时候也做过四个月的外包,那种日子简直不是人过的,就
- 记得上学的时候学习英语,每个英语老师说到英语翻译的时候都会说英语翻译要做到“信、达、雅”。如今做了一名程序员竟然体会我还是想用这三种境界来要
- 场景:有一个喜欢吃饺子,他有三种不同的方式去吃,蒸饺子,煮饺子,煎饺子,想要用策略模式来设计这个场景,怎么弄?1.复习简单工厂模式具体的代码
- 在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户
- 前言废话不多说直接开始老规矩,文章最后有源码完成效果图棋子加渐变色棋子不加渐变色一、测量1.获取宽高 @Override protected
- 本文实例讲述了Java实现对两个List快速去重并排序操作。分享给大家供大家参考,具体如下:1:去重并排序package twolist;i
- 1.Jquery验证1)引入头文件<script src="../../Scripts/jquery-1.7.1.js&qu
- 尽管Java提供了一个可以处理文件的IO操作类。 但是没有一个复制文件的方法。 复制文件是一个重要的操作,当你的程序必须处理很多文件相关的时
- 有一天,你写了好多好多带“形参”的构造函数(就是“方法”,同义),而且需要向这些构造函数里传递同样的“实参”,然后你就憨憨地一个一个函数的调
- @Valid注解可以实现数据的验证,你可以定义实体,在实体的属性上添加校验规则,而在API接收数据时添加@valid关键字,这时你的实体将会
- 本文实例讲述了Java基于外观模式实现美食天下食谱功能。分享给大家供大家参考,具体如下:一、模式定义外观模式,是软件工程师常用的一种软件设计
- 最近一个项目中,需要用到Java的websocket新特性,于是就学了一下,感觉这技术还挺好玩的,瞬间知道网页上面的那些在线客服是怎么做的了
- 首先,ListView不能直接用,要自定义一个,然后重写onMeasure()方法:@Override protected vo
- redisson的几大特性相信看了这个标题的同学,对这个问题以已经非常不陌生了,信手拈来redisson的几大特性:可重入性【多个业务线同一
- 汉诺塔的规则是:一共三根柱子,一根柱子从上到