今天在做内存释放的工作时,因为盲目相信经验和自己的理解,居然弄错了几个地方。一直以来,我以为Using statement结束后调用的是finalizer,还根据GC释放的原理:“如果对象的reference不为0,那么该对象永远不会被释放”大大的探讨了一翻工作中碰到的问题,结果后来被老大指出只要类实现了IDispose,那么using结束后是能够强制调用Dispose的。通过这个问题,也让我突然发现我对Dispose理解上的错误,重新思考了Dispose和Finalize的区别 -- Dispose和Finalizer的主要区别在于释放的时机:
Dispose是的主要目的是“立刻运行释放”,应该释放尽可能多的资源,但主要还是去释放“不立刻释放会影响进程运行”的资源,比如连接的关闭工作等。而一般托管对象能够被GC自动释放,而且一般不会影响当前软件运行,所以Dispose中不释放也不会影响系统运行。
而Finalizer中则基本上是释放“资源不被需要时”释放的资源。举个实际点的例子,LoadLibrary可以用来在运行时加载dll并调用其中的interfaces,但有个问题是我们很难控制何时unloadlibrary,因为这些interface在整个运行时都可能被调用到,由loadlibrary加载进来的interfaces只有在程序退出时才“不被需要”。在这种情况下我们就可以利用单例的finalizer去做unloadlibrary的工作,因为单例的生存周期是和整个appdomain一致的,当appdomain被unload时,interfaces和它所依附的单例自然也没有存在的必要了。(具体的解释看这里)另外,为了安全的原因,Finalizer也会顺带调用Dispose函数,以便在最后的释放时机把之前可能忘记释放的资源一并释放。
|
|
Dispose |
Finalizer |
|
不释放资源对进程的影响程度 |
高 |
低 |
|
资源生命周期 |
短,但频繁 |
长,约等于整个对象的生命周期 |
|
进程对资源的需求 |
临时的或离散的 |
需求时间长 |
|
释放的时机 |
一旦意识到资源的存在可能影响系统运行,由程序员手工显示释放 |
当对象的不可以访问时,即reference为0,由GC释放在未知时间释放。 |
|
传递给开发人员的信息 |
当前对象不关闭有危险 |
当前对象不要关闭,释放的工作我自己会处理! |
还有一些释放时需要注意的地方:
- Dispose和finalize被调用了并不意味着对象本身也会在调用完毕后被系统释放掉,它们的主要目的是释放该对象中创建的可能影响系统的运行的资源。
- 如果Dispose释放的内容和Finalizer一致,那么可以在Dispose的最后调用GC.SuppressFinalize(this),他的作用是禁止GC释放对象时调用finalizer。
- using statement和调用dispose的是类似的的。在写这篇文章前还以为using结束后系统是异步调用Dispose,但根据msdn上的解释,他们两者一样,甚至编译器本身就是把using解释成调用dispose。所以,网上一些说法认为Using后要加GC.WaitForPendingFinalizers()也是不必要的。WaitForPendingFinalizers应该是和GC.Collect合用。
- using和dispose的区别就是using可以用来一次性Dispose多个对象,而不需要开发人员写上一排的xxx.Dispose()。
References:
http://msdn.microsoft.com/en-us/library/yh598w02.aspx


Comments:
You can leave a comment on this post if you login