标签:关系 gis cache window 对象 rtu 并且 搜索 申请
Garbage Collector
以应用程序的根(root)为基础,遍历应用程序堆(heap)上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是已经死亡的,哪些仍需要被使用,已经不再被应用程序的根(root)或者别的对象所引用的对象就是已经死亡的对象,即所谓的垃圾,需要被回收。
将线程挂起 -> 确定Roots -> 创建Reachable Object Graph(可达的对象图) -> 对象回收 -> Heap压缩 -> 指针修复
Heap中对象的引用关系错综复杂(交叉引用,循环引用),形成复杂的Graph(图),Roots是CLR在Heap之外可以找到的各种入口点。
指根据对象引用关系,从Roots出发可以到达的对象。例如当前执行函数的局部变量对象A是一个Root object,他的成员变量引用了对象B,则B是一个Reachable object。从roots出发可以创建Reachable objects graph,剩余对象即为unreachable,可以被回收。
GC搜索Roots的地方包括全局对象、静态变量、局部对象、函数调用参数、当前CPU寄存器中的对象指针、终止队列(Finalization Queue)等。主要可归为2种类型:已经初始化了的静态变量、线程仍在使用的对象(Stack + CPU Register)。
Debug和Release执行模式之间稍有区别,Release模式下后续代码没有引用的对象是Unreachable的,而debug模式下需要等到当前函数执行完毕,这些对象才会成为Unreachable,目的是为了调试时跟踪局部对象的内容。传给了COM+的托管对象也会成为root,并且具有一个引用计数器以兼容COM+的内存管理机制,引用计数器为0时,这些对象才可能成为被回收对象。Pinned objects指分配之后不能移动位置的对象,例如传递给非托管代码的对象(或者使用了fixed关键字),GC在指针修复时无法修改非托管代码中的引用指针,因此将这些对象移动将发生异常。pinned objects会导致heap出现碎片,但大部分情况来说传给非托管代码的对象应当在GC时能够被回收掉。
将对象按照生命周期分成新的、老的,根据统计分布规律所反映的结果,可以对新、老区域采用不同的回收策略和算法,加强对新区域的回收处理力度,争取在较短时间间隔、较小的内存区域内,以较低成本将执行路径上大量新近抛弃不再使用的局部对象及时回收掉。
.NET将Heap分成3个代龄区域: Gen 0、Gen 1、Gen 2,相应的GC有3种方式: #Gen 0 Collections, # Gen 1 Collections, #Gen 2 Collections。如果Gen 0 Heap内存达到阀值,则触发0代GC,0代GC后Gen 0中幸存的对象进入Gen1。如果Gen 1的内存达到阀值,则进行1代GC,1代GC将Gen 0 Heap和Gen 1 Heap一起进行回收,幸存的对象进入Gen2。
2代GC将Gen 0 Heap、Gen 1 Heap和Gen 2 Heap一起回收,Gen 0和Gen 1比较小,这两个代龄加起来总是保持在16M左右;Gen2的大小由应用程序确定,可能达到几G,因此0代和1代GC的成本非常低,2代GC称为Full GC,通常成本很高。粗略的计算0代和1代GC应当能在几毫秒到几十毫秒之间完成,Gen 2 heap比较大时,Full GC可能需要花费几秒时间。大致上来讲.NET应用运行期间,2代、1代和0代GC的频率应当大致为1:10:100。
这两个队列和.NET对象所提供的Finalize方法有关。这两个队列并不用于存储真正的对象,而是存储一组指向对象的指针。当程序中使用了new操作符在Managed Heap上分配空间时,GC会对其进行分析,如果该对象含有Finalize方法则在Finalization Queue中添加一个指向该对象的指针。
在GC被启动以后,经过Mark阶段分辨出哪些是垃圾。再在垃圾中搜索,如果发现垃圾中有被Finalization Queue中的指针所指向的对象,则将这个对象从垃圾中分离出来,并将指向它的指针移动到Freachable Queue中。这个过程被称为是对象的复生(Resurrection),本来死去的对象就这样被救活了。为什么要救活它呢?因为这个对象的Finalize方法还没有被执行,所以不能让它死去。Freachable Queue平时不做什么事,但是一旦里面被添加了指针之后,它就会去触发所指对象的Finalize方法执行,之后将这个指针从队列中剔除,这是对象就可以安静的死去了。
.NET Framework的System.GC类提供了控制Finalize的两个方法,ReRegisterForFinalize和SuppressFinalize。前者是请求系统完成对象的Finalize方法,后者是请求系统不要完成对象的Finalize方法。ReRegisterForFinalize方法其实就是将指向对象的指针重新添加到Finalization Queue中。这就出现了一个很有趣的现象,因为在Finalization Queue中的对象可以复生,如果在对象的Finalize方法中调用ReRegisterForFinalize方法,这样就形成了一个在堆上永远不会死去的对象,像凤凰涅槃一样每次死的时候都可以复生。
结构:分代算法中的GC回收器使用的是标记压缩算法,标记压缩算法在Mark的时候会使用Finalization Queue和Freachable Queue。
一个托管堆(managed heap)是垃圾回收器从操作系统申请的内存区(通过调用windows api VirtualAlloc)。当CLR载入内存之后,会初始化两个托管堆,一个大对象堆(LOH –large object heap)和一个小对象对(SOH – small object heap)。
在.Net 1.0和2.0中,如果一个对象的大小超过85000byte,就认为这是一个大对象。这个数字是根据性能优化的经验得到的。
大对象回收是和二代一起回收的,大对象回收后不会进行移动(碎片整理),而是将空闲的内存当作下一次一个比空闲内存小于的对象放进去,此操作会导致出现内存碎片,并且这个碎片就在这个程序的生命周期中永远不能被再次利用了。
如果大对象堆上没有足够的空闲内存容纳要申请的大对象空间,CLR首先会尝试向操作系统申请内存,如果申请失败,就会触发一次二代回收来尝试释放一些内存。
在2代垃圾回收时,可以将不需要的内存通过VirtualFree交还给操作系统。
标签:关系 gis cache window 对象 rtu 并且 搜索 申请
原文地址:https://www.cnblogs.com/geduocoding/p/7686157.html