• GC的处理流程

    • GC暂停进程中的所有线程。
    • GC遍历堆中的所有对象,将某个位(这个位包含在对象同步块索引的字段中)设置为0(0表示将被删除)。
    • GC检查所有活动根(根是指引用类型的变量),把活动根指向的对象标记为1,再次该对象引用的其它对象标记为1,依次类推。
    • GC将活动对象进行重新排列,让所有幸存对象在内存中紧挨在一起。
    • 将移动过的幸存对象的根更新固定的偏移量。
    • GC恢复进程中的所有线程。
  • GC对活动对象(活动根)的认定:

    • 被任何静态对象或静态字段引用。
    • 应用程序栈中引用类型的变量指向的对象。
      • 应用程序中的本地引用类型变量。
      • 应用程序方法中的引用类型参数。
    • 等待被终结(finalized)的对象。
  • GC将活动对象进行重新排列后获得的好处:

    • 减少活动对象的内存地址范围,提升访问活动对象的性能。
    • 让剩余的内存空间变成连续的,便于后续分配给新对象。
    • 解决原生堆(非托管堆)的内存碎片问题。
  • GC代的算法的出发点:

    • 对象越新,生存期越短。
    • 对象越老,生存期越长。
    • 回收堆的一部分,速度快于回收整个堆。
  • GC关于代的算法流程:

    • 初始化堆,接下来分配的所有对象都是0代。
    • 某个时间点0代超过预算,触发一次GC回收。
    • 幸存下来的对象从0代提升到1代,此时0代不包含任何对象。
    • 对1代对象进行紧凑排列。
    • 在0代为新对象分配内存。
    • 一段时间后再次触发GC对0代进行回收。
    • 0代回收结束后发现0代的剩余空间不够用,此时会触发对1代的内存进行GC操作。
    • 1代中幸存下来的对象会被提升到2代。
    • 对1代对象进行紧凑排列。
    • 某个时间后再次触发GC对0代和1代进行回收。
    • 回收结束后发现0代的剩余空间不够用,此时会触发对2代的内存进行GC操作。
    • 对1代和2代对象进行紧凑排列。
    • 某个时间后GC对0-2代回收完成后,发现1代和2代的剩余空间太多,此时会对0-2代的内存进行重新分配。
  • GC触发的时机:

    • 0代超过预算时。
    • 显式调用System.GC.Collect()
    • Windows报告低内存
    • AppDomain正在被卸载
    • CLR正常关闭时
  • GC与Finalize(终结)方法

    • Finalize方法用于释放非托管资源的场景。
    • 定义过Finalize方法的对象在分配内存时,会被附加到支持终结的对象列表中。
    • GC在标记对象为可删除时会检查支持终结的对象列表是否存在,如果存在则会移到待终结的对象列表中。
    • 待终结的对象以及它引用的对象在判断为待删除时,这些对象还是会被临时性地提升一个代,让这些对象存活一会来确保终结操作能正常执行。
    • GC利用一个高优先级的线程来检查待终结的对象列表并调用对象的Finalize方法,Finalize方法完成后将对象引用从该列表中删除。
    • 下一轮GC触发时刚被终结的对象以及它引用的对象会被正常回收。
  • GC与大对象

    • ≥85KB的对象为大对象。
    • 大对象被分配在独立的堆地址空间中。
    • 大对象总是2代,并且只在2代回收操作时触发回收。
    • 大对象一般不会做紧凑操作。
  • 参考资料:
    • 《CLR via C# Fourth Edition》    Jeffrey Richter
    • Fundamentals of garbage collection
      https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals
    • C# Garbage Collection Active Roots
      https://stackoverflow.com/questions/8345075/c-sharp-garbage-collection-active-roots
posted on 2019-06-13 14:11  Sam Zhang  阅读(305)  评论(2编辑  收藏  举报