MMgc: The Garbage Collector of Flash Player

MMgc是Flash Player中所使用的memory management garbage collector。MMgc不仅是用来回收ActionScript中的对象,它也用来管理Flash Player中的所有动态内存分配、释放请求,Flash Player VM的C++代码中也有很多对象是交给MMgc自动回收的。所以,从理论上来说,MMgc可以从Flash Player的代码中分离出来,作为一个单独的GC库,给所有C++程序用。

在MMgc中,堆上的对象被分为两种,Managed和UnManaged。Managed的就是交给GC去管理,用户只管new,不用delete,但是你也可以手动delete。UnManaged的就是传统的C++中的那些对象,必须手动的new/delete。

所有Managed对象必须从class GCObject(或其子类)继承。这个类增加了new和delete方法:

class GCObject{
       static void *operator new(size_t size, GC *gc, size_t extra) ;
       static void *operator new(size_t size, GC *gc) ;
       static void operator delete(void *gcObject);
};

假如一个类从GCObject继承,例如

class MyPoint2D : public MMgc::GCObject {
    int x;
    int y;
};

那么我们既可以像以前那样直接new:

MyPoint2D* p = new MyPoint2D(); //这样得到的对象是不受GC管理的。

也可以传递一个gc对象给new:

MyPoint2D* p = new (gc) MyPoint2D(); //这样得到的对象受GC管理。不需要delete

后面这种的语法看起来很像带placeholder的new,但其实不是。

从Managed对象指向Managed对象,要加一个write barrier ,叫DWB

class MyOtherManagedObject : public MMgc::GCObject { 
    DWB(MyPoint2D*) object; 
};

对于那些不受GC管理的对象,要么它跟GC一点关系都没有,如std::string。要么,如果它里面包含了指向Managed对象的指针,那么这个类必须从GCRoot继承:

class MyUnmanagedObject : public MMgc::GCRoot { 
    MyPoint2D * point; 
};

顾名思义,这些处于分界点上的对象,是GC的起点。执行GC的时候,从这些对象开始,挨个的mark-sweep。

但是仅仅这样还不够,如果某个成员变量的类型是指针,指针指向的是Managed对象,还得加一个write barrier 。即,上面的例子得改成:

class MyUnmanagedObject : public MMgc::GCRoot { 
    DRC(MyPoint2D) * point; 
};

对于其它类型的不用加:

class MyUnmanagedObject : public MMgc::GCRoot { 
    DRC(MyPoint2D) * point;  
    std::string* name; 
};

另外就是析构函数的问题。众所周知,JAVA这样的带GC的语言中,都是没有析构函数的。你要实在想用,有finalize方法。对此,MMgc的做法是,把基类从GCObject 改成GCFinalizedObject。

GCFinalizedObject还有一个子类叫RCObject。这个对象会带一个类型为uint32_t的字段做引用计数。一旦count到0,就立马free。这样的话,释放内存更为即时,从而降低GC的压力。但是,write barrier 得从DWB换成DRCWB。

并且,对于RCObject对象,必须在它的析构函数中把所有成员变量都清0。

所以,虽然使用了MMgc就避免了手动delete,但是也带来了很多用错的风险,如:

  1. 用错write barrier。DWB、DRCWB、DRC用错或漏写,或者不该加的地方加了。
  2. unmanaged object没有从GCRoot继承。

不过这些问题基本都是在编译期很容易发现的,通过检查类的定义就可以看出。

MMgc现在其实有两套实现,一套是conservative gc,一套是exact gc。conservative gc有一个问题是,它并不能准确的判断指针和整数。从而,如果有一个int恰好它的值等于某个对象的地址,这个对象就因此多了一个虚假的引用,从而一直不能被释放。但是Adobe的想法是,这不是一个大问题。因为这种事情出现的概率很随机,不至于因此而导致内存一直猛涨。

前面用到的DRC宏,其中DRC是Deferred Reference Counting的缩写。Deferred 的意思是说,并非到了0就立马清除。这是因为,GC管理的只是堆上的东西,如果栈上有一个指针指向了这个对象,那么GC并不知道。count==0的RCObject对象会被放到一个叫做Zero Count Table(ZCT)的表中,然后在适当的时候通过回溯整个栈的方式,找出引用关系,然后把没有被引用的清理掉。

Flash的vm已经开源,叫tamarin,由Mozilla基金会管理。它里面含有MMgc的全部代码。我试图把MMgc单独剥离出来,但是不大容易。因为MMgc不仅依赖于下面的OS适配层,还依赖于VM core里面的一些代码(String、Math、Date)等。去掉memory profiler也许会好点。

我后来想想,别的程序想用MMgc估计也不大容易,因为MMgc是单线程的。现在单线程的程序愈来愈少了。

此博客中的热门博文

在windows下使用llvm+clang

少写代码,多读别人写的代码

tensorflow distributed runtime初窥