详细介绍
Home新浪微博GitHub科学上网© 2016. All rights reserved.Powered by Jekyll, Hyde & GitCafe Pages.水言木做一个真正的blogger.NET内存管理(4) - Dispose 和 Finalizer14 Jun 2015Dispose 模式{// 注意: Finalizer 通常是不需要的~MyDisposable(){Dispose(false);}public void Dispose(){// 调用 Dispose 重载,传入 trueDispose(true);GC.SuppressFinalize(this);}protected virtual void Dispose(bool disposing){// 清理操作}}开发人员手工调用Dispose时,调用的是第一个公开的Dispose方法,它转而调用protected的Dispose重载,并传入disposing = true,表示是手工调用Dispose。如果是 Finalizer 在调用Dispose重载,则传入disposing = false。所有的清理逻辑都应当写在protected的Dispose重载中,并通过disposing参数来判断Dispose是什么时候被调用的 (这很重要,后面再说);如果第一个Dispose被调用,那一定是开发人员手工调用,此时我们要告诉 GC,开发人员已经手工清理过了,不要再调用Finalizer,这是通过GC.SuppressFinalize(this)实现的。GC.SuppressFinalize(this)要放在Dispose(true)后面,因为要保证 Dispose 成功调用后才能不执行Finalizer;这里添加 Finalizer 是为了说明disposing的意义,事实上,Finalizer 不属于 Dispose 模式的内容,如上一篇博文所说,99.9% 的情况下我们都不要 Finalizer;Dispose方法中不要抛出异常,除非我们觉得系统状态已经严重破坏,必须马上中止执行;Dispose要允许被多次调用,调用多次和调用一次的效果要一样。我们可以在内部维护一个bool字段,用于标识Dispose是否已调用过,如果已调用过,再次调用时直接返回即可;什么时候需要 Finalizer继承实现了 Dispose 模式的基类{protected override void Dispose(bool disposing){// 清理操作写在这里// 再调用 base.Dispose(disposing)base.Dispose(disposing);}}{~MyFinalizable(){Dispose(false);}}{protected override void Finalize(){try {// 子类调用一次 Dispose(false)Dispose(false);} finally {// 基类的 Finalize 中还会再调用一次 Dispose(false)base.Finalize();}}}
disposing参数
{
if (disposing)
{
// 这里可以调用其它托管对象的方法
// 或按需调用其它对象的 Dispose()
}
// 这里不能调用其它托管对象的方法,
// 如果要调用,要放到上面的 if 中去。
// 这里可以释放非托管资源,例如 XXX.CloseHandle(...) 之类的调用
// 如果基类实现了 Dispose 模式,
// 这里就还要加上 base.Dispose(disposing)
}
如果要调用其它托管对象的方法,一定要放到if中去,也就是说,只有在开发人员手工调用Dispose时,才可以调用其它对象的方法,这是因为 Finalizer 是无序执行的,我们内部引用了FileStream,并不意味着我们的 Finalizer 一定会先于FileStream的 Finalizer 执行,当我们的 Finalizer 执行时,FileStream的 Finalizer 可能已经先执行了,显然,此时调用FileStream上的方法是很危险的。也许被引用的对象上确实有些方法总是可以安全调用,但我们很难确定具体哪些方法可以,也许这些方法第一版本时还可以安全调用,但第二版时就不行了,所以最保险的办法就是永远别调用;
可以考虑添加一个内部字段,用来标识Dispose是否已调用过,如果已调用过就直接返回,这可以避免Dispose的重复调用带来的性能影响;
Finalizer 执行的无序性
FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096);
var bytes = Encoding.ASCII.GetBytes("Hello World");
stream.Write(bytes, 0, bytes.Length);
GC.Collect();
GC.WaitForPendingFinalizers();
FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096);
var writer = new StreamWriter(stream, Encoding.ASCII);
writer.Write("Hello World");
GC.Collect();
GC.WaitForPendingFinalizers();
Critical Finalizer
{
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected CriticalFinalizerObject()
{
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
~CriticalFinalizerObject()
{
}
}
1. 其Finalize方法会在对象创建时立即被 JIT 编译,我们知道 CLR 采用的是即时编译,一个方法只有在被用到时才会被 JIT 编译,而编译方法需要内存,在内存受限系统中,如果等到要执行时才编译Finalize,可能会导致OutOfMemoryException。当然,如果硬要在Finalizer 中做分配内存之类的操作 (比如装箱,字符串上的方法调用等,都可能分配内存),那 CLR 也搞不定,所以除了 CLR 的努力,我们也要做相应配合;
2. 即使宿主要强制中止 (rude abort) 一个 AppDomain,那 CLR 也尽可能保证该 AppDomain 中的 Critical Finalizer 能得以执行。但
猜你喜欢
- Sina Visitor - 电脑网络 > 网址黄页
- 点点网络 | 轻博客 - 电脑网络 > 网址黄页
- SmdCn's - 电脑网络 > 网址黄页
- PC426论坛|学会思考, - 电脑网络 > 网址黄页
- 郑州房产-郑州楼市-郑州聊 - 电脑网络 > 网址黄页
- 纯碱,原盐,烧碱 - 山东 - 电脑网络 > 网址黄页
- WordPress中文社区 - 电脑网络 > 网址黄页
- 页面不存在 - 电脑网络 > 网址黄页
- 二蛋博客 - 电脑网络 > 网址黄页
- rennai::blog - 电脑网络 > 网址黄页
- 网站运营,网站运营方案,周 - 电脑网络 > 网址黄页
- 爱的回归线 | 下一个瞬间 - 电脑网络 > 网址黄页
- 点点网络 | 轻博客 - 电脑网络 > 网址黄页
- 宁海城市网—宁海论坛|全球 - 电脑网络 > 网址黄页
- 23wuhan.com - 电脑网络 > 网址黄页
- 高山流水 – - 电脑网络 > 网址黄页
- 【东莞房地产门户|东莞房产 - 电脑网络 > 网址黄页
- 南京业主论坛(社区)_南京 - 电脑网络 > 网址黄页
- 生活论坛_新浪网 - 电脑网络 > 网址黄页
- 成都整形美容医院_华西保健 - 电脑网络 > 网址黄页