今天抽空来讨论一下.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(定义了析构函数)。

具体代码如下:

1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Data;
 5using System.Data.Odbc;
 6using 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自动管理
15namespace 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 created ");
28        }
29        //测试方法
30        public void DoSomething()
31        {
32
33            ////code here to do something
34            return ;
35        }
36        //实现Dispose,释放本类使用的资源
37        public void Dispose()
38        {
39            if (_odbcConnection != null)
40                _odbcConnection.Dispose();
41            Console.WriteLine("FrankClassWithDispose has been disposed");
42        }
43    }
44    //没有实现Finalize,等着GC回收FrankClassFinalize的实例资源,GC运行时候直接回收45    public class FrankClassNoFinalize46    {47        private OdbcConnection _odbcConnection = null;48        //构造函数49        public FrankClassNoFinalize()50        {51            if (_odbcConnection == null)52                _odbcConnection = new OdbcConnection();53            Console.WriteLine("FrankClassNoFinalize  has been created");54        }55        //测试方法56        public void DoSomething()57        {5859            //GC.Collect();60            ////code here to do something61            return ;62        }63    }64    //实现析构函数,编译为Finalize方法,调用对象的析构函数65    //GC运行时,两次调用,第一次没释放资源,第二次才释放66    //FrankClassDestructor的实例资源67    //CLR使用独立的线程来执行对象的Finalize方法,频繁调用会使性能下降68    public class FrankClassWithDestructor69    {70        private OdbcConnection _odbcConnection = null;71        //构造函数72        public FrankClassWithDestructor()73        {74            if (_odbcConnection == null)75                _odbcConnection = new OdbcConnection();76            Console.WriteLine("FrankClassWithDestructor  has been created");77        }78        //测试方法79        public void DoSomething()80        {81            ////code here to do something8283            return ;84        }85        //析构函数,释放未托管资源86        ~FrankClassWithDestructor()87        {88            if (_odbcConnection != null)89                _odbcConnection.Dispose();90            Console.WriteLine("FrankClassWithDestructor  has been disposed");91        }92    }93}94

其中使用了非托管的对象OdbcConnection的实例。建立的客户端进行了简单的测试。客户端代码如下:

C#中垃圾回收与内存管理机制的更多相关文章

  1. 深入了解C#系列:谈谈C#中垃圾回收与内存管理机制

    今天抽空来讨论一下.Net的垃圾回收与内存管理机制,也算是完成上个<WCF分布式开发必备知识>系列后的一次休息吧.以前被别人面试的时候问过我GC工作原理的问题,我现在面试新人的时候偶尔也会 ...

  2. JavaScript 之垃圾回收和内存管理

    JavaScript 具有自动垃圾收集机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存.而在 C 和 C++ 之类的语言中,开发人员的一项基本 ...

  3. 《JavaScript 闯关记》之垃圾回收和内存管理

    JavaScript 具有自动垃圾收集机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存.而在 C 和 C++ 之类的语言中,开发人员的一项基本 ...

  4. C++中的垃圾回收和内存管理

    最开始的时候看到了许式伟的内存管理变革系列,看到性能测试结果的时候,觉得这个实现很不错,没有深入研究其实现.现在想把这个用到自己的一个项目中来,在linux下编译存在一些问题,所以打算深入研究一下. ...

  5. C++中的垃圾回收和内存管理(续)

    boost memory的gc_allocator的使用 首先编译生成boost-memory的库,由于生成的是.so的动态库,所以需要在运行程序之前,将库文件的路径添加到LD_LIBRARY_PAT ...

  6. javaScript 内存管理机制

    大家好,今天分享的主题为 JavaScript 内存管理机制,本次分享将从以下三部分进行讲述: js 内存管理与 js 垃圾 常见的 GC 算法 V8 引擎的垃圾回收 js 内存管理与 js 垃圾 关 ...

  7. php内存管理机制与垃圾回收机制

    PHP内存管理机制 1 var_dump(memory_get_usage()); //获取内存 2 $a = "laruence"; //定义一个变量 3 var_dump(me ...

  8. php内存管理机制、垃圾回收机制

    一.内存管理机制 先看一段代码: <?php //内存管理机制 var_dump(memory_get_usage());//获取内存方法,加上true返回实际内存,不加则返回表现内存 $a = ...

  9. JVM内存管理机制和垃圾回收机制

    JVM内存管理机制和垃圾回收机制 JVM结构 图片描述: java源码编译成class文件 class文件通过类加载器加载到内存 其中方法区存放的是运行时的常量.静态变量.类信息等,被所有线程共享 堆 ...

随机推荐

  1. bzoj2584

    这是bzoj上AC的第700题,一定要是一道神题!!! 当初分组赛的时候讲过拖到现在才写…… 我们考虑把垂直方向移动和水平方向移动分开来考虑,不合法的轮数取二者最小 假设考虑的是垂直方向移动,障碍其实 ...

  2. UVa 1252 (状压DP + 记忆化搜索) Twenty Questions

    题意: 有n个长为m的各不相同的二进制数(允许存在前导0),别人已经事先想好n个数中的一个数W,你要猜出这个数. 每次只可以询问该数的第K为是否为1. 问采用最优询问策略,则最少需要询问多少次能保证猜 ...

  3. 定义 androidlistview 滚动条位置

    1.找到每一页的最后一条数据的位置 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, ...

  4. MySQL auto_increment的坑

    背景: Innodb引擎使用B_tree结构保存表数据,这样就需要一个唯一键表示每一行记录(比如二级索引记录引用). Innodb表定义中处理主键的逻辑是: 1.如果表定义了主键,就使用主键唯一定位一 ...

  5. C#中嵌入互操作类型的含义

    首先说一下它的含义: 1. ”嵌入互操作类型”中的嵌入就是引进.导入的意思,类似于c#中using,c中include的作用,目的是告诉编译器是否要把互操作类型引入. 2. “互操作类型”实际是指一系 ...

  6. Android中不混淆类中函数

    情况一:混淆不同的函数aTest.bTest -keep class com.zony.Test { void aTest(byte[], int, int); void bTest(String, ...

  7. ISAPI在IIS7上的配置

    主要介绍ISAPI的作用.ISAPI在IIS7上的配置.开发ISAPI的基本内容及使用VS 2008配置ISAPI DLL开发项目. 一.ISAPI介绍 缩写词=Internet Server App ...

  8. webdriver(python)学习笔记五——层级定位

    层级定位 在实际的项目测试中,经常会有这样的需求:页面上有很多个属性基本相同的元素,现在需要具体定位到其中的一个.由于属性基本相当,所以在定位的时候会有些麻烦,这时候就需要用到层级定位.先定位父元素, ...

  9. 仿windows phone风格主界面

    使用了ZAKER到最新版本,其主界面采用windows phone的风格,感觉还蛮好看的,挺喜欢的,就模仿写了一下,实现到界面截图如下: 第一版面: 第二版面: 在实现了它到九宫格菜单,还实现了背景图 ...

  10. C#复习反射

    反射中常用方法: //获取对象类型 One one = new One(); Type t = one.GetType(); //动态加载 Assembly a = Assembly.LoadFile ...