[翻译]编写高性能 .NET 代码 第二章:垃圾回收
第二章:垃圾回收
垃圾回收是你开发工作中要了解的最重要的事情。它是造成性能问题里最显著的原因,但只要你保持持续的关注(代码审查,监控数据)就可以很快修复这些问题。我这里说的“显著的原因”,实际上是我们对垃圾回收的理解和期望不正确导致的。在.NET开发中,内存的性能问题和CPU的性能问题一样多,这就是单独开一章主要描述这个问题的原因。
当我们提及垃圾回收造成的开销时,就会不如自主的紧张起来,但一旦你理解它,就能很好的优化你的程序。在后面文章里,你可以看到GC可以在大多数情况下,在堆处理上提供很好的性能,同时也能很好解决内存分配与内存碎片问题。
Windows在非托管堆里用使用一个空闲列表来维护内存分配。尽管它会想尽办法来减少内存碎片,但很多长时间运行的非托管代码(本地代码)的程序还是会碰上内存碎片问题。它会花很多时间在空闲列表里找到合适可分配地址。随着内存使用持续增长,不可避免的需要不断重启来解问题。一些程序还会采用自定义内存分配的方案(自己的内存分配算法来接管malloc)函数来解决内存碎片问题。
.NET里的内存分配通常在一个内存段里进行,这样申请和回收的消耗会小很多。托管堆通过将最近申请的内存对象放在一起,可以减少对空闲列表的遍历,提升性能。
在默认的分配过程中,通过代码获得对象的大小,然后在剩余的缓冲区里分配它。因为没有竞争,只要有合适的空间就能很快的分配。一旦这要申请的空间这一段无法满足,GC分配器会创建一个新的内存段,在这上面开始分配,之后的新的分配也都会在这新创建的内存段里进行。
这个过程中系统代码(分配器)只会做一些简单的检查。
我们来看一个简单的栗子:
private class MyObject
{
private int x;
private int y;
private int z;
}
private static void Main(string[] args)
{
var x = new MyObject();
}
首先,我们在分配器前设置一个断点
; Copy method table pointer for the class into
; ecx as argument to new()
; You can use !dumpmt to examine this value.
mov ecx,3F3838h
; Call new
call 003e2100
; Copy return value (address of object) into a register
mov edi,eax
目前分配的是:
; NOTE: Most code addresses removed for formatting reasons.
;
; Set eax to value 0x14, the size of the object to
; allocate, which comes from the method table
mov eax,dword ptr [ecx+4] ds:002b:003f383c=00000014
; Put allocation buffer information into edx
mov edx,dword ptr fs:[0E30h]
; edx+40 contains the address of the next available byte
; for allocation. Add that value to the desired size.
add eax,dword ptr [edx+40h]
; Compare the intended allocation against the
; end of the allocation buffer.
cmp eax,dword ptr [edx+44h]
; If we spill over the allocation buffer,
; jump to the slow path
ja 003e211b
; update the pointer to the next free
; byte (0x14 bytes past old value)
mov dword ptr [edx+40h],eax
; Subtract the object size from the pointer to
; get to the start of the new obj
sub eax,dword ptr [ecx+4]
; Put the method table pointer into the
; first 4 bytes of the object.
; eax now points to new object
mov dword ptr [eax],ecx
; Return to caller
ret
; Slow Path – call into CLR method
003e211b jmp clr!JIT_New (71763534)
总之,这些涉及到方法调用的指令只有9个,不是很难懂。
如果你使用了一些配置选项,例如工作站模式,它不会因为竞争而导致分配变慢,因为GC给每一个处理器(cpu内核)分配了一个堆(段)。.NET在这些内存分配地方做了一些复杂处理,但你不必深入了解它是如何工作,只需要知道如何优化它就可以了。
我在本书开头就涉及到垃圾回收是因为今后章节里很多东西都会涉及到它。正确的理解垃圾回收是帮助你实现好性能的基础。
[翻译]编写高性能 .NET 代码 第二章:垃圾回收的更多相关文章
- [翻译]编写高性能 .NET 代码 第二章:垃圾回收 基本操作
返回目录 基本操作 垃圾回收的算法细节还在不断完善中,性能还会有进一步的提升.下文介绍的内容在不同的.NET版本里会略有不同,但大方向是不会有变动的. 在.net进程里会管理2个类型的内存堆:托管和非 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少分配率, 最重要的规则,缩短对象的生命周期,减少对象层次的深度,减少对象之间的引用,避免钉住对象(Pinning)
减少分配率 这个几乎不用解释,减少了内存的使用量,自然就减少GC回收时的压力,同时降低了内存碎片与CPU的使用量.你可以用一些方法来达到这一目的,但它可能会与其它设计相冲突. 你需要在设计对象时仔细检 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 避免使用终结器,避免大对象,避免复制缓冲区
避免使用终结器 如果没有必要,是不需要实现一个终结器(Finalizer).终结器的代码主要是让GC回收非托管资源用.它会在GC完成标记对象为可回收后,放入一个终结器队列里,在由另外一个线程执行队列里 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 将长生命周期对象和大对象池化
将长生命周期对象和大对象池化 请记住最开始说的原则:对象要么立即回收要么一直存在.它们要么在0代被回收,要么在2代里一直存在.有些对象本质是静态的,生命周期从它们被创建开始,到程序停止才会结束.其它对 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 配置选项
配置选项 在基于"less rope to hang yourself with"思想下,.NET 框架没有给开发提供很多太多的配置选项.但在大多数情况下,GC会跟你的硬件配置,及 ...
- [翻译] 编写高性能 .NET 代码--第二章 GC -- 减少大对象堆的碎片,在某些情况下强制执行完整GC,按需压缩大对象堆,在GC前收到消息通知,使用弱引用缓存对象
减少大对象堆的碎片 如果不能完全避免大对象堆的分配,则要尽量避免碎片化. 对于LOH不小心就会有无限增长,但LOH使用的空闲列表机制可以减轻增长的影响.利用这个空闲列表,我们可以在两块分配区域中间找到 ...
- [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Performance Counters(性能计数器)
<<返回目录 Performance Counters(性能计数器) 性能计数器是监视应用程序和系统性能的最简单的方法之一.它有几十个类别数百个计数器在,包括一些.net特有的计数器.要访 ...
- [翻译]编写高性能 .NET 代码 第一章:工具介绍 -- Visual Studio
<<返回目录 Visual Studio vs虽然不是全宇宙唯一的IDE,但它是.net开发人员最常用的开发工具.它自带一个性能分析工具,你可以使用它来做开发,不同的vs版本在工具上会略有 ...
- [翻译]编写高性能 .NET 代码 第一章:性能测试与工具 -- 平均值 vs 百分比
<<返回目录 平均值 vs 百分比 在考虑要性能测试的目标值时,我们需要考虑用什么统计口径.大多数人都会首选平均值,但在大多数情况下,这个正确的,但你也应该适当的考虑百分数.但你有可用性的 ...
随机推荐
- 实现鼠标hover动画效果自己理解的两种方法——练习笔记
练习前端技术学院的任务,需要实现"导航栏中的链接,随着鼠标悬浮的位置,相应的链接下出现红色线段"的效果(如图1),我的理解有简易与稍显复杂一些的方法: 首先想到的就是直接利用伪元素 ...
- scrapy_全站爬取
如何查询scrapy有哪些模版? scrapy genspider –list 如何创建crawl模版? scrapy genspider -t crawl 域名 scrapy genspider - ...
- python_如何读写csv数据
案例: 通过股票网站,我们获取了中国股市数据集,它以csv数据格式存储 Data,Open,High,Low,Close,Volume,Adj Close 2016-06-28,8.63,8.47,8 ...
- jquery与js添加子元素
例如在select中添加option JQuery做法: <select id="myselect" name="myselect"> </s ...
- tomcat安装自制作ssl证书
1.执行命令 C:\servers\apache-tomcat-8.0.27>keytool -genkey -alias tomcat -keyalg RSA -keypass pass123 ...
- 【转】EI收录的中国期刊
ISSN 期刊名 0567-7718 Acta Mechanica Sinica 1006-7191 Acta Metallurgica Sinica (English Letters) ...
- 【转】十分有用的linux shell学习总结
在最近的日常工作中由于经常会和Linux服务器打交道,如Oracle性能优化.我们 数据采集服务器的资源利用率监控,以及Debug服务器代码并解决其效率和稳定性等问题.因此这段时间总结的有关Linux ...
- 理解js中的作用域,作用域链以及闭包
作用域变量作用域的类型:全局变量和局部变量全局作用域对于最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的 <script> var outerVar = " ...
- 配置redis开机自启动和监听
redis安装好后,每次手动启动很不方便,配置开机自启动. 方法一:设置启动命令到/etc/rc.d/rc.local rc.local文件是系统全局脚本文件,会在其他开机进程脚本文件执行完毕后执行该 ...
- ABP 数据迁移
我主要是在项目部署的时候.当添加一个租户的时候.那么租户是有一个单独的数据库.而我的并没有用多租户单数据库. 因此我的模块里面有一个领域事件 当租户添加时将生前表生成到对应的数据库中.如果那位网友有更 ...