避免使用终结器

如果没有必要,是不需要实现一个终结器(Finalizer)。终结器的代码主要是让GC回收非托管资源用。它会在GC完成标记对象为可回收后,放入一个终结器队列里,在由另外一个线程执行队列里对象的终结器方法。这就意味着,如果你实现一个类的终结器,你必须保证在它在终结器执行后能被正常回收。这需要消耗一些CPU资源在清理对象上,会极大降低GC的整体效率。

如果你实现一个终结器,你也必须实现一个IDisposable接口用来清理资源,并在Dispose方法里调用GC.SupperessFinalize(this)将对象从终结器队列里移除。如果你在下次回收之前,正确的调用了Disspose方法,就不会让终结器执行。下面的栗子就是正确的演示了这个模式。

    class Foo : IDisposable
{
~Foo()
{
Dispose(false);
} public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} protected virtual void Dispose(bool disposing)
{
if (disposing)
{
this.managedResource.Dispose();
} // Cleanup unmanaged resourced
UnsafeClose(this.handle); // If the base class is IDisposable object
// make sure you call:
//base.Dispose(disposing);
}
}

你可以通过 [http://www.writinghighperf.net/go/15] 获得过的关于Dispose模式与终结器的更多信息。

注意 有些人认为终结器一定会被执行。正常情况下没错,但这不是绝对的。如果一个程序被强制终止,那么进程会立即消失,终结器自然也不会执行。当然主动退出时也许会有一个短暂的等待进程关闭时间,但如果你的终结器在终结器列表的后面,也是有可能不会被执行。此外由于终结器队列是循序执行,如果某个终结器进入了死循环,那么后面的终结器就不会被执行到。终结器不是执行在GC线程里,但他们会引发GC。

避免大对象

在分析了大量的程序代码后,大对象的边界被定义在85000 bytes上。任何大于等于这个边界值的对象都会判定为“大对象”,需要分配在一个单独的堆里。

我们希望尽可能的避免在大对象堆上进行分配。这不仅会导致更长的GC,也更容易造成内存碎片,让内存的分配边界随着时间不对增加。

为了避免这些问题,你需要严格控制程序在大对象堆里分配内容。你需要统筹安排你的对象在应用程序生存周期里的分配方案。

LOH是不会自动压缩的,但在.NET 4.5.1 之后,你还是可以通过特定方法去通知GC进行压缩。然而,这个是你最后的手段,因为这将导致一次很长的暂停。在做这之前,你还是好好想想,应该如何避免进入这个情况。

避免复制缓冲区

如果可能,你应该尽量避免复制数据。例如:如果你打算将一个文件数据读入MemoryStream(如果你需要一个大的缓冲区,最好做一个合并)。一旦分配了内存,每个组件都将从这个数据里的同一个副本里读取数据,并将其视为开发准则。

如果你只需要使用这个缓冲区里的一部分,可以使用ArraySegment类来访问这个缓冲区的部分数据。你可以使用 ArraySegment的Api来访问原始数据,你可以将它附加到一个创建的MemoryStram里。你所做的这一切不会产生新的数据副本。


var memoryStream = new MemoryStream();
var segment = new ArraySegment<byte>(memoryStream.GetBuffer(), 100, 1024);
var blockStream = new MemoryStream(segment.Array, segment.Offset, segment.Count);

复制内存最大的问题不在CPU而是GC。如果你发现要复制缓冲区,可以尝试将其复制或合并到一个现有的缓冲区里,以避免任何新的内存分配。

下一篇:第二章 GC -- 将长生命周期对象和大对象池化

[翻译] 编写高性能 .NET 代码--第二章 GC -- 避免使用终结器,避免大对象,避免复制缓冲区的更多相关文章

  1. [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少分配率, 最重要的规则,缩短对象的生命周期,减少对象层次的深度,减少对象之间的引用,避免钉住对象(Pinning)

    减少分配率 这个几乎不用解释,减少了内存的使用量,自然就减少GC回收时的压力,同时降低了内存碎片与CPU的使用量.你可以用一些方法来达到这一目的,但它可能会与其它设计相冲突. 你需要在设计对象时仔细检 ...

  2. [翻译] 编写高性能 .NET 代码--第二章 GC -- 将长生命周期对象和大对象池化

    将长生命周期对象和大对象池化 请记住最开始说的原则:对象要么立即回收要么一直存在.它们要么在0代被回收,要么在2代里一直存在.有些对象本质是静态的,生命周期从它们被创建开始,到程序停止才会结束.其它对 ...

  3. [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少大对象堆的碎片,在某些情况下强制执行完整GC,按需压缩大对象堆,在GC前收到消息通知,使用弱引用缓存对象

    减少大对象堆的碎片 如果不能完全避免大对象堆的分配,则要尽量避免碎片化. 对于LOH不小心就会有无限增长,但LOH使用的空闲列表机制可以减轻增长的影响.利用这个空闲列表,我们可以在两块分配区域中间找到 ...

  4. [翻译] 编写高性能 .NET 代码--第二章 GC -- 配置选项

    配置选项 在基于"less rope to hang yourself with"思想下,.NET 框架没有给开发提供很多太多的配置选项.但在大多数情况下,GC会跟你的硬件配置,及 ...

  5. [翻译]编写高性能 .NET 代码 第二章:垃圾回收

    返回目录 第二章:垃圾回收 垃圾回收是你开发工作中要了解的最重要的事情.它是造成性能问题里最显著的原因,但只要你保持持续的关注(代码审查,监控数据)就可以很快修复这些问题.我这里说的"显著的 ...

  6. [翻译]编写高性能 .NET 代码 第二章:垃圾回收 基本操作

    返回目录 基本操作 垃圾回收的算法细节还在不断完善中,性能还会有进一步的提升.下文介绍的内容在不同的.NET版本里会略有不同,但大方向是不会有变动的. 在.net进程里会管理2个类型的内存堆:托管和非 ...

  7. [翻译]编写高性能 .NET 代码 第一章:性能测试与工具 -- 平均值 vs 百分比

    <<返回目录 平均值 vs 百分比 在考虑要性能测试的目标值时,我们需要考虑用什么统计口径.大多数人都会首选平均值,但在大多数情况下,这个正确的,但你也应该适当的考虑百分数.但你有可用性的 ...

  8. [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Visual Studio

    <<返回目录 Visual Studio vs虽然不是全宇宙唯一的IDE,但它是.net开发人员最常用的开发工具.它自带一个性能分析工具,你可以使用它来做开发,不同的vs版本在工具上会略有 ...

  9. [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Performance Counters(性能计数器)

    <<返回目录 Performance Counters(性能计数器) 性能计数器是监视应用程序和系统性能的最简单的方法之一.它有几十个类别数百个计数器在,包括一些.net特有的计数器.要访 ...

随机推荐

  1. JavaScript 变量、类型与计算

    变量类型 变量计算 变量 题目: JavaScript 中使用typeof能得到的有哪些类型? ``` 1.1 变量类型 (1).js中的数据类型:字符串.数字.布尔.数组.对象.Null.Undef ...

  2. scrapy_xpath

    什么是xpath? 路径表达式 在xml和html中进行导航 包含标准函数库 遵循w3c标准 xpth节点关系是什么? 父节点 子节点 兄弟节点 先辈节点 后代节点 xpth语法 a         ...

  3. spring 声明式事务管理详解

    前言:spring框架对于事务管理提供了两种方案.一,编程式事务.二,声明式事务.本例主要剖析 声明式事务. 何为声明式事务: 通过spring的配置文件配置事务规则,或使用spring @Trans ...

  4. 【转】用Linux命令行获取本机外网IP地址

    $ curl ifconfig.me $ curl icanhazip.com $ curl ident.me $ curl ipecho.net/plain $ curl whatismyip.ak ...

  5. MyCat 入门:漫谈 MyCat 配置系统

    文章首发于[博客园-陈树义],点击跳转到原文<MyCat 入门:漫谈 MyCat 配置系统> 上篇文章<MyCat 启蒙:分布式系统的数据库架构演变>中,我们通过一个项目从零到 ...

  6. ado.net 参数传递之 in

    之前项目有一放行的功能,对某界面维护时(数据的增删改),先将数据保存到临时表中,放行后再真正的写入到库中.由于设计到主从表多中约束关系,所以当时我采用的是写一个存储过程来对某个界面的操作进行统一处理, ...

  7. Tomcat修改端口号(7.0 version)

    目的:有时端口号可能其他服务占用,就需要修改一下Tomcat的端口号,避免冲突. 自我总结,有什么需要改正的地方,请大家补充,感激不尽! 找到Tomcat的的配置文件server.xml 路径:%to ...

  8. 01_Python入门

    Python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆(中文名字:龟叔)为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程 ...

  9. Halcon一日一练:图像、变量实时更新

    某些场合,我们需要刷新图像来识别图像处理过程的差异性,便于调试判断问题和预测.Halcon提供了图像刷新操作,这些操作不会改变程序的最终处理结果. 例程: **实时刷新图像 dev_update_wi ...

  10. 一次saltstack环境变量的坑

    现场环境: salt-minion端: ip:10.0.3.149     环境:使用 nvm装的nodejs    受用nodejs自带的npm 安装pm2 sal-master端: IP:10.0 ...