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

下载本文档

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

文档简介

1、深入了解C#系列:谈谈C#中垃圾回收与内存管理机制今天抽空来讨论一下.Net的垃圾回收与内存管理机制,也算是完成上个WCF分布式开发必备知识系列后的一次休息吧。以前被别人面试的时候问过我GC工作原理的问题,我现在面试新人的时候偶尔也会问相关的问题。那么你是否也遇到这样的问题呢?比如你清楚.Net的垃圾回收机制吗?你能简述一下GC的工作原理吗?怎么样才能有效的管理内存呢?Using语句体内实例化的对象有什么作用?等等相关问题。下面我们就来详细讨论一下。相信你看完以后也可以面试别人。     1.Net的类型和内存分配    &

2、#160;Net中的所有类型都是(直接或间接)从System.Object类型派生的。    CTS中的类型被分成两大类引用类型(reference type,又叫托管类型managed type),分配在内存堆上,值类型(value type)。值类型分配在堆栈上。如图         值类型在栈里,先进后出,值类型变量的生命有先后顺序,这个确保了值类型变量在推出作用域以前会释放资源。比引用类型更简单和高效。堆栈是从高地址往低地址分配内存。     引用

3、类型分配在托管堆(Managed Heap)上,声明一个变量在栈上保存,当使用new创建对象时,会把对象的地址存储在这个变量里。托管堆相反,从低地址往高地址分配内存,如图      2.GC垃圾收集器的工作原理      上图中,当dataSet使用过期以后,我们不显示销毁对象,堆上的对象还继续存在,等待GC的 回收。垃圾收集器通过分代支持对象的年龄化是推荐的但不是必需的。一代在内存里是一个具有相对年龄的对象的单位。对象的代号或年龄标识对象属于那个分代。在应用程序的生命周期里,越近创建的对象属于

4、越新的代,并且比早创建的对象具有较低的分代号。最近分代里的对象代号是0.      在new对象时,要先搜索空闲链表,找到最适合内存块,分配,调整内存块链表,合并碎片。new操作几乎可以在O(1)的时间完成,把 堆顶指针加1。工作原理是: 当托管堆上剩余空间不足,或者Generator 0 的空间已满的时候GC运行,开始回收内存。垃圾回收的开始,GC对堆内存的压缩调整,对象集中到顶部。GC在扫描垃圾的时候会占用一定的CPU时间片的, 最初的GC算法真的是扫描整个堆,效率低。现在的GC把堆中的对象分成3代,最近进入堆的是第 0

5、代(generation 0), 其次是generation 1, generation2. 第一次GC只扫描第0代。如果回收的空间足够当前使用就不必扫描其它generation的对象。所以,GC创建对象的效率比C+高效,不需要扫描全部 堆空间。它通过扫描策略,再加上内存管理策略带来的性能提升,足以补偿GC所占用的CPU时间。    3.什么是非托管资源常见 的非托管资源就是包装操作系统资源的对象,例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它知道如何清 理这些资源。好在.net Framework提供的Finali

6、ze()方法,它允许在垃圾回收器回收该类资源前,适当的清理非托管资源。这里列举几种常见的非托管资源:画笔、流 对象、组件对象等等资源 (Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Timer,Tooltip)。(参考MSDN)    4.如何有效释放非托

7、管资源。     GC无法管理非托管资源,那么如何释放非托管资源呢?.Net提供了两种方式:(1)析构函数:垃圾收集器回收非托管对象的资源时,会调用对象的终结方法Finalize(),进行资源的清理工作,但是由于GC工作规则的限制,GC调用对象的Finalize方法,第一次不会释放资源,第二次调用之后才删除对象。(2)继承IDisposable接口,实现Dispose()方法,IDisposable接口定义了一个模式(具有语言级的支持),为释放未托管的资源提供了确定的机制,并避免产生析构函数固有的与垃圾收集器相关的问题。   

8、;为了更好的理解垃圾回收机制,我特地写了部分代码,里面添加了详细的注释。定义单个类FrankClassWithDispose(继承接口IDisposable)、FrankClassNoFinalize(没终结器)、FrankClassWithDestructor(定义了析构函数)。具体代码如下:-1using System; 2using 3using System.Text; 4using System.Data; 5using 6using System.Drawing; 7/Coded&#

9、160;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 Delete14/VB自动管理15namespace&

10、#160;MemoryManagement1617    /继承接口IDisposable,实现Dispose方法,可以释放FrankClassDispose的实例资源18    public class FrankClassWithDispose : IDisposable19    20        private OdbcConnectio

