コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

GC.hが消えた

Android-4.0.1_r1のdalvikのalloc周りを見ていて、ずいぶんスッキリしたなと思っていたのですが、GC.hまで無くなっているではないかいな。これはまたえらくスッキリだな!いったいどこへ行ってしまったの?
ということで調べてみました。

GC.h

v2.3以前では、下のようなGC.hがありました。当然、宣言されているdvmGcScanRootClassLoader()などの関数も存在していました。

//GC.h
void dvmCollectGarbage(bool collectSoftRefs);
void dvmMarkObjectNonNull(const Object *obj);

#define dvmMarkObject(obj) \
    do { \
        Object *DMO_obj_ = (Object *)(obj); \
        if (DMO_obj_ != NULL) { \
            dvmMarkObjectNonNull(DMO_obj_); \
        } \
    } while (false)

#define dvmMarkIfObject(obj) \
    do { \
        Object *DMIO_obj_ = (Object *)(obj); \
        if (DMIO_obj_ != NULL && dvmIsValidObject(DMIO_obj_)) { \
            dvmMarkObjectNonNull(DMIO_obj_); \
        } \
    } while (false)
void dvmGcScanRootClassLoader(void);
void dvmGcScanRootThreadGroups(void);
void dvmGcScanInternedStrings(void);
void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *));
void dvmGcScanPrimitiveClasses(void);
void dvmGcMarkJniGlobalRefs(void);
void dvmGcMarkDebuggerRefs(void);

#if WITH_HPROF && !defined(_DALVIK_HPROF_HPROF)
#include "hprof/Hprof.h"
#define HPROF_SET_GC_SCAN_STATE(tag_, thread_) \
    dvmHeapSetHprofGcScanState((tag_), (thread_))
#define HPROF_CLEAR_GC_SCAN_STATE() \
    dvmHeapSetHprofGcScanState(0, 0)
#else
#define HPROF_SET_GC_SCAN_STATE(tag_, thread_)  do {} while (false)
#define HPROF_CLEAR_GC_SCAN_STATE()  do {} while (false)
#endif

ところが、v4.0.1ではきれいさっぱりなくなっています。GC.hで宣言されている関数は何に使われていたかというと、見てわかるようにDalvikがMark&SweepするときにそれぞれのオブジェクトのRootを検索してmarkbitsにマークする処理をしていました。見てもわからんね(^^;。

dvmHeapMarkRootSet()

dvmCollectGarbageInternal()を見ると、dvmHeapMarkRootSet()はあります。

 /* Mark the set of objects that are strongly reachable from the roots.
  */
  LOGD_HEAP("Marking...");
  dvmHeapMarkRootSet();

dvmHeapMarkRootSet()の中を見ると、おぉー、めちゃめちゃスッキリしています。v2.3のdvmHeapMarkRootSet()と見比べてみるとスッキリ具合が良く分かると思います。

void dvmHeapMarkRootSet()
{
    GcHeap *gcHeap = gDvm.gcHeap;
    dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
    dvmVisitRoots(rootMarkObjectVisitor, &gcHeap->markContext);
}

VisitにRootを探索用のdvmVisitRoots()を作ったようですね。Visitを見てみると、なるほど、GC.hで宣言して使っていた関数をVisitに持って行って、#defineしていたdvmMarkObject()はコルーチンで回すようにしたのですね。

/*
 * Visits roots.  TODO: visit cached global references.
 */
void dvmVisitRoots(RootVisitor *visitor, void *arg)
{
    assert(visitor != NULL);
    visitHashTable(visitor, gDvm.loadedClasses, ROOT_STICKY_CLASS, arg);
    visitPrimitiveTypes(visitor, arg);
    if (gDvm.dbgRegistry != NULL) {
        visitHashTable(visitor, gDvm.dbgRegistry, ROOT_DEBUGGER, arg);
    }
    if (gDvm.literalStrings != NULL) {
        visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg);
    }
    dvmLockMutex(&gDvm.jniGlobalRefLock);
    visitIndirectRefTable(visitor, &gDvm.jniGlobalRefTable, 0, ROOT_JNI_GLOBAL, arg);
    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
    dvmLockMutex(&gDvm.jniPinRefLock);
    visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg);
    dvmUnlockMutex(&gDvm.jniPinRefLock);
    visitThreads(visitor, arg);
    (*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
    (*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
    (*visitor)(&gDvm.noClassDefFoundErrorObj, 0, ROOT_VM_INTERNAL, arg);
}

Globals.hを見ると、classのタイプを全部 HashTable* に宣言していますね。ちなみに、Primitive Typeも下のように全て探索しています。

static void visitPrimitiveTypes(RootVisitor *visitor, void *arg)
{
    (*visitor)(&gDvm.typeVoid, 0, ROOT_STICKY_CLASS, arg);
    (*visitor)(&gDvm.typeBoolean, 0, ROOT_STICKY_CLASS, arg);
    (*visitor)(&gDvm.typeByte, 0, ROOT_STICKY_CLASS, arg);
    (*visitor)(&gDvm.typeShort, 0, ROOT_STICKY_CLASS, arg);
    (*visitor)(&gDvm.typeChar, 0, ROOT_STICKY_CLASS, arg);
    (*visitor)(&gDvm.typeInt, 0, ROOT_STICKY_CLASS, arg);
    (*visitor)(&gDvm.typeLong, 0, ROOT_STICKY_CLASS, arg);
    (*visitor)(&gDvm.typeFloat, 0, ROOT_STICKY_CLASS, arg);
    (*visitor)(&gDvm.typeDouble, 0, ROOT_STICKY_CLASS, arg);
}

RootのタイプはVisit.hで下記のように宣言されていました。

enum RootType {
  ROOT_UNKNOWN = 0,
  ROOT_JNI_GLOBAL,
  ROOT_JNI_LOCAL,
  ROOT_JAVA_FRAME,
  ROOT_NATIVE_STACK,
  ROOT_STICKY_CLASS,
  ROOT_THREAD_BLOCK,
  ROOT_MONITOR_USED,
  ROOT_THREAD_OBJECT,
  ROOT_INTERNED_STRING,
  ROOT_DEBUGGER,
  ROOT_VM_INTERNAL,
  ROOT_JNI_MONITOR,
};

全てHash Tableを使って探索するように変更したようです。元々、v2.3でConcurrentGCとしてgcが走ったときに、Rootから参照オブジェクトをたどっているときに、スレッドを止めていないので参照切れが起きていないかどうか、もう一度Rootから探索するのに使われていましたが、初期Root探索にも拡張したのですね。
 これで、GC.hが消えて無くなった理由がわかりました。

おまけ