C#基础之垃圾回收
1.托管资源的回收
我们都知道C#托管资源的回收由GC全权负责控制,可是什么时候GC会回收垃圾呢?一般出现以下情况会回收垃圾:手动调用GC.Collect()强制回收;第0代对象内存已满;应用程序域被卸载时,CLR会回收所有资源;windows报告内存不足。其中作为开发人员,我们应该尽量少使用Collect方法,除非已经很明确的知道内存中有很多大对象需要被回收,否则随意使用Collect方法会导致扰乱GC正常的工作方式,从而扰乱了应用程序的内存使用。现在来看一个完整的托管垃圾回收过程,如下图所示。从这个过程可以看到分代回收的优点:经过几次垃圾回收后全局对象等生存时间较长的对象将被放到第二代,由于第0代空间很小因此可以快速遍历寻找未被引用的对象,而正好第0代总是有生存时间较短的新对象加入,最终的效果就是提高了性能。在图中还提到了根的概念,每个应用程序都有由JIT编译器和CLR运行时维护的一组指向托管堆中内存的根指针,主要包括全局变量、静态变量、局部变量、寄存器指针。当遍历代中堆的对象时,一个根将作为一个入口点,我们说的对象被引用指的是在根上有指向对象的指针。如下图a指向objA,objA、objB、objC形成一个环,此时如果a离开了作用域而被释放,objB和objC也会被标记为不可达,GC只会标记一次之后不会再标记。
当GC把不可达的对象标记为不可达时,并不会就开始释放资源,而是会检查Finalization队列中是否有指针指向不可达对象,如果有这样的对象则会将Finilazation队列中的指针移到Freachable队列中。Freachable队列中一旦有指针则会触发指向的对象去执行Finalize方法,之后再从队列中删除这个指针,被指向的对象就可以由GC回收了。这样设计也是为了彻底的释放资源,当new一个含有Finalize方法的对象时,就会在Finalization队列中添加指向这个对象的指针。一般我们使用Finalize方法是为了释放非托管资源,所以为了彻底释放资源我们要保证非托管资源也被正确释放了。
垃圾回收就是不断地循环上述的步骤,当第二代内存也满了则会对第二代内存来一次垃圾回收,不过这种情况发生的概率很低。另外在程序运行过程中这3代的内存大小并不是不变的,而是会根据实际情况动态改变的。现在假设执行完了一次垃圾回收,将第0代和第1代的不可达对象清理了。可以想象在堆中存在着许多碎片,连续的代内存变得不再连续。因此GC会线程挂起接着来一次内存压缩,在本例中是将第0代和第1代的剩余对象转移到第2代中,从这个过程中可以发现并不是简单的复制对象就可以了,因为根中的指针所指向的对象已被转移。所以此时GC需要修改应用程序的根指针和发生对象引用的指针,让这些指针指向新的对象内存地址。如果这里有一个对象是被非托管资源所指向的,由于GC无法去修改非托管资源的指针,因此这个对象将不会被转移。
2.非托管资源的回收
在C#中当我们使用非托管资源比如文件操作、数据库链接、套接字时就使用了非托管资源,比如文件操作程序中我们会在操作非托管资源时使用using包起来或者最后调用Close()方法。使用Reflector工具可以看到加上using语句块其实就是在程序中最后添加了Close方法,Close方法则是调用了Dispose方法,之后又调用了SuppressFinalize方法让Finalize方法禁止调用。从Close方法的内部实现可以看出在C#中释放非托管资源的工作是交给Finalize与Dispose这2个方法了。Finalize方法是有我们程序员自己定义的一个方法来释放内存,调用时间是上面提到的GC垃圾回收前。Dispose方法是实现了IDispose接口中的Dispose方法,开发者直接调用来释放非托管资源。
在VS中创建一个FileStream对象fs,会发现使用点是无法点出Finalize方法的,F12进去也没有看到这个方法。这是因为.NET对其做了规定,开发人员只能通过析构函数来实现,不能显示的进行调用。如下面代码所示,如果我在析构函数中不加上sw.Close()的话te.txt打开是什么都看不到的,因为这时非托管资源没有释放。也就是说这里析构函数没起作用,它只是一个声明告诉CLR这个对象的指针需要添加到Finalization中,因此我们需要在析构函数中手动添加Close方法去释放资源。再来看看IL代码,可以看到析构函数在IL中就是一个Finalize方法,方法里面又调用了父类的Finalize方法。从这可以看出Finalize方法的一个特点,子类Finalize方法中会调用父类的Finalize方法,这样递归调用可保证所有父类直到object的资源都被清理掉,不过这对性能也是一个很大的损失啊。现在可以总结Finalize的工作原理了,首先Object类有一个受保护的实现了的虚方法,.NET要求每一个释放非托管资源的类通过析构函数的方式重写这个方法,当然也可以不重写,如果没有重写则Finalization队列中不会添加这个这个对象的指针。如果添加了析构函数,则需要在析构函数中编写释放资源的代码,说到底Finalize方法需要我们程序员手动的释放非托管资源。而且它被调用的时机还不知道,只知道是在一个对象变为不可达后才会被调用,这样的话可能在下一个GC回收周期非托管资源才被释放或者代数的增加。另外Finalization和Freachable2个队列的维护以及GC开新线程去执行Finalize方法(包括父类的)都将带来性能的损耗。
public class MyClass
{
StreamWriter sw;
~MyClass()
{
//进行资源的清理
sw.Close();
}
public void Func()
{
FileStream fs = new FileStream("D:\\te.txt", FileMode.OpenOrCreate);
sw = new StreamWriter(fs);
string str="哈哈";
sw.Write(str);
}
}
从上面可以看出对于非托管资源的释放,Dispose方法是首选,只需我们手动的编写一条代码即可释放,控制权在程序员手中并且性能比Finalize要好。关于Dispose的工作模式可查看我的另一篇随笔cnblogs.com/fangyz/p/5293888.html,一般操作非托管资源的类都重写了Dispose方法,比如可以在VS中看到FileStream的Dispose方法。如果我们要在自定义类中重写Dispose方法,最后要加上base.Dispose(),这样可保证继承链上的父类资源也释放了资源。
声明:本文原创发表于博客园,作者为方小白,如有错误欢迎指出 。本文未经作者许可不许转载,否则视为侵权。
C#基础之垃圾回收的更多相关文章
- JVM基础(5)-垃圾回收机制
一.对象引用的类型 Java 中的垃圾回收一般是在 Java 堆中进行,因为堆中几乎存放了 Java 中所有的对象实例.谈到 Java 堆中的垃圾回收,自然要谈到引用.在 JDK1.2 之前,Java ...
- jvm基础知识—垃圾回收机制
1.首先类的实例化.static.父类构造函数执行顺序 我们来看下面的程序代码: public class A { int a1 = 8; { int a3 = 9; System.out.print ...
- java基础之 垃圾回收机制
1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的 ...
- Java基础之垃圾回收
/** * 对象在没有任何引用可以到达时,生命周期结束,成为垃圾. * 所有对象在被回收之前都会自动调用finalize()方法. * ******************************** ...
- JS基础_垃圾回收(GC)
垃圾回收(GC) 程序运行过程中也会产生垃圾,这些垃圾积攒过多以后,会导致程序运行的速度过慢,所以我门需要一个垃圾回收的机制,来处理程序运行过程中产生的垃圾 当一个对象没有任何的变量或属性对它进行引用 ...
- Java基础教程——垃圾回收机制
垃圾回收机制 Garbage Collection,GC 垃圾回收是Java的重要功能之一. |--堆内存:垃圾回收机制只回收堆内存中对象,不回收数据库连接.IO等物理资源. |--失去使用价值,即为 ...
- Java基础:JVM垃圾回收算法
众所周知,Java的垃圾回收是不需要程序员去手动操控的,而是由JVM去完成.本文介绍JVM进行垃圾回收的各种算法. 1. 如何确定某个对象是垃圾 1.1. 引用计数法 1.2. 可达性分析 2. 典型 ...
- 【Java_基础】JVM内存模型与垃圾回收机制
1. JVM内存模型 Java虚拟机在程序执行过程会把jvm的内存分为若干个不同的数据区域来管理,这些区域有自己的用途,以及创建和销毁时间. JVM内存模型如下图所示 1.1 程序计数器 程序计数器( ...
- 【转载】Java垃圾回收机制
原文地址:http://www.importnew.com/19085.html Java垃圾回收机制 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和Java联 ...
随机推荐
- Erlang 进程被抢占的条件——一个进程长时霸占调度器的极端示例
最近研究 binary 的实现和各种操作对应的 beam 虚拟机汇编指令,发现有一些指令序列是不可重入的,比如说有的指令构造一个上下文(也就是某种全局状态),然后下一条指令会对这个上下文做操作(具体的 ...
- PS网页设计教程XXIX——如何在PS中设计一个画廊布局
作为编码者,美工基础是偏弱的.我们可以参考一些成熟的网页PS教程,提高自身的设计能力.套用一句话,“熟读唐诗三百首,不会作诗也会吟”. 本系列的教程来源于网上的PS教程,都是国外的,全英文的.本人尝试 ...
- Tomcat报java.lang.ClassNotFoundException: 1catalina.org.apache.juli.FileHandler
最近在生产环境部署Tomcat的时候,在启动的时候,在控制台报"java.lang.ClassNotFoundException: 1catalina.org.apache.juli.Fil ...
- solrcloud 配置实践
1.环境 3台虚拟机:192.168.26.129.192.168.26.131.192.168.26.132,使用命令sudo iptables -F 关闭防火墙 Solr: solr-6.1.0 ...
- 烂泥:LVM学习之逻辑卷、卷组及物理卷删除
本文由秀依林枫提供友情赞助,首发于烂泥行天下. 上篇文章,我们介绍了有关LVM的逻辑卷及卷组的空间缩小.这次我们来介绍下如何删除一个逻辑卷及卷组. 删除逻辑卷需要以下几个步骤: 1. 卸载已经挂载的逻 ...
- [转]highcharts图表入门之:如何让highcharts图表自适应浏览器窗体的大小或者页面大小
本文转自: http://jsfiddle.net/vCZ8V/1/ http://www.stepday.com/topic/?740 http://blog.csdn.net/yueritian/ ...
- NOIP2008 普及组T4 立体图 解题报告-S.B.S.(施工未完成)
题目描述 小渊是个聪明的孩子,他经常会给周围的小朋友们将写自己认为有趣的内容.最近,他准备给小朋友们讲解立体图,请你帮他画出立体图. 小渊有一块面积为m*n的矩形区域,上面有m*n个边长为1的格子,每 ...
- 【问题&解决】sql2012安装时卡在正在启动操作系统功能"NetFx3"上不动的解决办法
安装完windows8 后开始安装sql2012,安装过程中停在“正在启动操作系统功能"NetFx3"”不动了,很是着急,于是上网查了一下资料,原来NetFx3指的是Framewo ...
- codeforces 711D D. Directed Roads(dfs)
题目链接: D. Directed Roads time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- [反编译U3D]Decompile Unity Resources
工具说明 反编译unity project资源文件,包括ios,android,pc等平台资源,仅供学习使用! 以下工具任选其一,未有特殊说明是可以同时提取unity3.x,unity4.x,unit ...