Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android dalvik heap 淺析

android dalvik heap 淺析

編輯:關於Android編程

android 系統中可以在prop中配置dalvik堆的有關設定。具體設定由如下三個屬性來控制dalvik.vm.heapstartsize堆分配的初始大小,調整這個值會影響到應用的流暢性和整體ram消耗。這個值越小,系統ram消耗越慢,但是由於初始值較小,一些較大的應用需要擴張這個堆,從而引發gc和堆調整的策略,會應用反應更慢。

相反,這個值越大系統ram消耗越快,但是程序更流暢。 dalvik.vm.heapgrowthlimit受控情況下的極限堆(僅僅針對dalvik堆,不包括native堆)大小,dvm heap是可增長的,但是正常情況下dvm heap的大小是不會超過dalvik.vm.heapgrowthlimit的值(非正常情況下面會詳細說明)。這個值控制那些受控應用的極限堆大小,如果受控的應用dvm heap size超過該值,則將引發oom(out of memory)。 dalvik.vm.heapsize不受控情況下的極限堆大小,這個就是堆的最大值。不管它是不是受控的。這個值會影響非受控應用的dalvikheap size。一旦dalvik heap size超過這個值,直接引發oom。

用他們三者之間的關系做一個簡單的比喻:分配dalvik heap就好像去食堂打飯,有人飯量大,要吃三碗,有人飯量小,連一碗都吃不完。如果食堂按照三碗的標准來給每個人打飯,那絕對是鋪張浪費,所以食堂的策略就是先打一碗,湊合吃,不夠了自己再來加,設定堆大小也是一樣,先給一個合理值,湊合用,自己不夠了再跟系統要。食堂畢竟是做買賣的,如果很多人明顯吃不了那麼多,硬是一碗接著一碗。為了制止這種不合理的現象,食堂又定了一個策略,一般人就只能吃三碗。但是如果虎背熊腰的大漢確實有需要,可以吃上五碗,超過五碗就不給了(太虧本了)。

開始給一碗 對應 dalvik.vm.heapstartsize

一般人最多吃三碗 對應 dalvik.vm.heapgrowthlimit

虎背熊腰的大漢最多能吃五碗 對應 dalvik.vm.heapsize

在android開發中,如果要使用大堆。需要在manifest中指定android:largeHeap為true。

這樣dvm heap最大可達dalvik.vm.heapsize。其中分配過程,可以在heap.cpp裡粗略看出一些原理:

/* Try as hard as possible to allocate some memory.
 */
static void *tryMalloc(size_t size)
{
    void *ptr;

    /* Don't try too hard if there's no way the allocation is
     * going to succeed.  We have to collect SoftReferences before
     * throwing an OOME, though.
     */
    if (size >= gDvm.heapGrowthLimit) {
        LOGW("%zd byte allocation exceeds the %zd byte maximum heap size",
             size, gDvm.heapGrowthLimit);
        ptr = NULL;
        goto collect_soft_refs;
    }

//TODO: figure out better heuristics
//    There will be a lot of churn if someone allocates a bunch of
//    big objects in a row, and we hit the frag case each time.
//    A full GC for each.
//    Maybe we grow the heap in bigger leaps
//    Maybe we skip the GC if the size is large and we did one recently
//      (number of allocations ago) (watch for thread effects)
//    DeflateTest allocs a bunch of ~128k buffers w/in 0-5 allocs of each other
//      (or, at least, there are only 0-5 objects swept each time)

    ptr = dvmHeapSourceAlloc(size);
    if (ptr != NULL) {
        return ptr;
    }

    /*
     * The allocation failed.  If the GC is running, block until it
     * completes and retry.
     */
    if (gDvm.gcHeap->gcRunning) {
        /*
         * The GC is concurrently tracing the heap.  Release the heap
         * lock, wait for the GC to complete, and retrying allocating.
         */
        dvmWaitForConcurrentGcToComplete();
        ptr = dvmHeapSourceAlloc(size);
        if (ptr != NULL) {
            return ptr;
        }
    }
    /*
     * Another failure.  Our thread was starved or there may be too
     * many live objects.  Try a foreground GC.  This will have no
     * effect if the concurrent GC is already running.
     */
    gcForMalloc(false);
    ptr = dvmHeapSourceAlloc(size);
    if (ptr != NULL) {
        return ptr;
    }

    /* Even that didn't work;  this is an exceptional state.
     * Try harder, growing the heap if necessary.
     */
    ptr = dvmHeapSourceAllocAndGrow(size);
    if (ptr != NULL) {
        size_t newHeapSize;

        newHeapSize = dvmHeapSourceGetIdealFootprint();
//TODO: may want to grow a little bit more so that the amount of free
//      space is equal to the old free space + the utilization slop for
//      the new allocation.
        LOGI_HEAP("Grow heap (frag case) to "
                "%zu.%03zuMB for %zu-byte allocation",
                FRACTIONAL_MB(newHeapSize), size);
        return ptr;
    }

    /* Most allocations should have succeeded by now, so the heap
     * is really full, really fragmented, or the requested size is
     * really big.  Do another GC, collecting SoftReferences this
     * time.  The VM spec requires that all SoftReferences have
     * been collected and cleared before throwing an OOME.
     */
//TODO: wait for the finalizers from the previous GC to finish
collect_soft_refs:
    LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation",
            size);
    gcForMalloc(true);
    ptr = dvmHeapSourceAllocAndGrow(size);
    if (ptr != NULL) {
        return ptr;
    }