11、n _odbcConnection = null;21        22        /构造函数23        public FrankClassWithDispose()24        25   

12、         if (_odbcConnection = null)26                _odbcConnection = new OdbcConnection();27        &#

13、160;   Console.WriteLine("FrankClassWithDispose has been created ");28        29        /测试方法30        public void DoSomethin

14、g()31        3233            /*/code here to do something34            return 35      

15、;  36        /实现Dispose,释放本类使用的资源37        public void Dispose()38        39            if (_od

16、bcConnection != null)40                _odbcConnection.Dispose();41            Console.WriteLine("FrankClassWithDispose has been d

17、isposed");42        43    44    /没有实现Finalize,等着GC回收FrankClassFinalize的实例资源,GC运行时候直接回收45    public class FrankClassNoFinalize46    47     &#

18、160;  private OdbcConnection _odbcConnection = null;48        /构造函数49        public FrankClassNoFinalize()50        51    

19、60;       if (_odbcConnection = null)52                _odbcConnection = new OdbcConnection();53          

20、;  Console.WriteLine("FrankClassNoFinalize  has been created");54        55        /测试方法56        public void DoSomething()57 

21、;       5859            /GC.Collect();60            /*/code here to do something61       &

22、#160;    return 62        63    64    /实现析构函数,编译为Finalize方法,调用对象的析构函数65    /GC运行时,两次调用,第一次没释放资源,第二次才释放66    /FrankClassDestructor的实例资源67   &

23、#160;/CLR使用独立的线程来执行对象的Finalize方法,频繁调用会使性能下降68    public class FrankClassWithDestructor69    70        private OdbcConnection _odbcConnection = null;71      

24、0; /构造函数72        public FrankClassWithDestructor()73        74            if (_odbcConnection = null)75     &

25、#160;          _odbcConnection = new OdbcConnection();76            Console.WriteLine("FrankClassWithDestructor  has been created");77  

26、;      78        /测试方法79        public void DoSomething()80        81            

27、;/*/code here to do something8283            return 84        85        /析构函数,释放未托管资源86        

28、FrankClassWithDestructor()87        88            if (_odbcConnection != null)89                _odbcConn

29、ection.Dispose();90            Console.WriteLine("FrankClassWithDestructor  has been disposed");91        92    9394其中使用了非托管的对象OdbcConnection的实例。建立的客户

30、端进行了简单的测试。客户端代码如下:-1using System; 2using 3using System.Text; 4using System.Data; 5using MemoryManagement; 6/Coded By Frank Xu Lei 18/2/2009 7/Study the .NET Memory Management 8/Test The Unma

31、naged Objects Reclaimed. 9/针对非托管代码的测试,比较10/托管代码,GC可以更具策略自己回收,也可以实现IDisposable,调用Dispose()方法,主动释放。11namespace MemoryManagementClient1213    class Program14    15        static void Main(

32、string args)16        1718            /*/(1)/19            /调用Dispose()方法,主动释放。资源,灵活20        &

33、#160;   FrankClassWithDispose _frankClassWithDispose = null;21            try22            23         

34、60;      _frankClassWithDispose = new FrankClassWithDispose();24                _frankClassWithDispose.DoSomething();25         &#

35、160;      26            27            finally28            29     

36、;           if (_frankClassWithDispose!=null)30                _frankClassWithDispose.Dispose();31          &#

37、160;     /Console.WriteLine("FrankClassWithDispose实例已经被释放");32            33                34     &

38、#160;      /*/(2)/35            /可以使用Using语句创建非托管对象,方法执行结束前,会调用36            using (FrankClassWithDispose _frankClassWithDispose2 =

39、0;new FrankClassWithDispose()37            38                /_frankClassWithDispose2.DoSomething();39         

40、;   4041            /*/(3)/42            /垃圾收集器运行的时候,一次就释放资源43            FrankClassNoFinalize _fran

41、kClassNoFinalize = new FrankClassNoFinalize();44            _frankClassNoFinalize.DoSomething();45             46        

42、;    /*/(4)/47            /垃圾收集器运行的时候,两次才能够释放资源48            FrankClassWithDestructor _frankClassWithDestructor = new FrankClassWithDestruct

43、or();49            _frankClassWithDestructor.DoSomething();50            /*/(5)/51            /不能使用Using语句来创建对象,因为其没实现ID

44、ispose接口52            /using (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor()53            /54    

45、        /    _frankClassWithDestructor2.DoSomething();55            /5657            /*/58     

46、       /For Debug59            Console.WriteLine("Press any key to continue");60            Console.ReadLine()

