当满足以下条件之一时将发生垃圾回收:
操作系统报告低内存请看(将触发第2代垃圾回收)。 这是通过 OS 的内存不足通知或主机指示的内存不足检测出来。
由托管堆上已分配的对象使用的内存超出了可接受的阈值。 随着进程的运行,此阈值会不断地进行调整。触发第0代回收
调用 GC.Collect 方法。 几乎在所有情况下,你都不必调用此方法,因为垃圾回收器会持续运行。 此方法主要用于特殊情况和测试。如果应用程序代码通过调用 GC.Collect 方法并将 generation
参数指定为 2 来包含回收。
应用程序调用new操作符创建对象,发现没有足够的地址空间来分配对象,CLR就经行垃圾回收。
CLR卸载APPDomain,所有代0、1、2的垃圾回收。
CLR正在关闭,收回内存
通常情况下,出现以下三种情形中的任一情况,都会执行 GC:
分配超出第 0 代或大型对象阈值。
阈值是某代的属性。 垃圾回收器在其中分配对象时,会为代设置阈值。 超出阈值后,会在该代上触发 GC。 因此,分配小型或大型对象时,需要分别使用第 0 代和 LOH 的阈值。 当垃圾回收器分配到第 1 代和第 2 代中时,将使用它们的阈值。 运行此程序时,会动态调整这些阈值。
这是典型情况,大部分 GC 执行都因为托管堆上的分配。
调用 GC.Collect 方法。
如果调用无参数 GC.Collect() 方法,或另一个重载作为参数传递到 GC.MaxGeneration,将会一起收集 LOH 和剩余的托管堆。
系统处于内存不足的状况。
垃圾回收器收到来自操作系统 的高内存通知时,会发生以上情况。 如果垃圾回收器认为执行第 2 代 GC 会有效率,它将触发第 2 代。
1、标记-清除算法
2、压缩算法
3、分代清除算法
标记不可达对象,然后将其清除。
标记对象的工作有两种模式:
1、CLR启动时会选择一个GC模式,进程终止前该模式不会改变。
2、加载 CLR 时,GC 分配两个初始堆段:一个用于小型对象(小型对象堆或 SOH),一个用于大型对象(大型对象堆,每个对象都大于85000字节)。
1、GC首先挂起所有线程,再判断要回收哪些代,然后应用程序的线程恢复允许。
如果回收第第一代或者第0代那么一切如常进行,如果要回收第二代,就会增加第0代的大小(超过其预算),以便再第0代中分配新对象。然后应用程序的线程恢复允许。
开始一次垃圾回收时候,GC会检查第一代(有过第一代)占用了多少内存,如果第一代远少于预算(内存空间),那么GC只检查第0代的对象。GC 将寻找存在的对象并将它们压缩。 但是由于压缩费用很高,GC 会扫过 LOH,列出没有被清除的对象列表以供以后重新使用,从而满足大型对象的分配请求。 相邻的被清除对象将组成一个自由对象。
2、构建不可达对象。目的是为了找出哪些可以删除,哪些不能删除。
GC运行一个普通的线程优先级的后台线程来查找不可达对象,找到之后再挂起所有线程。
(1)GC标记为“0”阶段。假定全部都可以删除。GC首先假定堆中的所有对象都是不可达unreachable(没有被人应用)的,然后CLR遍历堆中的所有对象,将同步索引字段中的一位设定为0,表示所有对象都因删除
(2)CLR检查所有的活动根,查看它们引用了哪些对象(这就为什么CLR的GC称为引用跟踪GC的原因)如果一个根包含null,CLR忽略这个根并继续检查下个根。
任何根如果引用了堆上的对象,CLR都会标记那个对象,也就是将该对象的同步块索引中的位设为1。一个对象被标记后,CLR会检查那个对象中的根,标记它们引用的对象。如果发现对象已经标记,就不重新检查对象的字段。这就避免了因为循环引用而产生死循环。
标记对象的工作有两种模式:
(3) 在标记完栈中根指向的可达对象后。GC开始标记GC句柄表和GC终结列表中的根指向的可达对象,具体如下:
1、然后垃圾回收器扫描appDomain 的GC句柄表中保护Normal、pinned标记的项,将他们当作根来处理,同时标记执行项目的所引用的对象(已经这些对象的内部字段所引用的对象)。
2、扫描appDomain 的GC句柄表中wesk记录项。如果weak记录项引用了未标记的对象,该引用标识就是不可达对象。该记录项的引用值改为null。
3、GC扫描终结列表,如果发现垃圾中有被Finalization Queue中的指针所指向的对象,则将这个对象从垃圾中分离出来,并将指向它的指针移动到Freachable Queue中。这个过程被称为是对象的复生。在GC压缩后将复活的对象提升到较老的一代,一个特殊的高优先级CLR线程,执行每个对象的Finale方法,最后清空Freachable队列。下一代垃圾回收时,发现以终结的对象成为真正的垃圾。可终结对象需要执行两次的垃圾回收才能释放它们的占用的内存。
4、weakTrackResurrection不理解 基本用不到
5、GC对内存进行压缩,填补不可达对象留下的内存空洞,这其实就是一个内存碎片整理的过程。Pinned对象不会压缩(移动),垃圾回收器会移动它周围的其他对象。
应用程序的根包含线程堆栈上的静态字段、局部变量、CPU 寄存器、GC 句柄和终结队列。
可达(reachable)
已标记的对象不能被垃圾回收,因为至少有一个根在引用它。我们说这种对象是可达(reachable)的,因为应用程序代码可通过仍在引用它的变量抵达(或访问)它。
不可达(unreachable)
未标记的对象是不可达(unreachable)的,因为应用程序中不存在使对象能被再次访问的根。
4、GC的压缩(compact)阶段
删除未标记的,然后把可达的对象整到一起形成连续可用的内存空间。整理好对象的内存地址发生变化。不会压缩大对象。AppDomain中的GC句柄表用pinned标记的项也不会移动。
5、修正根的引用。CLR还有将每个根的地址减去整合后偏移的字节数,保证根的地址指向正确内存对象。
6、重置托管堆的NextObjPtr指针。指向最后一个幸存对象之后的位置。
7、CLR恢复所有线程。
如果CLR在一次GC之后回收不了内存,而且进程中没有空间来分配新的GC区域,就说明该进程的内存已耗尽。此时,试图分配更多内存的new操作符会抛出OutOMemoryException。应用程序可捕捉该异常并从中恢复。但大多数应用程序都不会这么做:相反,异常会成为未处理异常,Windows将终止进程并回收进程使用的全部内存。
8、汇总这次清除的结果,自动调优。垃圾回收后会检测有各代的多少内存被回收,多少对象幸存,垃圾回收器会根据应用程序的要求的内存负载自由优化。增大或者减小这些代的预算、增加或减少回收的次数。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章