//TODO: maybe wait for finalizers and try one last time

    LOGE_HEAP("Out of memory on a %zd-byte allocation.", size);
//TODO: tell the HeapSource to dump its state
    dvmDumpThread(dvmThreadSelf(), false);

    return NULL;
}

這裡分為如下幾個動作

1 首先判斷一下需要申請的size是不是過大,如果申請的size超過了堆的最大限制,則轉入步驟6

2 嘗試分配,如果成功則返回,失敗則轉入步驟3

3 判斷是否gc正在進行垃圾回收,如果正在進行則等待回收完成之後,嘗試分配。如果成功則返回,失敗則轉入步驟4

4 自己啟動gc進行垃圾回收,這裡gcForMalloc的參數是false。所以不會回收軟引用,回收完成後嘗試分配,如果成功則返回,失敗則轉入步驟5

5 調用dvmHeapSourceAllocAndGrow嘗試分配,這個函數會擴張堆。所以heap startup的時候可以給一個比較小的初始堆,實在不夠用再調用它進行擴張

6 進入回收軟引用階段,這裡gcForMalloc的參數是ture,所以需要回收軟引用。然後調用dvmHeapSourceAllocAndGrow嘗試分配,如果失敗則拋出OOM

如果設置了largeHeap,具體流程從解析apk開始,源碼位於PackagePaser.java中,其中parseApplication函數負責解析apk。其中有一個小段代碼如下:

if (sa.getBoolean(  
              com.android.internal.R.styleable.AndroidManifestApplication_largeHeap,  
              false)) {  
          ai.flags |= ApplicationInfo.FLAG_LARGE_HEAP;  
      }  

如果解析到apk中設置了largeHeap,則在applicationinfo中添加FLAG_LARGE_HEAP標簽。之後會在ActivityThead.java中的handleBindApplication處理,這個函數非常重要,底層process fork好之後,會由這個函數把上層應用綁定過去。並且調用上層應用的入口點。其中處理largeHeap的代碼如下:

if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {  
            dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();  
        }  

這裡經過jni調用,最終回來到heapsource.cpp中的dvmClearGrowthLimit函數中:

/* 
 * Removes any growth limits.  Allows the user to allocate up to the 
 * maximum heap size. 
 */  
void dvmClearGrowthLimit()  
{  
    HS_BOILERPLATE();  
    dvmLockHeap();  
    dvmWaitForConcurrentGcToComplete();  
    gHs->growthLimit = gHs->maximumSize;  
    size_t overhead = oldHeapOverhead(gHs, false);  
    gHs->heaps[0].maximumSize = gHs->maximumSize - overhead;  
    gHs->heaps[0].limit = gHs->heaps[0].base + gHs->heaps[0].maximumSize;  
    dvmUnlockHeap();  
}  

這裡會把HeapSource的growthLimit設置為maximumSize,說簡單點就是把growthLimit有原來dalvik.vm.heapgrowthlimit的值調整為dalvik.vm.heapsize。不過分配的時候判斷oom的依據是根據heap中的maximumSize來決定。這裡不得不說一下HeapSource的兩個堆了,heaps[]數組中有兩個堆。簡單來講,0號堆是可用堆,是開發給上層使用的。1號堆是fork的時候從zygote進程直接復制過來的,這個是死的,不會由dvm開放給上層使用。overhead標明了堆中已經分配可多少(包括0號堆和1號堆)。所以上層能分配打的最大使用量為 gHs->maxmumSize - overhead。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved