构建高性能ASP.NET
站点
第七章
如何解决内存的问题(
前篇)—
托管资源优化—
垃圾回收机制剖析
前言:本章主要详细的讲述如何因内存问题而导致的性能问题,很多的时候都是深入.NET
内核进行分析,然后给出解决方案,同时,本系列的其他文章,也争取做到:深入浅出。
本篇是为后面的做个铺垫,而且比较的精彩。只有真正的理解了本篇,后面才可以顺利的走下去。
本篇的议题如下:内存问题概述( 前篇)托管资源优化(前篇)对象的生命周期(前篇)对象的” 代“(前篇)大对象堆(LOH) (前篇)CLR 计数器的使用 (前篇)CLR Profiler 的使用(中篇)垃圾回收器的不同版本(中篇)对象使用注意事项(中篇)常用优化措施(后篇)非托管资源优化Session 会话的优化
内存问题概述
和CPU
一样,内存也是一个直接影响服务端性能的重要的硬件资源。
一般来说,如果服务端内存不足,从导致以下两个问题产生:
1. 导致服务端把一些原本要写到内存中的数据,写到硬盘上面。这样不仅仅加大了CPU 和磁盘的I/O 操作,同时也延长了读取这些数据的时间。2. 阻止了一些缓存策略的使用。
对于内存不足,一直最快最直接的方式就是去买内存条加在服务器上面。但是这样存在一个隐患的问题就是:如果加了新的内存之后,服务端又面临内存不足的问题,我们不可能无止境的加内存条,那么我们就必须从站点本身来解决这个问题,例如从服务端的配置,对站点的代码进行分析,优化。
托管资源优化
对于托管资源,相信大家并不陌生了,简单的说就是:在C#
的托管堆上面创建的资源,或者说通过new
产生的对象。
在深入讲解之前,我们首先来看看”
对象的生命周期”
对象的生命周期
当我们用new
关键字创建了一个对象的时候,这个对象就被分配到CRL
托管堆上面。这个托管堆是在内存中的。而且这个分配对象空间的速度是非常的快的,因为每次都是在托管堆的最后面划出一定的空间来给这个对象,不用去堆上面需找合适大小的空间。
如果当托管堆准备为一个对象分配空间的时候,发现托管堆上面的空间太小了,不足以分配给这个新的对象,那么CLR
就开始运行垃圾回收机制了。我们知道:垃圾回收机制会把那些在托管堆上面没有了引用指向的那些对象都清理掉,同时也会把托管堆上面现存的对象进行压缩。
但是有一点需要清楚:如果此时进行了垃圾回收的时候,清除了一些没有用的对象,但是只有在下一次来回收进行的时候,上次垃圾回收清除的对象才真正的从内存中消除(
此时,还有一些“对象复苏“等话题就不在赘述)
。
下面就来讲述一些垃圾回收的话题。
对象的”
代“
在CLR
进行垃圾回收的时候,垃圾回收器回去托管堆上面去检查对象是否可以被回收,这个检查过程是非常消耗资源的。为了避免每次垃圾回收都要便利托管堆上面的所有对象,CLR
给把托管堆上面的对象用”
代”
来划分,例如,第一代,第二代。然后每次便利扫描托管堆的时候,就去扫描某一个”
代”
中的对象,这样性能就好点。
在托管堆上面,可以把对象分为三个”
代”:0
代,1
代,2
代,仅此这三个代。每个对象都是从0
代开始的。一个对象每经历一次垃圾回收,并且这个对象还在使用中,那么这个对象的“代“就会增加1
代。例如,如果在0
代的对象,经历了一次垃圾回收之后,他的代就是1
代,如果是1
代的对象,最后就会变为2
代。如果对象本身已经是2
代了,不管经历多少次垃圾回收(
如果对象一直在使用)
,那么这个对象还是2
代。
在CLR
垃圾回收中有句话要记得:”
’
代’
数越大,被回收的可能性就越小”
。而且一些性能优化就是根据这个进行的。
每次CLR
在进行垃圾回收的时候,都会优先的去扫描第0
代的对象,所以,一些新的,临时使用的对象可以被立刻的清除。相比而言,垃圾回收器扫描第1
代对象的频率就没有第0
代强,扫描第2
代对象的频率就更低了。所以说:对象存活的时间越长,就越难被回收,而且一直占据CLR
的内存资源。
还有有点需要注意的就是
:如果CLR
决定要扫描了第1
代了,同时也用扫描第0
代的对象,同时如果,CLR
扫描第2
代对象,那么第0
代,第1
代对象都会被扫描。
所以,从这里可以得出:我们尽量避免把原本需要立刻回收的的对象变为长期存活的对象。通俗点说就是:如果一个对象本来已经存活在0
代的,然后用完就回收的,我们不要让这个对象一直存活到第1
代,甚至第2
代。在编程上面基本就是这样的实现思路:尽可能晚的实例化对象,尽可能早的释放对象。
大对象堆
(Large Objecet Heap)
我们之前讲述了”
堆”
的一些话题,CLR
除了上面的一般的堆(
一般的new
对象分配空间的那个堆)
,CLR
中还存在另外的一个堆:专门用来放置那些大于了58k
的对象的堆,大对象堆。
如果new
一个对象的时候,这个对象的大小超过了85k
,那么CLR
就会把这个对象放在LOH
上面。如果此时LOH
的空间不足了,那么CLR
就会启动垃圾回收器去扫描LOH
堆和那个一般堆上面的第2
代对象,我们之前说过,如果扫描第2
代对象,就同时扫描第1
代,第0
代,那么实际相当于扫描了整个托管堆,性能影响可想而知。
而且不想之前那个一般堆,在LOH
上面的对象被垃圾回收器回收之后,上面的大对象是不会被压缩的,那么LOH
这个堆上面就可能存在一些”
空间碎片”
,然后分配新的大对象的时候,就要找空间,甚至进行碎片的整理,大家可以联想一下我们电脑的磁盘碎片整理。
OK
,今天就讲到这里,理论有点多,但是都是基本要清楚和掌握的,希望多多理解。
本文转自yanyangtian51CTO博客,原文链接:
http://blog.51cto.com/yanyangtian/495267
,如需转载请自行联系原作者