[翻译] 编写高性能 .NET 代码--第二章 GC -- 避免使用终结器,避免大对象,避免复制缓冲区
避免使用终结器
如果没有必要,是不需要实现一个终结器(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。如果你发现要复制缓冲区,可以尝试将其复制或合并到一个现有的缓冲区里,以避免任何新的内存分配。
[翻译] 编写高性能 .NET 代码--第二章 GC -- 避免使用终结器,避免大对象,避免复制缓冲区的更多相关文章
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少分配率, 最重要的规则,缩短对象的生命周期,减少对象层次的深度,减少对象之间的引用,避免钉住对象(Pinning)
减少分配率 这个几乎不用解释,减少了内存的使用量,自然就减少GC回收时的压力,同时降低了内存碎片与CPU的使用量.你可以用一些方法来达到这一目的,但它可能会与其它设计相冲突. 你需要在设计对象时仔细检 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 将长生命周期对象和大对象池化
将长生命周期对象和大对象池化 请记住最开始说的原则:对象要么立即回收要么一直存在.它们要么在0代被回收,要么在2代里一直存在.有些对象本质是静态的,生命周期从它们被创建开始,到程序停止才会结束.其它对 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少大对象堆的碎片,在某些情况下强制执行完整GC,按需压缩大对象堆,在GC前收到消息通知,使用弱引用缓存对象
减少大对象堆的碎片 如果不能完全避免大对象堆的分配,则要尽量避免碎片化. 对于LOH不小心就会有无限增长,但LOH使用的空闲列表机制可以减轻增长的影响.利用这个空闲列表,我们可以在两块分配区域中间找到 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 配置选项
配置选项 在基于"less rope to hang yourself with"思想下,.NET 框架没有给开发提供很多太多的配置选项.但在大多数情况下,GC会跟你的硬件配置,及 ...
- [翻译]编写高性能 .NET 代码 第二章:垃圾回收
返回目录 第二章:垃圾回收 垃圾回收是你开发工作中要了解的最重要的事情.它是造成性能问题里最显著的原因,但只要你保持持续的关注(代码审查,监控数据)就可以很快修复这些问题.我这里说的"显著的 ...
- [翻译]编写高性能 .NET 代码 第二章:垃圾回收 基本操作
返回目录 基本操作 垃圾回收的算法细节还在不断完善中,性能还会有进一步的提升.下文介绍的内容在不同的.NET版本里会略有不同,但大方向是不会有变动的. 在.net进程里会管理2个类型的内存堆:托管和非 ...
- [翻译]编写高性能 .NET 代码 第一章:性能测试与工具 -- 平均值 vs 百分比
<<返回目录 平均值 vs 百分比 在考虑要性能测试的目标值时,我们需要考虑用什么统计口径.大多数人都会首选平均值,但在大多数情况下,这个正确的,但你也应该适当的考虑百分数.但你有可用性的 ...
- [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Visual Studio
<<返回目录 Visual Studio vs虽然不是全宇宙唯一的IDE,但它是.net开发人员最常用的开发工具.它自带一个性能分析工具,你可以使用它来做开发,不同的vs版本在工具上会略有 ...
- [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Performance Counters(性能计数器)
<<返回目录 Performance Counters(性能计数器) 性能计数器是监视应用程序和系统性能的最简单的方法之一.它有几十个类别数百个计数器在,包括一些.net特有的计数器.要访 ...
随机推荐
- J.U.C JMM. pipeline.指令重排序,happen-before
pipeline: 现在的CPU一般采用流水线方式来执行指令.一个指令执行周期被分成:取值,译码,执行,访存,写会,更新PC若干阶段.然后,多条指令可以同时存在于流水线中,同时被执行,来提高系统的吞吐 ...
- JAVA正则表达式 Pattern和Matcher
java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. 1.简介: java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包. ...
- assembly 基础
body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...
- _2_head_中标签
创:20_3_2017修:5_4_2017 什么是title标签? --title 页面名(双) -- 整个html的页面名字,相当于一本书的书名 <title>北门吹雪</ti ...
- Servlet--继承HttpServlet写自己的Servlet
前面2篇关注的都是Servlet接口,在实际编码中一般不直接实现这个接口,而是继承HttpServlet类.因为j2e的包里面写好了GenericServlet和HttpServlet类来让我们简化编 ...
- Asp.net core 2.0.1 Razor 的使用学习笔记(一)
环境:vs2017 版本:15.5.6 一.新建项目 1.文件>新建>项目>Visual c#>.NET Core>ASP.NET Core Web应用程序(“.NET ...
- Spring ioc,aop的理解
什么是控制反转? 控制反转是一种将组件依赖关系的创建和管理置于程序外部的技术. 由容器控制程序之间的关系,而不是由代码直接控制 由于控制权由代码转向了容器,所以称为反转 依赖注入,作用是避免手工在各代 ...
- Java项目专栏之数据库建表
Java项目专栏之数据库建表 数据库建表前期准备 1. 安装mysql:数据库语言,语法和sql server差不太多,如果习惯于sql server可以不用mysql. 2. 安装navicat:可 ...
- java 获取ip地址和网络接口
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- linux的8小时差问题解决
下面是同步时间的解决方法: 1.vi /etc/sysconfig/clock #编辑文件 ZONE="Asia/Shanghai" UTC=false #设置为false,硬件时 ...