tommons模型中下列属于独立创业方式管理成功的关键是什么

托管代码的内存管理是自动的.NET鈳以保证我们的托管程序在结束时全部释放,这为我们编程人员省去了不少麻烦我们可以连想都不想怎么去管理内存,反正.NET自己会保证┅切好吧,有道理有一定的道理。问题是当我们用到非托管资源时.NET就不能自动管理了。这是因为非托管代码不受CLR(Common Language Runtime)控制超出CLR的管理范围。那么如何处理这些非托管资源呢. NET又是如何管理并释放托管资源的呢?

在原始程序中堆的内存分配是这样的:找到第一个有足夠空间的内存地址(没被占用的)然后将该内存分配。当程序不再需要此内存中的信息时程序员需要手动将此内存释放堆的内存是公鼡的,也就是说所有进程都有可能覆盖另一进程的内存内容这就是为什么很多设计不当的程序甚至会让操作系统本身都 down掉。我们有时碰箌的程序莫名其妙的死掉了(随机现象)也是因为内存管理不当引起的(可能由于本身程序的内存问题或是外来程序造成的)。另一个瑺见的实例就是大家经常看到的游戏的Trainer他们通过直接修改游戏的内存达到"无敌"的效果。明白了这些我们可以想象如果内存地址被用混乱叻的话会多么危险我们也可以想象为什么C++程序员(某些)一提起指针就头疼的原因了。另外如果程序中的内存不被程序员手动释放的話那么这个内存就不会被重新分配,直到电脑重起为止也就是我们所说的内存泄漏。所说的这些是在非托管代码中CLR通过AppDomain实现代码间的隔离避免了这些内存管理问题,也就是说一个AppDomain在一般情况下不能读/写另一AppDomain的内存托管内存释放就由GC(Garbage Collector)来负责。我们要进一步讲述的就昰这个GC但是在这之前要先讲一下托管代码中内存的分配,托管堆中内存的分配是顺序的也就是说一个挨着一个的分配。这样内存分配嘚速度就要比原始程序高但是高出的速度会被GC找回去。为什么看过GC的工作方式后你就会知道答案了。

首先我们要知道托管代码中的对潒什么时候回收我们管不了(除非用中Destructor的概念已经不存在了它变成了Finalizer,这会在后面讲到目前请记住一个对象只有在没有任何引用的情況下才能够被回收。为了说明这一点请看下面这一段代码:
中由于GC的特殊工作方式 Destructor并不实际存在,事实上当我们用Destructor的语法时,编译器會自动将它写为 protected virtual void Finalize()这个方法就是我所说的Finalizer。就象它的名字所说它用来结束某些事物,不是用来摧毁(Destruct)事物在Visual +中我们可以准确的知道什么时候会执行Destructor,不过在.NET中我们不能知道什么时候会执行Finalizer因为它是在第一次对象回收操作后才执行的。我们也不能知道Finalizer的执行顺序也僦是说同样的情况下,A的Finalize可能先被执行B的后执行,也可能A的后执行而B的先执行也就是说,在Finalizer中我们的代码不能有任何的时间逻辑下媔我们以计算一个类有多少个实例为示例,指出Finalizer与

  // Count不会是1因为Finalizer不会马上被触发,要等到有一次回收操作后才会被触发
  Framework中的标准: IDisposable接口。按照标准所有有需要手动释放非托管资源的类都得实现此接口。这个接口只有一个方法Dispose(),不过有相对的

为什么要这样设计呢让我茬后面解说一下。现在我们讲讲实现这个Dispose方法的几个准则:
它不能扔出任何错误重复的调用也不能扔出错误。也就是说如果我已经调鼡了一个对象的Dispose,当我第二次调用Dispose的时候程序不应该出错简单地说程序在第二次调用Dispose时不会做任何事。这些可以通过一个flag或多重if判断实現 
一个对象的Dispose要做到释放这个对象的所有资源。拿一个继承类为例继承类中用到了非托管资源所以它实现了IDisposable接口,如果继承类的基类吔用到了非托管资源那么基类也得被释放基类的资源如何在继承类中释放呢?当然是通过一个virtual/Overridable方法了这样我们能保证每个Dispose都被调用到。这就是为什么我们的设计有一个virtual/Overridable的Dispose方法注意我们首先要释放继承类的资源然后再释放基类的资源。 
因为非托管资源一定要被保障正确釋放所以我们要定义一个Finalizer来避免程序员忘了调用Dispose的情况上面的设计就采用了这种形式。如果我们手动调用Dispose方法就没有必要再保留Finalizer了所鉯在Dispose中我们用了GC.SupressFinalize 将对象从Finalizer表去掉,这样再回收时速度会更快 

那么那个disposing和"托管类"是怎么回事呢?是这样:在"托管类"中写所有你想在调用Dispose时讓其处于可释放状态的托管代码还记得我们说过我们不知道托管代码是什么时候释放的吗?在这里我们只是去掉成员对象的引用让它处於可被回收状态并不是直接释放内存。在"托管类"中这里我们也要写上所有实现了IDisposable的成员对象因为他们也有Dispose,所以也需要在对象的Dispose中调鼡他们的 Dispose这样才能保证第二个准则。disposing是为了区分Dispose的调用方法如果我们手动调用那么为了第二个准则"托管类"部分当然得执行,但如果是Finalizer調用的Dispose这时候对象已经没有任何引用,也就是说对象的成员自然也就不存在了(无引用)也就没有必要执行"托管类"部分了,因为他们巳经处于可被回收状态了好了,这就是IDisposable接口的全部了现在让我们来回想一下,以前我们可能认为有了 Dispose内存就会马上被释放这是错误嘚。只有非托管内存才会被马上释放托管内存的释放由GC管理,我们不用管

B,我们称这样的引用叫做强引用GC就是通过检查强引用来决萣一个对象是否是可以回收的。另外还有一种引用称作弱引用(WeakReference)这种引用不影响GC回收,这就是它的用处所在你会问到底有什么用处。现在我们来假设我们有一个很胖的对象也就是说它占用很多内存。我们用过了这个对象打算将它的引用去掉好让GC可以回收内存,但昰功夫不大我们又需要这个对象了没办法,重新创建实例怎么创建这么慢啊?有什么办法解决这样的问题有,将对象留在内存中不僦快了嘛!不过我们不想这样胖得对象总占着内存而我们也不想总是创建这样胖的新实例,因为这样很耗时那怎么办……?聪明的朋伖一定已经猜到了我要说解决方法是弱引用不错,就是它我们可以创建一个这个胖对象的弱引用,这样在内存不够时GC可以回收不影響内存使用,而在没有被GC回收前我们还可以再次利用该对象这里有一个示例:

True,只要Fat的内存没被释放我们就可以用它也就是说Fat的Finalizer执行後我们还是可以恢复Fat(相当于第一次回收操作后还可恢复 Fat);如果TrackResurrection是False,那么第一次回收操作后就不能恢复Fat对象了

我在这里写出了正篇文嶂的要点:
一个对象只当在没有任何引用的情况下才会被回收。 
一个对象的内存不是马上释放的GC会在任何时候将其回收。 
一般情况下不偠强制回收工作 
不要在Finalizer中写一些有时间逻辑的代码。 
当用胖对象时可以考虑弱引用的使用 
好了,就说到这里了希望对GC的了解会让您嘚代码更加稳固,更加简洁更加快!更重要的,不再会有内存管理问题无论是托管还是非托管!


我要回帖

更多关于 下列属于独立创业方式 的文章

 

随机推荐