freepeople性欧美熟妇, 色戒完整版无删减158分钟hd, 无码精品国产vα在线观看DVD, 丰满少妇伦精品无码专区在线观看,艾栗栗与纹身男宾馆3p50分钟,国产AV片在线观看,黑人与美女高潮,18岁女RAPPERDISSSUBS,国产手机在机看影片

正文內(nèi)容

dalvik虛擬機垃圾收集過程分析-資料下載頁

2025-06-25 06:24本頁面
  

【正文】 p = (u4 *)thread。 fp != NULL。 fp = (u4 *)saveAreaprevFrame) { Method *method。 saveArea = SAVEAREA_FROM_FP(fp)。 method = (Method *)saveAreamethod。 if (method != NULL amp。amp。 !dvmIsNativeMethod(method)) { const RegisterMap* pMap = dvmGetExpandedRegisterMap(method)。 const u1* regVector = NULL。 if (pMap != NULL) { ...... int addr = saveArea methodinsns。 regVector = dvmRegisterMapGetLine(pMap, addr)。 } if (regVector == NULL) { ...... for (size_t i = 0。 i methodregistersSize。 ++i) { if (dvmIsValidObject((Object *)fp[i])) { (*visitor)(amp。fp[i], threadId, ROOT_JAVA_FRAME, arg)。 } } } else { ...... u2 bits = 1 1。 for (size_t i = 0。 i methodregistersSize。 ++i) { bits = 1。 if (bits == 1) { ....... bits = *regVector++ | 0x0100。 } if ((bits amp。 0x1) != 0) { ...... (*visitor)(amp。fp[i], threadId, ROOT_JAVA_FRAME, arg)。 } } dvmReleaseRegisterMapLine(pMap, regVector)。 } } ...... } } 這個函數(shù)定義在文件dalvik/vm/alloc/。 在Dalvik虛擬機中,每一個Dalvik虛擬機線程都用一個Thread結(jié)構(gòu)體來描述。在Thread結(jié)構(gòu)體中,有一個成員變量interpSave,它指向的是一個InterpSaveState結(jié)構(gòu)體。在InterpSaveState結(jié)構(gòu)體中,又有一個成員變量curFrame,它指向線程的當(dāng)前調(diào)用棧幀,如圖2所示:圖2 Dalvik虛擬機線程的調(diào)用棧在圖2中,Dalvik虛擬機調(diào)用棧由高地址往低地址增長,即棧頂位于低地址。在每一個調(diào)用幀前面,有一個類型為StackSaveArea的結(jié)構(gòu)體。在StackSaveArea結(jié)構(gòu)體中,有四個重要的成員變量prevFrame、savedPc、method和currentPc。其中,prevFrame指向前一個調(diào)用幀,savedPc指向當(dāng)前函數(shù)調(diào)用完成后的返回地址,method指向當(dāng)前調(diào)用的函數(shù),currentPc指向當(dāng)前執(zhí)行的指令。通過成員變量prevFrame,就可以從當(dāng)前調(diào)用棧幀開始,遍歷整個調(diào)用棧。此外,通過成員變量method,可以知道一個調(diào)用棧幀對應(yīng)的調(diào)用函數(shù)是一個Java函數(shù)還是一個JNI函數(shù)。如果是一個JNI函數(shù),那么是不需要檢查它的棧幀的。因為JNI函數(shù)不會在里面保存Java對象。 那么在JNI函數(shù)里面創(chuàng)建或者訪問的對象是如何管理的呢?我們在JNI函數(shù)中需要創(chuàng)建或者訪問Java對象時,都必須要通過JNI接口來進行。JNI接口會把JNI函數(shù)創(chuàng)建或者訪問的Java對象保存線程相關(guān)的一個JNI Local Reference Table中。這些被JNI Local Reference Table引用的Java對象,在JNI函數(shù)調(diào)用結(jié)束的時候,會相應(yīng)地被自動釋放引用。但是有些情況下,我們需要在JNI函數(shù)中手動釋放被JNI Local Reference Table引用的Java對象,因為JNI Local Reference Table的大小是有限的。一旦一個JNI函數(shù)引用的Java對象數(shù)大于JNI Local Reference Table的容量,那么就會發(fā)生錯誤。這種情況特別容易發(fā)生循環(huán)語句中。 由于JNI函數(shù)里面創(chuàng)建或者訪問的對象保存在JNI Local Reference Table中,因此,在前面分析的函數(shù)visitThread中,需要對每一個Dalvik虛擬機線程的JNI Local Reference Table中的Java對象進行遍歷和標(biāo)記,避免它們在GC過程中被回收。 好了,我們現(xiàn)在將目光返回到Dalvik虛擬機線程的調(diào)用棧中,我們需要做的是遍歷那些非JNI函數(shù)調(diào)用,并且對位于棧幀里面的Java對象進行標(biāo)記,以免它們在GC過程中被回收。我們知道,Dalvik虛擬機指令是基于寄存器的。也就是說,無論函數(shù)里面的參數(shù),還是局部變量,都是保存在寄存器中。但是,我們需要注意的是。這些寄存器不是真實的硬件寄存器,而是虛擬出來的。那么是怎么虛擬出來的呢?其實就是將寄存器映射到調(diào)用棧幀對就的內(nèi)存塊上。因此,我們只需要遍歷調(diào)用棧幀對應(yīng)的內(nèi)存塊,就可以知道線程的調(diào)用棧都引用了哪些Java對象,從而可以對它們進行標(biāo)記。 現(xiàn)在,新的問題又出現(xiàn)了。調(diào)用棧幀的數(shù)據(jù)不一定都是Java對象引用,還有可能是一個原子類型,例如整數(shù)、布爾變量或者浮點數(shù)。在遍歷調(diào)用棧幀的時候,我們是怎么知道里面的一個數(shù)據(jù)到底是Java對象引用還是一個原子類型呢?我們可以想到的一個辦法是判斷它們值。如果它們的值是位于Java堆的范圍內(nèi),那么就認(rèn)為它們是Java對象引用。但是這樣做是不準(zhǔn)確的,因為有可能這是一個整數(shù)值,并且這個整數(shù)值剛好落在Java堆的地址范圍之內(nèi)。不過,我們寧愿將一個整數(shù)值誤認(rèn)為一個Java對象引用,也比漏掉一個Java對象引用好。因為將一個整數(shù)值誤認(rèn)為一個Java對象引用,導(dǎo)致的后果僅僅是垃圾不能及時回收,而漏掉一個Java對象引用,則意味著一個正在使用Java對象還在被引用的時候被回收。 上面描述的在調(diào)用棧幀中,大小值只要是落在Java堆的地址范圍之內(nèi)就認(rèn)為是Java對象引用的做法稱為保守GC算法,與此相對的稱為準(zhǔn)確GC。在準(zhǔn)確GC中,用一個稱為Register Map的數(shù)據(jù)結(jié)構(gòu)來輔助GC。Register Map記錄了一個Java函數(shù)所有可能的GC執(zhí)行點所對應(yīng)的寄存器使用情況。如果在某一個GC執(zhí)行點,某一個寄存器對應(yīng)的是一個Java對象引用,那么在對應(yīng)的Register Map中,就有一位被標(biāo)記為1。 從前面我們分析的函數(shù)dvmSuspendAllThreads可以知道,每當(dāng)一個Dalvik虛擬機線程被掛起等待GC時,它們總是掛起在IF、GOTO、SWITCH、RETURN和THROW等跳轉(zhuǎn)指令中。這些指令點對應(yīng)的實際上就GC執(zhí)行點。因此,我們可以在一個函數(shù)中針對出現(xiàn)上述指令的地方,均記錄函數(shù)當(dāng)前的寄存器使用情況,從而可以實現(xiàn)準(zhǔn)確GC。那么,這個工作是由誰去做的呢?我們知道,APK在安裝的時候,安裝服務(wù)會它們的DEX字節(jié)碼進行優(yōu)化,得到一個odex文件。實際上,APK在安裝的時候,它們的DEX字節(jié)碼除了會被優(yōu)化之外,還會被驗證和分析。驗證是為了保證不包含非法指令,而分析就是為了得到指令的執(zhí)行狀況,其中就包括得到每一個GC執(zhí)行點的寄存器使用情況,最終形成一個Register Map,并且保存在odex文件中。這樣,當(dāng)一個odex文件被加載使用的時候,我們就可以直接獲得一個函數(shù)在某一個GC執(zhí)行點的寄存器使用情況。 函數(shù)visitThreadStack在遍歷調(diào)用棧幀的過程中,通過調(diào)用函數(shù)dvmGetExpandedRegisterMap可以獲得當(dāng)前調(diào)用函數(shù)對應(yīng)的Register Map。有了這個Register Map之后,再通過在當(dāng)前StackSaveArea結(jié)構(gòu)體的成員變量currentPc的值,就可以獲得一個用來描述調(diào)用函數(shù)當(dāng)前寄存器使用情況的一個寄存器向量regVector。在獲得的寄存器向量regVector中,如果某一個位的值等于1,那么就說明對應(yīng)的寄存器保存的是一個Java對象引用。在這種情況下,就需要將該Java對象標(biāo)記為活的。 注意,函數(shù)visitThreadStack按照字節(jié)來訪問寄存器向量regVector。每次讀出1個字節(jié)的數(shù)據(jù)到變量bits的0~7位,然后再將變量bits的第8位設(shè)置為1。此后從低位開始,每訪問一位就將變量bits向右移一位。當(dāng)向左移夠8位后,就能保證變量bits的值變?yōu)?,這時候就再從寄存器向量regVector讀取下一字節(jié)進行遍歷。 由于Register Map不是強制的,因此有可能某些函數(shù)不存在對應(yīng)的Register Map,在這種情況下,就需要使用前面我們所分析的保守GC算法,遍歷調(diào)用棧幀的所有數(shù)據(jù),只要它們的值在Java堆的范圍之內(nèi),均認(rèn)為它們是Java對象引用,并且對它們進行標(biāo)記。 在前面分析的一系列函數(shù),都是通過調(diào)用函數(shù)rootMarkObjectVisitor來對對象進行標(biāo)記的,它的實現(xiàn)如下所示:[cpp] view plain copy 在CODE上查看代碼片派生到我的代碼片static void rootMarkObjectVisitor(void *addr, u4 thread, RootType type, void *arg) { ...... Object *obj = *(Object **)addr。 GcMarkContext *ctx = (GcMarkContext *)arg。 if (obj != NULL) { markObjectNonNull(obj, ctx, false)。 } } 這個函數(shù)定義在文件dalvik/vm/alloc/。 參數(shù)addr指向的是Java對象地址,arg指向一個描述當(dāng)前GC標(biāo)記上下文的GcMarkContext結(jié)構(gòu)體。函數(shù)rootMarkObjectVisitor通過調(diào)用另外一個函數(shù)markObjectNonNull來標(biāo)記參數(shù)addr所描述的Java對象。 函數(shù)markObjectNonNull的實現(xiàn)如下所示:[cpp] view plain copy 在CODE上查看代碼片派生到我的代碼片static void markObjectNonNull(const Object *obj, GcMarkContext *ctx, bool checkFinger) { ...... if (obj (Object *)ctximmuneLimit) { assert(isMarked(obj, ctx))。 return。 } if (!setAndReturnMarkBit(ctx, obj)) { /* This object was not previously marked. */ if (checkFinger amp。amp。 (void *)obj ctxfinger) { /* This object will need to go on the mark stack. */ markStackPush(amp。ctxstack, obj)。 } } } 這個函數(shù)定義在文件dalvik/vm/alloc/。 函數(shù)markObjectNonNull不僅在標(biāo)記根集對象的時候會調(diào)用到,在遞歸標(biāo)記被根集對象所引用的對象的時候也會調(diào)用到。在標(biāo)記根集對象調(diào)用時,參數(shù)checkFlinger的值等于false,而在遞歸
點擊復(fù)制文檔內(nèi)容
黨政相關(guān)相關(guān)推薦
文庫吧 www.dybbs8.com
備案圖鄂ICP備17016276號-1