GC总结
来源:互联网 发布:mac markdown 编辑:程序博客网 时间:2024/06/03 13:21
内存基础知识
下面的列表总结了重要的 CLR内存概念。
· 每个进程都有其自己单独的虚拟地址空间。同一台计算机上的所有进程共享相同的物理内存,如果有页文件,则也共享页文件。
· 默认情况下,32 位计算机上的每个进程都具有 2 GB 的用户模式虚拟地址空间。
· 作为一名应用程序开发人员,您只能使用虚拟地址空间,请勿直接操控物理内存。垃圾回收器为您分配和释放托管堆上的虚拟内存。如果您编写的是本机代码,请使用Win32 函数处理虚拟地址空间。 这些函数为您分配和释放本机堆上的虚拟内存。
· 虚拟内存有三种状态:
o 可用。 该内存块没有引用关系,可用于分配。
o 保留。 内存块可供您使用,并且不能用于任何其他分配请求。但是,在该内存块提交之前,您无法将数据存储到其中。
o 提交。 内存块已指派给物理存储。
· 可能会存在虚拟地址空间碎片。就是说地址空间中存在一些被称为孔的可用块。 当请求虚拟内存分配时,虚拟内存管理器必须找到满足该分配请求的足够大的单个可用块。 即使您具有 2 GB 的可用空间,2 GB 的分配请求也有可能会不成功,除非所有这些空间必须位于单个的地址块中。
· 如果用完保留的虚拟地址空间或提交的物理空间,则可能会用尽内存。
堆
进程初始化期间,CLR保留两个区段(segment)的虚拟地址空间:一个区段是普通堆,另一个区段是大对象堆。每个区段的大小是不同的。对于客户端应用程序,每个区段约为16MB;对于服务器应用程序,每个区段大约为64MB。还有一些其他因素会影响区段的大小,例如是32位还是64位操作系统上运行,以及机器安装的CPU数量,在CPU较多的机器上,区段会小一些。随着区段装满非垃圾对象,CLR会分配更多的区段,这个操作一直继续,直到整个进程都满了为止。
代
垃圾回收是在第0代满的时候发生的。使用代(generation)的机制的唯一目的就是提高性能。基本思路是,第0代是最近分配的对象,从未被垃圾回收算法检查过。在一次垃圾回收中存活下来的对象被提升到另一代。如经过一次垃圾回收,第0代被提升为第1代;第1代被提升为第2代。
GC会检查托管堆中是否有应用程序不再使用的对象。如果有,他们使用的内存就可以被回收,如果回收完毕后,仍然没有可用内存,那么new操作符会抛出一个OutOfMemoryException。
垃圾回收
有5种事件导致垃圾回收:
1. 第0代满
2. 显式调用GC.Collect
3. Windows报告内存不足
4. CLR卸载AppDomain
5. CLR关闭(进程关闭)
CLR使用一个高优先级,专用的线程来调用Finalize方法。对于前四种情况,如果一个Finalize方法进入了无限循环,那么这个特殊的线程就会被阻塞,其他Finalize方法就得不到调用。因为应用程序永远都不能回这些可终结对象的内存,只要程序还在运行,内存就会一直泄露。
对于第五种情况,每个Finalize方法大约有2秒的时间返回。如果2秒之内没有返回,CLR将直接杀死该进程。另外调用所有Finalize方法的时间超过了40秒,CLR也会杀死进程。这些值在将来有可能会发生改变。
即使实例的构造函数抛出了异常,实例的Finalize方法也会被调用,所以不应在Finalize方法中假定对象处于一致的状态。
对于一个可终结对象,在其构造函数被调用之前,此对象将被放入终结列表(finalization list)中。该列表是一个由GC控制的数据结构,它包含了所有可被终结的对象。在垃圾回收开始后,GC扫描终结列表,将垃圾对象从列表中移除(因为非垃圾对象会被标记,所以垃圾对象就是那些没有被标记的对象),然后将其添加到Freachable队列中,该队列是CLR的另一个内部数据结构。其中的每一个对象的Finalize方法都已经准备好被调用。当Freachable队列为空时,负责调用Finalize方法的线程会睡眠。当队列进入记录项,线程被唤醒,每一项都会被移除并调用Finalize方法。因此在Finalize方法中不应该对执行线程做任何假设。
虽然System.Object类型定义了Finalize方法,但是CLR知道忽略它。
当对象被添加到Freachable队列时,这个对象(及其引用的对象)复活了,因为对象现在是可达的了。现在GC不再认为对象是垃圾,所以GC不会回收这些对象的内存。此时特殊的线程清空Freachable队列并调用Finalize方法。当下一次垃圾回收的时候,会发现被终结的对象是垃圾对象,将会进行内存回收。整个过程中,需要两次垃圾回收才能收回这些对象占有的内存。实际情况下可终结对象会被提升代,所以需要不止两次回收才能收回这些对象占有的内存。
调用GC.SupressFinalize方法,将会打开当前对象的一个标志位,次标志位被打开后,CLR就不会把对象添加到Freachable队列。
大对象堆
任何大于或等于85000字节的对象都被视为大对象,大对象从大对象堆中分配。大对象和小对象一样终结和释放,但是大对象永不压缩。但是此行为在4.5.1中可以更改。大对象总是属于第二代,所以大对象很难被回收,所以尽量不要分配临时大对象。
在4.5.1中,使用如下代码压缩大对象堆,每次压缩完毕,GCSettings.LargeObjectHeapCompactionMode被重置为默认值。还要注意的是,后台垃圾回收器永远不会压缩大对象堆。
GCSettings.LargeObjectHeapCompactionMode =GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
预测大量内存操作是否成功
实现一个算法时,可能需要事先知道该算法需要大量内存。如果直接执行算法,有可能抛出OutOfMemoryException,这种情况下,之前的工作算是白做了。.NetFramework提供System.Runtime.MemoryFailPoint类,它允许在消耗内存的算法开始之前检测是否有充足内存。
创建 MemoryFailPoint 对象并指定下操作 (MB)需要使用MB 的内存位数。 如果没有足够的内存不可用, 将引发InsufficientMemoryException 异常。 MemoryFailPoint 运行在 16 MB 粒度。 所有值小于 16MB 视为 16 MB,同时,其他值视为 16 MB 的下一个更大的多个。
以下代码保留了1G内存用于执行算法,
try
{
using (MemoryFailPoint mfp =newMemoryFailPoint(1000))
{
//Do Anything needs 1G memory
}
}
catch (InsufficientMemoryException)
{
Console.WriteLine("InsufficientMemory");
}
如果MemoryFailPoint类的构造器没有抛出异常,表明逻辑上已经保留了请求的内存。但是请求的物理内存尚未分配。这表示,算法只是更有可能获得所需的内存并允许成功,并不表示一定会分配到所需的物理内存。此类只是帮助你写更健壮的程序。
线程劫持
当GC开始内测回收的时候,所有线程都必须挂起,因为这些线程不能再访问内存中的对象。这是因为GC线程要压缩内存,压缩过程中,对象的引用可能变得无效,所以只能在GC完成后,其他线程才能继续执行。
GC开始的时候,CLR会对其他线程进行劫持,使其进入一个特殊的函数,而这个函数将会进入一个临界区。所以如果在GC线程正在垃圾回收的时候,使用WinDbg查看进程中所有其他线程的堆栈,那么回发现,所有这些线程都进入了同一个方法,并且都试图获取同一个临界区。
并不是任何时刻都可以劫持线程,必须等待线程进入一个安全点。如果CLR创建了一个新对象,但是还没有将对象赋值给变量,此时劫持线程就不可行的,或者说线程不在一个安全点上。若是此时劫持线程并进行垃圾回收,那么新对象将被垃圾回收,而变量指向一个无效地址。
垃圾回收模式
垃圾回收模式包括工作站模式和服务器模式,使用如下配置节来配置模式,默认情况下使用工作站模式。
<runtime>
<gcServer enabled="true"/>
</runtime>
还可以在运行时获取垃圾回收模式是否为服务器模式,如下:
GCSettings.IsServerGC{get;}
该模式不能在运行时修改。
GC延迟模式
有四种延迟模式可供选择
· Batch
· Interactive
· LowLatency
· SustainedLowLatency
该模式可以在运行时获取或者设置,如下:
GCSettings.LatencyMode= GCLatencyMode.SustainedLowLatency;
这里面有一些版本问题,第四种模式只被.Net Framework4.0支持,而且只能用于工作站模式,但是到了.Net Framework 4.5,也可以用于服务器模式。
.Net Framework提供了配置节用于配置延迟模式,如下:
<gcConcurrentenabled="true"/>
要注意的是,这是个bool值,而且名称也不是gcLatencyMode。所以不能完全对应到四种延迟模式,具体的对应关系如下:
gcServer=true
gcServer=false
gcConcurrent =true
· .Net4.5之前:Batch
· .Net 4.5之后:SustainedLowLatency
· .Net4.0之前:Interactive
· .Net 4.0之后:SustainedLowLatency
gcConcurrent =false
Batch
Batch
对于服务器模式Interactive和LowLatency模式是无效的,但是在运行却能看到LatencyMode是Interactive。这也许是.Net的Bug。
总结一下,工作站GC支持所有四种模式,服务器GC支持Batch和SustainedLowLatency模式。
对于四种延迟模式的具体作用,解释如下:
· Batch
Batch模式是全阻塞模式,一旦GC开始运行,所有非GC线程都会被挂起,直到垃圾回收结束。
· Interactive
此模式是工作站GC的默认模式(在.Net 4.0之前)。GC会使用多个线程进行并发垃圾回收,但是此并发只针对第二代对象。第零代和第一代对象永远使用全阻塞模式回收。具体的回收场景是,某个对象的创建引发了垃圾回收,GC使用创建此对象的线程回收第0,1代垃圾,同时有一个专用线程用于在后台回收第二代垃圾。
· LowLatency
此模式用于对时间敏感的进程。GC会全力避免回收第二代,所以应该在短时间内应用此模式,并在不需要此模式的时候设置回原来的模式。
· SustainedLowLatency
作用域Interactive基本相同,但是可以应用在服务器GC模式。
区别是:Interactive模式的专用线程在GC的过程中,不允许发起另外一个GC过程,而且只能在内存段中剩余的空间中分配内存。
SustainedLowLatency模式允许在后台GC运行中启动另一次针对第0和1代的GC过程,甚至允许创建另一个新段来进行内存分配。
工作站和服务器垃圾回收比较
以下是工作站垃圾回收的线程处理和性能注意事项:
· 回收发生在触发垃圾回收的用户线程上,并保留相同优先级。因为用户线程通常以普通优先级运行,所以垃圾回收器(在普通优先级线程上运行)必须与其他线程竞争 CPU 时间。
· 不会挂起运行本机代码的线程。(针对后台回收且只限于标记过程)
· 工作站垃圾回收始终用在只有一个处理器的计算机上,而不管 <gcServer> 设置如何。 如果您指定服务器垃圾回收,则CLR 会使用工作站垃圾回收,并禁用并发。
以下是服务器垃圾回收的线程处理和性能注意事项:
· 回收发生在以 THREAD_PRIORITY_HIGHEST 优先级运行的多个专用线程上。
· 为每个 CPU(逻辑CPU)提供一个用于执行垃圾回收的专用线程和一个堆,并将同时回收这些堆。 每个堆都包含一个小对象堆和一个大对象堆,并且所有的堆都可由用户代码访问。 不同堆上的对象可以相互引用。
· 因为多个垃圾回收线程一起工作,所以对于相同大小的堆,服务器垃圾回收比工作站垃圾回收更快一些。
· 服务器垃圾回收通常具有更大的段。
· 服务器垃圾回收会占用大量资源。例如,如果在一台具有 4 个处理器的计算机上运行了12 个进程,则在它们都使用服务器垃圾回收的情况下,将有 48 个专用垃圾回收线程。在高内存加载的情况下,如果所有进程开始执行垃圾回收,则垃圾回收器将要计划 48 个线程。如果运行应用程序的数百个实例,请考虑使用工作站垃圾回收并禁用并发垃圾回收。这可以减少上下文切换,从而提高性能。如果启用后台回收,启用的线程将会更多。
如果要手动回收内存,那么就可以指定是否使用后台回收功能,如下:
public static void Collect(int generation,GCCollectionMode mode, bool blocking);
blocking参数为false,则表示使用后台回收功能。
如果要分配超过2G内存的对象,需要使用如下配置:
<gcAllowVeryLargeObjects enabled="true|false" />
- gc总结
- GC总结
- GC 总结
- GC总结
- CMS gc实践总结
- CMS gc实践总结
- Java GC 算法总结
- GC机制总结
- Java GC 算法总结
- JAVA GC 总结
- Java GC 算法总结
- CMS gc实践总结
- CMS GC总结
- GC、集合、IO总结
- java GC总结
- Java GC 算法总结
- JVM-GC总结-算法
- JVM GC总结
- 三星Note 2发送不了短信怎么办
- 如何制作一个基于Tile的游戏 Cocos2d-x 2.0.4
- OpenERP重载create方法
- oc学习之旅:自动释放池
- 忙碌的一天结束了
- GC总结
- 神奇的C语言十五:dummy代码?
- 开闭原则
- 每一天都是新的
- 网站备份
- buffer lock 引起的buffer cache上的等待事件
- Map.js
- Redis与Memcached的区别
- 通过win7 eclipse 连接虚拟机redhat 上hadoop的实现(上)