zoukankan      html  css  js  c++  java
  • 浅谈你感兴趣的 CLR GC 机制底层

    本文内容是学习CLR.via C#的21章后个人整理,有不足之处欢迎指导。

    昨天是1024,coder的节日,我为自己coder之路定下一句准则--保持学习,保持自信,保持谦逊,保持分享,越走越远。

    第一部分—基本原理思想

       垃圾回收机制是针对托管堆而言。

       不同于C的运行时堆,托管堆是内存是连续的,每次分配新内存,NextObjPtr指针只需要加上新分配内存块大小即可。C运行时堆为了维护链表的完整性,每当分配新的内存时,遍历链表,一旦发现足够大的内存块,则拆分块,修改节点中的指针。从托管堆中分配内存的速度,几乎可以与线程栈分配相媲美。

       GC机制回收的就是托管堆中的垃圾对象。

    第二部分—基本算法思想

       GC检查托管堆中是否有不再使用的对象。

       那么什么是不再使用的对象?

       首先要解释什么是根(root)。每个应用程序都有一组根,每个根都是一个存储位置,其中包含指向引用类型对象的一个指针。该指针要么运用托管堆中的一个对象,要么为null。类型中定义的任何静态字段被认为是一个根,任何方法参数或者局部变量也被认为是一个根。只有引用类型变量,才被认为是根,值类型的变量永远不被认为是根。

       GC开始执行时,假设所有对象都是垃圾。

       GC沿着线程栈上检查所有的根,如果发现了一个跟引用堆中的对象,则在这个对象的“同步索引字段”上开启一位,也就是将这个bit设置为1,也就是说这个对象被标记了。

       GC就是这样,以递归的方式遍历所有可达的对象。可达的对象也就是说有根的对象,就是在标记阶段被标记的,也就是本次不回收的。所以不可达的对象就被回收了。

       进入第二阶段--压缩阶段。

       实际上此压缩非彼压缩,在这里是指碎片整理,如何整理呢?如果发现晓得内存块,GC忽略它们,如果发现大的,可用的连续内存块,GC把非垃圾对象移动到这里以压缩堆。

       包含只想这些对象的指针的变量和CPU寄存器,现在都会变得无效,NextObjPtr也应重新指向托管堆的结尾。

      

    第三部分—列表和F-reachable队列

       说到终结列表要从某些不仅占用内存的对象说起,比如FileStream,它不仅占用内存资源,也在占用本地资源。

       Finalize方法就是用于释放本地资源的方法。

       那么这个终结列表用来做什么呢?微软当然不会画蛇添足,请仔细看好下面这段:

       既占用内存资源,又占用本地资源的,在GC回收这样对象所占的内存时,仅仅回收内存时不够的,因为一定要调用Finalize方法来释放本地资源啊!强烈不建议在代码中我们手动Finalize,这需要堆Finalize的实现有相当深刻并且全面的理解。那么微软的GC是什么怎么来给我们执行的呢?这就用到了终结列表,为了一定要保证执行Finalize,在最初我们new操作符分配内存地时候,如果该对象的类型中定义了Finalize方法,那么将该对象的一个指针方法到终结列表当中,当此类对象在托管堆中判定为垃圾的时候,GC扫描终结列表,以查找这些对象的指针,该指针会从终结列表中移除,并追加到F-reachable队列。

       那么这个队列干嘛的呢?我认为唯一的目的就是复活对象,并调用Finalize方法释放本地资源(如果对象是死的,我们无法调用其方法),调用方法的是一个微软定义好的优先级比较高的一个线程,听说这样做有很多好处。那么为什么放到F-reachable队列中就复活了?f(finalization)终结,reachable可达的。换言之,可将这个队列看做静态字段那样的一个跟。

      

    第四部分—代的思想和原理

       GC机制无论何时,都分为三代,0代,1代,2代。

       代是什么,微软关于这个做了假设,新对象生存周期比较短,而老对象倾向于活的久一些。所在代越高的对象,存活的越久,所在代越低的对象,越容易被回收。

       代就是托管堆中被分配的内存而已,也可以说把托管堆分成三部分吧?

       0代初始的预算大小为256kb,当0代中的内存用完时,为新对象分配内存,0代内存不够用时,GC开始回收第一代,未被标记的对象当然回收,已标记的对象则这些对象提高一代,进入1代区域,与此同理,当一代内存已满时,回收1代,此时根对象也就是标记的对象提升至2代。

       再次重点说一下三代预算大小分别为256KB,2MB,10MB,预算大小以提升性能为宜,预算越大,垃圾回收频率越低。再次注意的是,性能提升的理论源于开始的假设:新对象生存期较短,老对象倾向于活的久一些。请仔细看下面一段。

       如果GC回收后,0代存活下来的对象很少,或者说回收的内存很多。0代预算可能会从256调整为128。代的分配空间减少,意味着回收频繁回收频繁,但GC所做的工作会减少,从而减小进程的工作集,最理想的状态是0代对象都是当做垃圾被回收,这样不必压缩内存,NextObjPtr指向0代起始处。这样来讲,最开始的假设是是成立的。

  • 相关阅读:
    django页面分类和继承
    django前端从数据库获取请求参数
    pycharm配置django工程
    django 应用各个py文件代码
    CF. 1428G2. Lucky Numbers(背包DP 二进制优化 贪心)
    HDU. 6566. The Hanged Man(树形背包DP DFS序 重链剖分)
    小米邀请赛 决赛. B. Rikka with Maximum Segment Sum(分治 决策单调性)
    区间树 学习笔记
    CF GYM. 102861M. Machine Gun(主席树)
    2016-2017 ACM-ICPC East Central North America Regional Contest (ECNA 2016) (B, D, G, H)
  • 原文地址:https://www.cnblogs.com/tdws/p/4909589.html
Copyright © 2011-2022 走看看