深入了解C#系列:谈谈C#中垃圾回收与内存管理机制.doc_第1页
深入了解C#系列:谈谈C#中垃圾回收与内存管理机制.doc_第2页
深入了解C#系列:谈谈C#中垃圾回收与内存管理机制.doc_第3页
深入了解C#系列:谈谈C#中垃圾回收与内存管理机制.doc_第4页
深入了解C#系列:谈谈C#中垃圾回收与内存管理机制.doc_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

深入了解C#系列:谈谈C#中垃圾回收与内存管理机制doc文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。 C#系列 谈谈 C#中垃圾回收与 系列: C#中垃圾回收与 深入了解 C#系列: 内存管理机制 今天抽空来讨论一下.Net 的垃圾回收与内存管理机制,也算是完成上个WCF 分布式开发必备知识系列后的一次休息吧。以前被别人面试的时候问过我 GC 工作原理的问题,我现在面试新人的时候偶尔也会问相关的问题。那么你是否也 遇到这样的问题呢?比如你清楚.Net 的垃圾回收机制吗?你能简述一下 GC 的 工作原理吗?怎么样才能有效的管理内存呢?Using 语句体内实例化的对象有 什么作用?等等相关问题。下面我们就来详细讨论一下。相信你看完以后也可以 面试别人。 本节的组织如下,1.Net 的类型和内存分配 2.GC 垃圾收集器的工作原理 3.什么是非托管资源 4.如何有效释放对象资源。总结.现在开始我们本节的学习。 1.Net 的类型和内存分配 Net 中的所有类型都是(直接或间接)从 System.Object 类型派生的。 CTS 中的类型被分成两大类引用类型 (reference type, 又叫托管类型managed type) , 分配在内存堆上,值类型(value type)。值类型分配在堆栈上。如图 值类型在栈里,先进后出,值类型变量的生命有先后顺序,这个确保了值 类型变量在推出作用域以前会释放资源。比引用类型更简单和高效。堆栈是从高 地址往低地址分配内存。 引用类型分配在托管堆(Managed Heap)上,声明一个变量在栈上保存,当 使用 new 创建对象时,会把对象的地址存储在这个变量里。托管堆相反,从低地 址往高地址分配内存,如图 2.GC 垃圾收集器的工作原理 上图中,当 dataSet 使用过期以后,我们不显示销毁对象,堆上的对象还 继续存在,等待 GC 的 回收。 垃圾收集器通过分代支持对象的年龄化是推荐的但不是必需的。 一代在内存里是 一个具有相对年龄的对象的单位。对象的 代号或年龄标识对象属于那个分代。在应用程序的生命周期里,越近创建的对象 属于越新的代,并且比早创建的对象具有 较低的分代号。最近分代里的对象代号是 0. 在 new 对象时,要先搜索空闲链表,找到最适合内存块,分配,调整内存 块链表,合并碎片。new 操作几乎可以在 O(1)的时间完成,把 堆顶指针加 1。 工作原理是: 当托管堆上剩余空间不足,或者 Generator 0 的空间已满的时候 GC 运行,开始回收内存。垃圾回收的开始,GC 对堆内存的压缩调整,对象集中 到顶部。GC 在扫描垃圾的时候会占用一定的 CPU 时间片的, 最初的 GC 算法真 的是扫描整个堆,效率低。现在的 GC 把堆中的对象分成 3 代,最近进入堆的是 第 0 代(generation 0), 其次是 generation 1, generation2. 第一次 GC 只扫 描第 0 代。如果回收的空间足够当前使用就不必扫描其它 generation 的对象。 所以,GC 创建对象的效率比 C+高效,不需要扫描全部 堆空间。它通过扫描策 略,再加上内存管理策略带来的性能提升,足以补偿 GC 所占用的 CPU 时间。 3.什么是非托管资源 常见 的非托管资源就是包装操作系统资源的对象,例如文件,窗口或网络 连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期, 但它知道如何清 理这些资源。好在.net Framework 提供的 Finalize()方法,它 允许在垃圾回收器回收该类资源前,适当的清理非托管资源。这里列举几种常见 的非托管资源:画笔、流 对象、组件对象等等资源 (Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter ,ApplicationContext,Brush, Component,ComponentDesigner,Container,Context,Cursor,FileStream, Font,Icon,Image,Matrix,Timer,Tooltip)。(参考 MSDN) 4.如何有效释放非托管资源。 GC 无法管理非托管资源, 那么如何释放非托管资源呢?.Net 提供了两种方 式: (1)析构函数:垃圾收集器回收非托管对象的资源时,会调用对象的终结方法 Finalize(),进行资源的清理工作,但是由于 GC 工作规则的限制,GC 调用对象 的 Finalize 方法,第一次不会释放资源,第二次调用之后才删除对象。 (2)继承 IDisposable 接口,实现 Dispose()方法,IDisposable 接口定义了一 个模式(具有语言级的支持),为释放未托管的资源提供了确定的机制,并避免产 生析构函数固有的与垃圾收集器相关的问题。 为了更好的理解垃圾回收机制,我特地写了部分代码,里面添加了详细的注 释。定义单个类 FrankClassWithDispose(继承接口 IDisposable)、 FrankClassNoFinalize(没终结器)、FrankClassWithDestructor(定义了 析构函数)。 具体代码如下: 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Data; 5 using System.Data.Odbc; 6 using System.Drawing; 7 /Coded By Frank Xu Lei 18/2/2009 8 /Study the .NET Memory Management 9 /Garbage Collector 垃圾收集器。可以根据策略在需要的时候回收托管资 源, 10 /但是 GC 不知道如何管理非托管资源。如网络连接、数据库连接、画笔、 组件等 11 /两个机制来解决非托管资源的释放问题。析构函数、IDispose 接口 12 /COM 引用计数 13 /C+手动管理,New Delete 14 /VB 自动管理 15 namespace MemoryManagement 16 17 /继承接口 IDisposable,实现 Dispose 方法,可以释放 FrankClassDispose 的实例资源 18 public class FrankClassWithDispose : IDisposable 19 20 private OdbcConnection _odbcConnection = null; 21 22 /构造函数 23 public FrankClassWithDispose() 24 25 if (_odbcConnection = null) 26 _odbcConnection = new OdbcConnection(); 27 Console.WriteLine(FrankClassWithDispose has been creat ed 28 29 30 31 32 33 34 35 36 37 38 39 40 41 sed ); ); /测试方法 public void DoSomething() /code here to do something return ; /实现 Dispose,释放本类使用的资源 public void Dispose() if (_odbcConnection != null) _odbcConnection.Dispose(); Console.WriteLine(FrankClassWithDispose has been dispo 42 43 44 /没有实现 Finalize,等着 GC 回收 FrankClassFinalize 的实例资源, GC 运行时候直接回收 45 public class FrankClassNoFinalize 46 47 private OdbcConnection _odbcConnection = null; 48 /构造函数 49 public FrankClassNoFinalize() 50 51 if (_odbcConnection = null) 52 53 ed 54 55 56 57 58 59 60 61 62 63 64 65 66 67 下降 68 69 70 71 72 73 74 75 76 reated 77 78 79 80 81 82 83 84 85 86 87 88 ); _odbcConnection = new OdbcConnection(); Console.WriteLine(FrankClassNoFinalize has been creat /测试方法 public void DoSomething() /GC.Collect(); /code here to do something return ; /实现析构函数,编译为 Finalize 方法,调用对象的析构函数 /GC 运行时,两次调用,第一次没释放资源,第二次才释放 /FrankClassDestructor 的实例资源 /CLR 使用独立的线程来执行对象的 Finalize 方法,频繁调用会使性能 public class FrankClassWithDestructor private OdbcConnection _odbcConnection = null; /构造函数 public FrankClassWithDestructor() if (_odbcConnection = null) _odbcConnection = new OdbcConnection(); Console.WriteLine(FrankClassWithDestructor ); /测试方法 public void DoSomething() /code here to do something return ; /析构函数,释放未托管资源 FrankClassWithDestructor() if (_odbcConnection != null) has been c 89 90 isposed 91 92 93 94 ); _odbcConnection.Dispose(); Console.WriteLine(FrankClassWithDestructor has been d 其中使用了非托管的对象 OdbcConnection 的实例。建立的客户端进行了简单 的测试。客户端代码如下: 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Data; 5 using MemoryManagement; 6 /Coded By Frank Xu Lei 18/2/2009 7 /Study the .NET Memory Management 8 /Test The Unmanaged Objects Reclaimed. 9 /针对非托管代码的测试,比较 10 /托管代码,GC 可以更具策略自己回收,也可以实现 IDisposable,调用 Dispose()方法,主动释放。 11 namespace MemoryManagementClient 12 13 class Program 14 15 static void Main(string args) 16 17 18 /(1)/ / 19 /调用 Dispose()方法,主动释放。资源,灵活 20 FrankClassWithDispose _frankClassWithDispose = null; 21 try 22 23 _frankClassWithDispose = new FrankClassWithDispose( ); 24 _frankClassWithDispose.DoSomething(); 25 26 27 28 29 30 31 经被释放 32 33 34 ); finally if (_frankClassWithDispose!=null) _frankClassWithDispose.Dispose(); /Console.WriteLine(FrankClassWithDispose 实例已 /(2)/ / 35 /可以使用 Using 语句创建非托管对象,方法执行结束前,会 调用 36 using (FrankClassWithDispose _frankClassWithDispose2 = new FrankClassWithDispose() 37 38 /_frankClassWithDispose2.DoSomething(); 39 40 41 /(3)/ / 42 /垃圾收集器运行的时候,一次就释放资源 43 FrankClassNoFinalize _frankClassNoFinalize = new FrankC lassNoFinalize(); 44 _frankClassNoFinalize.DoSomething(); 45 46 /(4)/ / 47 /垃圾收集器运行的时候,两次才能够释放资源 48 FrankClassWithDestructor _frankClassWithDestructor = ne w FrankClassWithDestructor(); 49 _frankClassWithDestructor.DoSomething(); 50 /(5)/ / 51 /不能使用 Using 语句来创建对象,因为其没实现 IDispose 接口 52 /using (FrankClassWithDestructor _frankClassWithDestru ctor2 = new FrankClassWithDestructor() 53 / 54 / _frankClassWithDestructor2.DoSomething(); 55 / 56 57 / / 58 /For Debug 59 60 61 62 63 64 65 66 Console.WriteLine(Press any key to continue Console.ReadLine(); ); 有些时候资源必须在特定时间释放,类可以实现执行资源管理和清除任务方法 IDisposable.Dispose 的接口 IDisposable。 如果调用者需要调用 Dispose 方法清理对象,类作为契约的一部分必须实现 Dispose 方法。垃圾收集器默认情况下不会调用 Dispose 方法;然而,实现 Dispose 方法可以调用 GC 里的方法去规范垃圾收 器的终结行为。 值得一提的是:调用 Dispose()方法,主动释放资源,灵活,可以使用 Using 语句创建非托管对象,方法执行结束前,会调用 Dispose()方法释放资源,这两端代码的效果是一样的,可以查看编译后 IL。 1 .try 2 3 IL_0003: nop 4 IL_0004: newobj instance void MemoryManagementMemoryMana gement.FrankClassWithDispose:.ctor() 5 IL_0009: stloc.0 6 IL_000a: ldloc.0 7 IL_000b: callvirt instance void MemoryManagementMemoryMana gement.FrankClassWithDispose:DoSomething() 8 IL_0010: nop 9 10 11 12 13 14 15 16 17 18 19 20 21 22 IL_0011: nop IL_0012: leave.s / end .try finally IL_0014: nop IL_0015: ldloc.0 IL_0016: ldnull IL_0017: ceq IL_0019: stloc.s IL_001b: ldloc.s IL_001d: brtrue.s IL_001f: ldloc.0 IL_0028 CS$4$0000 CS$4$0000 IL_0026 IL_0020: callvirt instance void MemoryManagementMemoryMana gement.FrankClassWithDispose:Dispose() 23 IL_0025: nop 24 IL_0026: nop 25 IL_0027: endfinally 26 / end handler 27 IL_0028: nop 28 IL_0029: newobj instance void MemoryManagementMemoryManage ment.FrankClassWithDispose:.ctor() 29 IL_002e: stloc.1 30 .try 31 32 IL_002f: nop 33 IL_0030: nop 34 IL_0031: leave.s IL_0045 35 / end .try 36 finally 37 38 IL_0033: ldloc.1 39 IL_0034: ldnull 40 IL_0035: ceq 41 IL_0037: stloc.s CS$4$0000 42 IL_0039: ldloc.s CS$4$0000 43 IL_003b: brtrue.s IL_0044 44 IL_003d: ldloc.1 45 IL_003e: callvirt instance void mscorlibSystem.IDisposable :Dispose() 46 IL_0043: nop 47 48 49 IL_0044: endfinally / end handler Using 语句有同样的效果, 来实现非托管对象资源的释放。 这点在面试中也会经常遇到, Using 关键字的用法有哪几种等等类似的问题。基本理想的答案都是除了引用 命名空间,和命名 空间设置别名外,就是这个用法实现如 try finally 块一样作用的对非托管对象资源的回收。 只是一种简便的写法。 当你用 Dispose 方法释放未托管对象的时候,应该调用 GC.SuppressFinalize。如果对象 正在终结队列(finalization queue),GC.SuppressFinalize 会阻止 GC 调用 Finalize 方法。 因为 Finalize 方法的调用会牺牲部分性能。 如果你的 Dispose 方法已经对委托管资源作了 清理,就没必要让 GC 再调用对象的 Finalize 方法(MSDN)。附上 MSDN 的代码,大家可 以参考. public class BaseResource: IDisposable / 指向外部非托管资源 private IntPtr handle; / 此类使用的其它托管资源. private Component Components; / 跟踪是否调用.Dispose 方法,标识位,控制垃圾收集器的行为 private bool disposed = false; / 构造函数 publicBaseResource() / Insert appropriate constructor code here. / 实现接口 IDisposable. / 不能声明为虚方法 virtual. / 子类不能重写这个方法. public void Dispose() Dispose(true); / 离开终结队列 Finalization queue / 设置对象的阻止终结器代码 / GC.SuppressFinalize(this); / Dispose(bool disposing) 执行分两种不同的情况. / 如果 disposing 等于 true, 方法已经被调用 / 或者间接被用户代码调用. 托管和非托管的代码都能被释放 / 如果 disposing 等于 false, 方法已经被终结器 finalizer 从内部调用过, /你就不能在引用其他对象,只有非托管资源可以被释放。 protected virtual void Dispose(bool disposing) / 检查 Dispose 是否被调用过. if(!this.disposed) / 如果等于 true, 释放所有托管和非托管资源 if(disposing) / 释放托管资源. Components.Dispose(); / 释放非托管资源,如果 disposing 为 false, / 只会执行下面的代码. CloseHandle(handle); handle = IntPtr.Zero; / 注意这里是非线程安全的. / 在托管资源释放以后可以启动其它线程销毁对象, / 但是在 disposed 标记设置为 true 前 / 如果线程安全是必须的,客户端必须实现。 disposed = true; / 使用 interop 调用方法 / 清除非托管资源. System.Runtime.Intero

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论