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が消えて無くなった理由がわかりました。