VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • CLR探索系列Card Table和Brick Table垃圾回收系列

制作者:剑锋冷月 单位:无忧统计网,www.51stat.net
 

  在CLR的垃圾回收子系统中,Card Table和Brick Table是两个比较有意思的表。

  在GC的过程中,一个Heap在运行了一段时间以后,已经分配的空间就会越来越大。在进行了一次局部代或者是完全的垃圾回收以后,就会涉及到一个GC堆的类似碎片整理的概念。整理优化一次GC Heap。同时,这种机制保证了譬如一个IIS Server在长时间的运行过程中的稳定性并且优化了其内存管理。

  这样的好处是显而易见的,但是采用这种解决方案带来的问题也很容易想到:譬如一个存在于GC Heap里面的"Small" Object被移动了,同时它也被其它的object引用。出现了这种情况,在进行GC的时候,就需要遍历整个GC的各种table,root,heap以及保留块来搜索对这个对象的引用,然后更新这个引用。

  这是一个相当消耗CPU资源的动作,幸运的是,GC使用了Card table来减小这个动作带来的系统资源消耗。Card Table缩小了在这个搜索遍历的过程中需要遍历的范围。

  Card table实际上是一块连续的内存区域,做为一个index,这块内存区域里面的每一个bit,都代表了GC Heap中的一块连续的区域,这些bits就组成了一个card table。

  CLR的实现中,Card Table中的一个bit就代表了GC heap里面的128byte的区域。同时,对Card Table的update的动作是以byte为单位的。这就造成了每一次对Card Table的Update,影响到的其实是128*8=1kb这么大的一个区域。

  在DotNet的中间层,但凡涉及到修改一个Object的Ref的CIL Opcodes, 不仅仅会执行份内的修改ref的事情,同时还会很小心的update这个Card Table。

  基于这种设计,在GC的时候,就可以先查找Card Table来看看哪些Object的Ref被修改了,然后只是针对这些区域去进行搜索遍历来Update GC Heap中其它的地方对这个对象的引用。

  这些Bit位的Update,可以由WriteBarrierHelper和ErectWriteBarrier 这两个方法来完成:

//update一个Card table的方法。void GCHeap::ErectWriteBarrier(OBJECTREF *dst, OBJECTREF ref)
{
  // 检查dst的地址是不是在heap之内的地址。  if (((*(BYTE**)&dst) < g_lowest_address) || ((*(BYTE**)&dst) >= g_highest_address))
    return;
if((BYTE*) OBJECTREFToObject(ref) >= g_ephemeral_low
&& (BYTE*) OBJECTREFToObject(ref) < g_ephemeral_high)
  {
    size_t card = gcard_of((BYTE*)dst);
    BYTE* pCardByte = ((BYTE*) g_card_table) + card / CARDS_PER_BYTE;
       //每一次update是以一个byte为单位的。    BYTE bitMask = (BYTE) (1 << (card % 8));
    if( !((*pCardByte) & bitMask) )
    {  
           //如果这个有ref被修改,那么这个card的一个byte里面都被置为1      *pCardByte = 0xFF;
    }
  }
}    

  同时,在update一个bit的时候,还使用了一种叫做wirte barrier的技术。这种技术,在程序修改一个ref的内容的时候,可以被编译器得知。这个技术在card update里面,具体到某个平台上面是一段汇编的代码,其实个人认为就是对CIL代码的一个扩展。

  Bricks和card差不多。它的最主要的作用,是Collector用来定位一个Object在Heap里面的位置。它的表示形式,是一个16位有符号int类型的数组。每一个单元叫做brick slot,cover2048byte的内存区域。对于每一个Brick slot,每一个16bit的成员都可以有三种表现形式:

  1.     十六位的正数表示偏移。

  2.     一个负数表示相对于Brick table本身的唯一。

  3.     保留值,做为一个标志位来使用。

  另外,这两种结构,都不能保证完全表现一个Heap里面的状态信息。



相关教程