47、;6162        63        64    6566 有些时候资源必须在特定时间释放,类可以实现执行资源管理和清除任务方法IDisposable.Dispose的接口IDisposable。如果调用者需要调用Dispose方法清理对象,类作为契约的一部分必须实现Dispose方法。垃圾收集器默认情况下不会调用Dispose方法;然而,实现Dispose方法可以调用GC里的方

48、法去规范垃圾收器的终结行为。值得一提的是:调用Dispose()方法,主动释放资源,灵活,可以使用Using语句创建非托管对象,方法执行结束前,会调用Dispose()方法释放资源,这两端代码的效果是一样的,可以查看编译后IL。1.try 2   3    IL_0003:  nop 4    IL_0004:  newobj     instance void

49、60;MemoryManagementMemoryManagement.FrankClassWithDispose:.ctor() 5    IL_0009:  stloc.0 6    IL_000a:  ldloc.0 7    IL_000b:  callvirt   instance void MemoryManagementMe

50、moryManagement.FrankClassWithDispose:DoSomething() 8    IL_0010:  nop 9    IL_0011:  nop10    IL_0012:  leave.s    IL_002811    / end .try12 

51、0;finally13  14    IL_0014:  nop15    IL_0015:  ldloc.016    IL_0016:  ldnull17    IL_0017:  ceq18    IL_0019:  stloc.s   

52、60;CS$4$000019    IL_001b:  ldloc.s    CS$4$000020    IL_001d:  brtrue.s   IL_002621    IL_001f:  ldloc.022    IL_0020:  callvirt  

53、0;instance void MemoryManagementMemoryManagement.FrankClassWithDispose:Dispose()23    IL_0025:  nop24    IL_0026:  nop25    IL_0027:  endfinally26    / end handler27 

54、; IL_0028:  nop28  IL_0029:  newobj     instance void MemoryManagementMemoryManagement.FrankClassWithDispose:.ctor()29  IL_002e:  stloc.130  .try31  32    IL_002f: &

55、#160;nop33    IL_0030:  nop34    IL_0031:  leave.s    IL_004535    / end .try36  finally37  38    IL_0033:  ldloc.139    

56、;IL_0034:  ldnull40    IL_0035:  ceq41    IL_0037:  stloc.s    CS$4$000042    IL_0039:  ldloc.s    CS$4$000043    IL_003b:  brtrue

57、.s   IL_004444    IL_003d:  ldloc.145    IL_003e:  callvirt   instance void mscorlibSystem.IDisposable:Dispose()46    IL_0043:  nop47    IL_0044: 

58、; endfinally48    / end handler49Using 语句有同样的效果,来实现非托管对象资源的释放。这点在面试中也会经常遇到,Using关键字的用法有哪几种等等类似的问题。基本理想的答案都是除了引用 命名空间,和命名空间设置别名外,就是这个用法实现如try finally块一样作用的对非托管对象资源的回收。只是一种简便的写法。当你用Dispose方法释放未托管对象的时候,应该调用GC.SuppressFinalize。如果对象 正在终结队列(finalization queue),GC.Suppres

59、sFinalize会阻止GC调用Finalize方法。因为Finalize方法的调用会牺牲部分性能。如果你的 Dispose方法已经对委托管资源作了清理,就没必要让GC再调用对象的Finalize方法(MSDN)。附上MSDN的代码,大家可以参考.-public class BaseResource: IDisposable   / 指向外部非托管资源   private IntPtr handle;   / 此类使用的其它托管资源.   private Component Components; 

60、60; / 跟踪是否调用.Dispose方法,标识位,控制垃圾收集器的行为   private bool disposed = false;   / 构造函数   public BaseResource()         / Insert appropriate constructor code here.      / 实现接口IDisposable.   / 不能声明为虚方法virtual. 

61、;  / 子类不能重写这个方法.   public void Dispose()         Dispose(true);      / 离开终结队列Finalization queue       / 设置对象的阻止终结器代码      /       GC.SuppressFinalize(

62、this);      / Dispose(bool disposing) 执行分两种不同的情况.   / 如果disposing 等于 true, 方法已经被调用   / 或者间接被用户代码调用. 托管和非托管的代码都能被释放   / 如果disposing 等于false, 方法已经被终结器 finalizer 从内部调用过,   /你就不能在引用其他对象,只有非托管资源可以被释放。   protected virtual void Dispose(b

63、ool disposing)         / 检查Dispose 是否被调用过.      if(!this.disposed)               / 如果等于true, 释放所有托管和非托管资源          if(disposing)                     / 释放托管资源.            Components.Dispose();  

温馨提示

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

评论

0/150

提交评论