返回目录

第二章:垃圾回收

垃圾回收是你开发工作中要了解的最重要的事情。它是造成性能问题里最显著的原因,但只要你保持持续的关注(代码审查,监控数据)就可以很快修复这些问题。我这里说的“显著的原因”,实际上是我们对垃圾回收的理解和期望不正确导致的。在.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 代码 第二章:垃圾回收的更多相关文章

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

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

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

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

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

    避免使用终结器 如果没有必要,是不需要实现一个终结器(Finalizer).终结器的代码主要是让GC回收非托管资源用.它会在GC完成标记对象为可回收后,放入一个终结器队列里,在由另外一个线程执行队列里 ...

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. Linux指令--性能监控和优化命令相关指令

    原文出处:http://www.cnblogs.com/peida/archive/2012/12/05/2803591.html.感谢作者无私分享 性能监控和优化命令相关指令有:top,free,v ...

  2. Spring 4.x (三)

    1 Spring中加入DataSource并引入jdbc.properties 步骤: ①加入c3p0的jar包和mysql的驱动包 ②在src下新建jdbc.propertes文件 jdbc.dri ...

  3. awk grep sed cut学习

    awk学习网站 grep sed cut

  4. CSS继承、层叠和特殊性

    1.继承 (1)样式应用于某个特定的HTML标签元素,而且应用于其后代. (2)但某些标签不适用,如border: (3)例子:p{color:red;}设置了颜色 <p class=" ...

  5. python之在线平台与量化投资

    0. 第一个量化策略 # 初始化函数,设定基准等等 def initialize(context): set_benchmark('000300.XSHG') g.security = get_ind ...

  6. 【原创】Hibernate通过实体类自动建表时type=MyISAM的问题

    ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 当使用的mysql数据库为5.5版本时,方言需要设置为 <property name="hibernate.dialect&q ...

  7. Spring MVC Mock demo

    package com.niwodai.mem.web.controller; import com.alibaba.fastjson.JSON; import org.junit.Before; i ...

  8. null undefiend NaN

    console.log(typeof NaN) console.log(typeof undefined) console.log(typeof null)

  9. apache配置详解

    可参考:Apache 的 httpd.conf 详解 ServerTokens OS 此指令控制了Server回送给客户端的回应头域是否包含关于服务器OS类型和编译进的模块描述信息.服务器会发送:Se ...

  10. POJ [P2594] Treasure Exploration

    DAG图上可相交最小路径覆盖 先求给定DAG的传递闭包,将任意相连的两点加入二分图中,然后就是经典的不相交最小路径覆盖 所谓传递闭包就是将DAG图中任意点间的连通关系处理出来,用Floyd即可 #in ...