CLR via C# 读书笔记-21.托管堆和垃圾回收
前言
近段时间工作需要用到了这块知识,遂加急补了一下基础,CLR中这一章节反复看了好多遍,得知一二,便记录下来,给自己做一个学习记录,也希望不对地方能够得到补充指点。
1,.托管代码和非托管代码的区别
2.托管堆是什么?
3.托管堆基础,托管堆分配资源
4.内存泄漏 内存溢出、内存损坏
5.C# new操作符分配资源
6.垃圾回收算法
7.代:提升性能
8.垃圾回收触发条件
9.大对象
10.使用需要特殊清理的类型
11.使用包装了本机资源的类型
一、托管代码和非托管代码的区别
托管代码:执行过程交由运行时管理的代码。IL(中间语言)有时也成为托管代码。CLR负责提取托管代码,利用JIT编译为机器代码,从而执行它。(CLR提供 自动内存管理、安全边界、类型安全)
非托管代码:在CLR外部,由操作系统直接执行的代码。(按自己的想法提供垃圾回收、类型检查、安全)例如:COM 组件、ActiveX 接口和 Win32 API 函数,数据库连接
二、托管堆是什么?.
托管堆:CLR要求所有对象都从托管堆中分配。进程初始化时,CLR划出一个地址空间区域作为托管堆。
(CLR还要维护一个指针,我们称它作NextObjPtr。该指针指向下一个对象在堆中的分配位置)
三、托管堆基础,托管堆分配资源
在面向对象中,每个类型都代表可供程序提供的一种资源,要使用这些资源,必须为代表资源的这些类型分配内存
1.调用IL指令newobj,为代表资源的类型分配内存(一般使用C#new操作符来完成)
2.访问类型的成员来使用资源(有必要可以重复)
3.摧毁资源的状态以进行清理
4.释放内存。垃圾回收器独自负责这一步
四、内存泄漏 内存溢出、内存损坏
内存泄漏:申请了一块内存,但不需要时一直不去删除,导致这块内存一直被占用
内存溢出:程序在申请内存时,没有足够的内存空间供其使用,抛出OutOfMemoryException
内存损坏:访问被释放的内存
五、C#的new操作符导致CLR执行以下步骤
1.计算类型的字段(以及从基类型继承的字段)所需的字节数
2.加上对象开销所需的字节数。每个对象都有两个开销字段:类型对象指针和同步块索引。
32位应用程序,这两个字段各自需要32位,所以每个对象要增加8字节
64位应用程序,这两个字段各自需要64位,所以每个对象要增加16字节
3.CLR检查区域中是否有分配对象所需的字节数。如果托管堆有足够的可用空间,就在NextObjPtr指针指向的地址处放入对象,为对象分配的字节会被清零。接着调用类型的构造器(为this参数传递NextObjPtr),new操作符返回对象引用。就在返回这个引用之前,NextObjPtr指针的值会加上对象占用的字节数来得到一个新值,及下个对象放入托管堆时的地址
六、垃圾回收算法
1.引用计数算法。
Microsoft自己的“组件对象模型”(Component Object Model,COM),每个对象都维护着一个内存字段来统计程序中多少“部分”正在使用对象,随着每一“部分”到达代码中某个不再需要对象的地方,就递减对象的计数字段。计数对象变为0,对象就可以从内存中删除了。
许多引用技术系统最大的问题是处理不好循环引用。这种引用会阻止两个对象的计数器达到0,所以两个对象永远不会删除
2.引用跟踪算法
所有引用类型的变量都成为根。
CLR开始GC时,首先暂停进程中的所有线程,这样可以防止CLR检查期间访问对象并更改其状态。然后进入GC的标记阶段,将所有对象同步块索引的一位设为0,这表明所有对象都应被删除。任何根如果引用了堆上的对象,CLR就会将该对象的同步块索引中的位设为1,一个对象被标记后,CLR就会检查那个对象的根,标记他们的引用对象。如果发现对象已经标记,就不重新检查对象的字段。这样就避免了循环引用而产生死循环。
已标记的对象不能被垃圾回收。CLR知道哪些对象可以幸存,哪些对象可以删除后,会进入GC的压缩阶段,压缩所有幸存下来的对象,使它们占用连续的内存空间。减少了应用程序的工作集,从而提升将来访问这些对象时的性能。解决了本机堆空间碎片化的问题。
但是引用幸存对象的根现在引用的还是对象最初在内存中的位置,被暂停的线程恢复执行时,将访问旧的内存位置,会造成内存损坏,所以GC进入压缩阶段后,CLR还要从每个根减去所有引用的对象在内存中偏移的字节数。
压缩好内存后,NextObjPtr将指向最后一个幸存对象之后的位置,CLR恢复应用程序的所有线程。
注:静态字段引用的对象一直存在,直到用于加载类型的AppDomain卸载为止
有趣的三张图示留给读者思考:
图示一:
图示二:
图示三:
七、代:提升性能
CLR的GC是基于代的垃圾回收器,它对代码做了如下假设:
对象越新,生存期越短。
对象越老,生存期越长。
回收堆的一部分,速度快于回收整个堆。
托管堆在初始化时不包含对象。添加到堆的对象被称为第0代对象。CLR初始化时为第0代对象选择一个预存容量。如果分配一个新对象造成第0代超过预算,就必须启动一次垃圾回收。在一次垃圾回收后存活的第0代对象会成为第1代对象(事实上CLR还必须为第1代选择预算),第0代对象就不包含任何对象了。周而复始(第1代预算内存不满时不会自动释放内存),直到某一次,第0代和第1代预算内存都满了,第1代可能已经有许多对象变得不可达,这次垃圾回收器会检查第1代和第0代的所有对象,两代都被垃圾回收。和之前一样,第0代幸存者被提升为第1代,第1代幸存者被提升为第2代,第0代空出来了。
托管堆只支持三代:第0代、第1代和第2代。CLR初始化时,会为每次带选择预算。CLR的垃圾回收器是自调节的,会根据应用程序的内存负载自动优化。
八、垃圾回收触发条件
1.代码显示调用System.GC的静态Collect方法
2.Windows报告低内存情况
CLR使用Win32函数CreateMemoryResourceNotification和QueryMemoryResourceNotification监视系统的总体内存使用情况
3.CLR正在卸载AppDomain
CLR认为一切都不是根,执行涵盖所有代的垃圾回收
4.CLR正在关闭
整个进程都要终止了,Windows将回收进程的全部内存
九、大对象
CLR将对象分为大对象和小对象。
85000字节或更大的对象是大对象。
1.大对象不是在小对象的地址空间分配,而是在进程地址空间的其它地方分配
2.目前版本的CLR不压缩大对象,因为在内存中移动他们的代价过高
3.大对象总是第2代,绝不可能是第0代或第1代
十、使用需要特殊清理的类型
有的类型除了内存还需要本机资源(文件、网络连接、套接字、互斥体),GC回收托管堆中使用的内存,会造成本机资源的泄漏。
CLR提供了成为终结的机制,允许对象在被判定为垃圾之后,但在对象内存被回收之前执行一些代码。
终结基类System.Object定义了受保护的虚方法Finalize。垃圾回收器判定对象是垃圾后,会调用对象的Finalize方法(如果重写)
C#要求在类名前添加~符号来定义Finalize方法,如下图所示:
被视为垃圾的对象在垃圾回收完毕后才调用Finalize方法,这些对象的内存不是被马上回收的。
因为Finalize方法可能要执行访问字段的代码,可终结对象在回收时必须存活,造成被提升到另一代,使对象活的比正常时间长,增大了内存好用,所以应尽可能避免终结。
注:Finalize方法的执行时间是控制不了的。
十一、使用包装了本机资源的类型
如果想允许使用者控制类所包装的本机资源的生存期,就必须实现IDisposable接口。
如果类定义的一个字段的类型实现了dispose模式,那么类本身也实现了dispose模式。
”dispose一个对象”真正的意思是:清理或处置对象中包装的资源(比如它的字段引用的对象),然后等着在一次垃圾回收之后回收该对象占用的托管堆内存(此时才释放)
using语言编译器自动生成try和finally块,在finnally块中编译器生成代码将变量转型为一个IDisposable并调用dispose方法。所以using语句只能用于实现了IDisposable接口的类型。
一个清理资源的范例:
class People:IDisposable
{
//这是一个Finalize方法
~People()
{
Dispose(false);
} public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} private bool disposed = false;
public virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free any other managed objects here.
}
// Free any unmanaged objects here.
}
disposed = true;
}
}
参考:http://www.cnblogs.com/luminji/archive/2011/03/29/1997812.html
天道酬勤,大道至简,坚持。
CLR via C# 读书笔记-21.托管堆和垃圾回收的更多相关文章
- 【C#进阶系列】21 托管堆和垃圾回收
托管堆基础 一般创建一个对象就是通过调用IL指令newobj分配内存,然后初始化内存,也就是实例构造器时做这个事. 然后在使用完对象后,摧毁资源的状态以进行清理,然后由垃圾回收器来释放内存. 托管堆除 ...
- 【CLR】解析CLR的托管堆和垃圾回收
目录结构: contents structure [+] 为什么使用托管堆 从托管堆中分配资源 托管堆中的垃圾回收 垃圾回收算法 代 垃圾回收模式 垃圾回收触发条件 强制垃圾回收 监视内存 对包装了本 ...
- 重温CLR(十五) 托管堆和垃圾回收
本章要讨论托管应用程序如何构造新对象,托管堆如何控制这些对象的生存期,以及如何回收这些对象的内存.简单地说,本章要解释clr中的垃圾回收期是如何工作的,还要解释相关的性能问题.另外,本章讨论了如何设计 ...
- .NET 托管堆和垃圾回收
托管堆基础 简述:每个程序都要使用这样或那样的资源,包括文件.内存缓冲区.屏幕空间.网络连接.....事实上,在面向对象的环境中,每个类型都代表可供程序使用的一种资源.要使用这些资源,必须为代表 ...
- 托管堆和垃圾回收(GC)
一.基础 首先,为了深入了解垃圾回收(GC),我们要了解一些基础知识: CLR:Common Language Runtime,即公共语言运行时,是一个可由多种面向CLR的编程语言使用的"运 ...
- cir from c# 托管堆和垃圾回收
1,托管堆基础 调用IL的newobj 为资源分配内存 初始化内存,设置其初始状态并使资源可用.类型的实列构造器负责设置初始化状态 访问类型的成员来使用资源 摧毁状态进行清理 释放内存//垃圾回收期负 ...
- C#托管堆和垃圾回收
垃圾回收 值类型 每次使用都有对应新的线程栈 用完自动释放 引用类型 全局公用一个堆 因此需要垃圾回收 操作系统 内存是链式分配 CLR 内存连续分配(数组) 要求所有对象从 托管堆分配 GC 触发条 ...
- 如何管好.net的内存(托管堆和垃圾回收)
一:C#标准Dispose模式的实现 需要明确一下C#程序(或者说.NET)中的资源.简单的说来,C#中的每一个类型都代表一种资源,而资源又分为两类: 托管资源:由CLR管理分配和释放的资源,即由CL ...
- Clr Via C#读书笔记---垃圾回收机制
#1 垃圾回收平台的基本工作原理: 访问一个资源所需的具体步骤: 1)调用IL指令newobj,为代表资源的类型分配内存.在C#中使用new操作符,编译器就会自动生成该指令.2)初始化内存,设置资源的 ...
随机推荐
- WebSocket实践——Java实现WebSocket的两种方式
什么是 WebSocket? 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信 ...
- Windows Intel VT-x开启
解决虚拟机安装64位系统“此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态”的问题 背景:win7 旗舰版 64位+VMware 10.0 启动虚拟机时报错 问题:已将该虚拟机 ...
- 笔记-TCPCLIENT
]; private void ReceiveMessage() { try { tcpClient = );//创建TcpClient对象实例 } catch (Exception le) { } ...
- iOS学习之第二个View使用UITabBarViewController
前面有一篇博文iOS学习之Tab Bar的使用和视图切换 这是在AppDelegate里使用Tabbar,这样的程序打开就是TabbarView了,有时候我们需要给程序做一些帮助页面,或者登录页面,之 ...
- java使用POI进行Excel读写
1 jar包下载 参考链接:http://www.cnblogs.com/gmq/archive/0001/01/01/1521496.html poi-3.7-20101029.jar和源码下载地址 ...
- EF CodeFirst简单实例
运行环境:VS2012,添加的EntityFramework为6.0.2 版本不用太关心,只要知道原理就行了: 基本代码就这几行: namespace ConsoleApplication1 { ...
- 蒟蒻LQL的博客
这里是蒟蒻LQL的博客!!! 一枚水的不能再水的弱校ACMer···· 可能会在这写一些题解或者别的什么乱七八糟的··· 可能大概没什么人看,就当错题本好了o(* ̄▽ ̄*)ブ 因为太弱了难免有错误!发 ...
- 在子页面使用layer弹出层时只显示遮罩层,不显示弹出框问题
最近子页面使用layer弹出层时只显示遮罩层,不显示弹出框,这个问题搞了很久,最后才发现,在子页面上使用弹出框时,如果只使用layer.alert()或者layer.open()时,会默认在当前页面弹 ...
- 764. Largest Plus Sign最大的dfs十字架
[抄题]: 求挖掉一些区域后,能允许出现的最大十字架 In a 2D grid from (0, 0) to (N-1, N-1), every cell contains a 1, except t ...
- python中执行命令的3种方法小结-乾颐堂
目前我使用到的python中执行cmd的方式有三种: 1. 使用os.system("cmd") 特点是执行的时候程序会打出cmd在linux上执行的信息. import os o ...