没有合适的资源?快使用搜索试试~ 我知道了~
详谈.net中的垃圾回收机制,需要的朋友可以参考一下
资源推荐
资源详情
资源评论
详谈详谈.net中的垃圾回收机制中的垃圾回收机制
详谈.net中的垃圾回收机制,需要的朋友可以参考一下
1. 自动内存管理和GC
在原始程序中堆的内存分配是这样的:找到第一个有足够空间的内存地址(没被占用的),然后将该内存分配。当程序不
再需要此内存中的信息时程序员需要手动将此内存释放。堆的内存是公用的,也就是说所有进程都有可能覆盖另一进程的内存
内容,这就是为什么很多设计不当的程序甚至会让操作系统本身都down掉。我们有时碰到的程序莫名其妙的死掉了(随机现
象),也是因为内存管理不当引起的(可能由于本身程序的内存问题或是外来程序造成的)。另一个常见的实例就是大家经常
看到的游戏的Trainer,他们通过直接修改游戏的内存达到"无敌"的效果。明白了这些我们可以想象如果内存地址被用混乱了的
话会多么危险,我们也可以想象为什么C++程序员(某些)一提起指针就头疼的原因了。另外,如果程序中的内存不被程序员
手动释放的话那么这个内存就不会被重新分配,直到电脑重起为止,也就是我们所说的内存泄漏。所说的这些是在非托管代码
中,CLR通过AppDomain实现代码间的隔离避免了这些内存管理问题,也就是说一个AppDomain在一般情况下不能读/写另一
AppDomain的内存。托管内存释放就由GC(Garbage Collector)来负责。我们要进一步讲述的就是这个GC,但是在这之前
要先讲一下托管代码中内存的分配,托管堆中内存的分配是顺序的,也就是说一个挨着一个的分配。这样内存分配的速度就要
比原始程序高,但是高出的速度会被GC找回去。为什么?看过GC的工作方式后你就会知道答案了。
2. GC工作方式
首先我们要知道托管代码中的对象什么时候回收我们管不了(除非用GC.Collect**GC回收,这不推荐,后面会说明为什
么)。GC会在它"高兴"的时候执行一次回收(这有许多原因,比如内存不够用时。这样做是为了提高内存分配、回收的效
率)。那么如果我们用Destructor呢?同样不行,因为.NET中Destructor的概念已经不存在了,它变成了Finalizer,这会在后
面讲到。目前请记住一个对象只有在没有任何引用的情况下才能够被回收。为了说明这一点请看下面这一段代码:
复制代码 代码如下:
view sourceprint?object objA = new object();
object objB = objA;
objA = null;
// **回收。
GC.Collect();
objB.ToString();
这里objA引用的对象并没有被回收,因为这个对象还有另一个引用,ObjB。对象在没有任何引用后就有条件被回收了。
当GC回收时,它会做以下几步:
1、确定对象没有任何引用。
2、检查对象是否在Finalizer表上有记录。如果在Finalizer表上有记录,那么将记录移到另外的一张表上,在这里我们叫
它Finalizer2。如果不在Finalizer2表上有记录,那么释放内存。在Finalizer2表上的对象的Finalizer会在另外一个low priority的
线程上执行后从表上删除。当对象被创建时GC会检查对象是否有Finalizer,如果有就会在Finalizer表中添加纪录。我们这里所
说的记录其实就是指针。如果仔细看这几个步骤,我们就会发现有Finalizer的对象第一次不会被回收,也就是,有Finalizer的
对象要一次以上的Collect操作才会被回收,这样就要慢一步,所以作者推荐除非是绝对需要不要创建Finalizer。
GC为了提高回收的效率使用了Generation的概念,原理是这样的,第一次回收之前创建的对象属于Generation 0,之
后,每次回收时这个Generation的号码就会向后挪一,也就是说,第二次回收时原来的Generation 0变成了Generation 1,而
在第一次回收后和第二次回收前创建的对象将属于Generation 0。GC会先试着在属于Generation 0的对象中回收,因为这些是
最新的,所以最有可能会被回收,比如一些函数中的局部变量在退出函数时就没有引用了(可被回收)。如果在Generation 0
中回收了足够的内存,那么GC就不会再接着回收了,如果回收的还不够,那么GC就试着在Generation 1中回收,如果还不够
就在Generation 2中回收,以此类推。Generation也有个最大限制,根据Framework版本而定,可以用GC.MaxGeneration获
得。在回收了内存之后GC会重新排整内存,让数据间没有空格,这样是因为CLR顺序分配内存,所以内存之间不能有空着的
内存。现在我们知道每次回收时都会浪费一定的CPU时间,这就是我说的一般不要手动GC.Collect的原因。
当我们用Destructor的语法时,编译器会自动将它写为protected virtual void Finalize(),这个方法就是我所说的Finalizer。
就象它的名字所说,它用来结束某些事物,不是用来摧毁(Destruct)事物。在Visual Basic中它就是以Finalize方法的形式出
现的,所以Visual Basic程序员就不用操心了。C#程序员得用Destructor的语法写Finalizer,不过千万不要弄混了,.NET中已
经没有Destructor了。C++中我们可以准确的知道什么时候会执行Destructor,不过在.NET中我们不能知道什么时候会执行
Finalizer,因为它是在第一次对象回收操作后才执行的。我们也不能知道Finalizer的执行顺序,也就是说同样的情况下,A的
Finalize可能先被执行,B的后执行,也可能A的后执行而B的先执行。也就是说,在Finalizer中我们的代码不能有任何的时间
逻辑。下面我们以计算一个类有多少个实例为示例,指出Finalizer与Destructor的不同并指出在Finalizer中有时间逻辑的错
误:
复制代码 代码如下:
view sourceprint?public class CountObject {
public static int Count = 0;
public CountObject() {
Count++;
}
~CountObject() {
Count--;
}
}
static void Main() {
资源评论
weixin_38557530
- 粉丝: 6
- 资源: 896
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功