欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android 虛擬機(jī)中的內(nèi)存分配與OOM問(wèn)題詳解

 更新時(shí)間:2022年09月02日 16:27:30   作者:Android社區(qū)  
這篇文章主要為大家介紹了Android 虛擬機(jī)中的內(nèi)存分配與OOM問(wèn)題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景知識(shí)

Android中每個(gè)App默認(rèn)情況下是運(yùn)行在一個(gè)獨(dú)立進(jìn)程中的, 而這個(gè)獨(dú)立進(jìn)程正是從Zygote孵化出來(lái)的VM進(jìn)程, 也就是說(shuō), 也就是說(shuō)每個(gè)Android APP在運(yùn)行時(shí)會(huì)啟動(dòng)一個(gè)Java虛擬機(jī)。

并且系統(tǒng)會(huì)給它分配固定的內(nèi)存空間(手機(jī)廠商會(huì)根據(jù)手機(jī)的配置情況來(lái)對(duì)其進(jìn)行調(diào)整)。

一、Android VM的內(nèi)存空間

Android是一個(gè)多任務(wù)系統(tǒng), 為了保證多任務(wù)的運(yùn)行, Android給每個(gè)App可使用的Heap大小設(shè)定了一個(gè)限定值.

這個(gè)值是系統(tǒng)設(shè)置的prop值, 保存在System/build.prop文件中. 一般國(guó)內(nèi)的手機(jī)廠商都會(huì)做修改, 根據(jù)手機(jī)配置不同而不同, 可以直接打開查看與修改。

其中和虛擬機(jī)內(nèi)存相關(guān)的主要有以下三個(gè):

1 . dalvik.vm.heapstartsize

– App啟動(dòng)后,系統(tǒng)分配給它的Heap初始大小,隨著App使用可增加。

2 . dalvik.vm.heapgrowthlimit

– 默認(rèn)情況下, App可使用的Heap的最大值, 超過(guò)這個(gè)值就會(huì)產(chǎn)生OOM.

3 . dalvik.vm.heapsize

– 如果App的manifest文件中配置了largeHeap屬性, 那么App可使用的Heap的最大值為此項(xiàng)設(shè)定值。

 <application
    android:largeHeap="true">
    ...
</application>

所以對(duì)于同一個(gè)手機(jī),不開啟largeHeap屬性時(shí)與多進(jìn)程時(shí),每個(gè)APP的虛擬機(jī)分配的內(nèi)存的上限都是heapgrowthlimit。

1.查看內(nèi)存的API

Android在ActivityManager類中提供了API可以運(yùn)行時(shí)獲取這些屬性值,如下:

//ActivityManager的getMemoryClass()獲得內(nèi)用正常情況下內(nèi)存的大小,即heapgrowthlimit的值
//ActivityManager的getLargeMemoryClass()可以獲得開啟largeHeap最大的內(nèi)存大小,即heapsize的指
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryClass();
activityManager.getLargeMemoryClass();

二、Android VM內(nèi)存分配流程

虛擬機(jī)分配內(nèi)存的具體源碼可以AOSP的Heap.cpp文件中查看:

/* Try as hard as possible to allocate some memory.
 */
static void *tryMalloc(size_t size)
{
    void *ptr;
//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();
    } else {
      /*
       * Try a foreground GC since a concurrent GC is not currently 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
    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;
}

具體流程如下:

  • 嘗試分配,如果成功則返回,失敗則轉(zhuǎn)入步驟2
  • 判斷是否gc正在進(jìn)行垃圾回收,如果正在進(jìn)行則等待回收完成之后,嘗試分配。如果成功則返回,失敗則轉(zhuǎn)入步驟3
  • 自己?jiǎn)?dòng)gc進(jìn)行垃圾回收,這里gcForMalloc的參數(shù)是false。所以不會(huì)回收軟引用,回收完成后嘗試分配,如果成功則返回,失敗則轉(zhuǎn)入步驟4
  • 調(diào)用dvmHeapSourceAllocAndGrow嘗試分配,這個(gè)函數(shù)會(huì)擴(kuò)張堆的大小,失敗轉(zhuǎn)入步驟5
  • 進(jìn)入回收軟引用階段,這里gcForMalloc的參數(shù)是ture,所以需要回收軟引用。然后再調(diào)用dvmHeapSourceAllocAndGrow嘗試分配,如果失敗則拋出OOM

小結(jié)

所以產(chǎn)生OOM時(shí),一定是java的堆中 已有的內(nèi)存 + 申請(qǐng)的內(nèi)存 >= heapgrowthlimit導(dǎo)致的,不會(huì)因?yàn)槭謾C(jī)目前物理內(nèi)存是否緊張而改變 - 當(dāng)物理內(nèi)存非常緊張時(shí)系統(tǒng)會(huì)通過(guò)LowMemory Killer殺掉一些低優(yōu)先級(jí)的進(jìn)程。

相應(yīng)的,物理內(nèi)存非常充足的情況也會(huì)有OOM的情況發(fā)生。

三、出現(xiàn)OOM的建議解決方案

當(dāng)APP出現(xiàn)OOM時(shí),建議可以從以下兩個(gè)方向來(lái)處理:

1 . 排查內(nèi)存泄露問(wèn)題

  • 排查各個(gè)功能是否內(nèi)存泄露情況,可以通過(guò)Android Studio中的MemoryMonitor功能進(jìn)行分析,Memory Monitor也集成了HPROF Viewer和Allocation Tracker可以分析內(nèi)存快照與內(nèi)存分配追蹤。另外推薦一個(gè)工具,square公司開源的leakcanary,非常簡(jiǎn)潔好用。

  • 排查進(jìn)程初始化時(shí)就直接申請(qǐng)并常駐內(nèi)存的對(duì)象以及其他功能里申請(qǐng)的static對(duì)象或者單例對(duì)象的必要性。

2 . 內(nèi)存優(yōu)化

按照谷歌在youtube上發(fā)布的性能優(yōu)化典范之內(nèi)存篇,優(yōu)化各功能的內(nèi)存,或可參照 胡凱的總結(jié) 。

大致有以下這些,具體請(qǐng)參見原文:

  • 謹(jǐn)慎使用large heap
  • 綜合考慮設(shè)備的內(nèi)存閾值與其他因素設(shè)計(jì)合適的緩存大小
  • onLowMemory與onTrimMemory
  • 資源文件需要選擇合適的文件夾進(jìn)行存放
  • Try catch某些大內(nèi)存分配的操作
  • 謹(jǐn)慎使用static對(duì)象
  • 特別留意單例對(duì)象中不合理的持有
  • 珍惜Services資源
  • 優(yōu)化布局層次,減少內(nèi)存消耗
  • 謹(jǐn)慎使用“抽象”編程
  • 使用nano protobufs序列化數(shù)據(jù)
  • 謹(jǐn)慎使用依賴注入框架
  • 謹(jǐn)慎使用多進(jìn)程
  • 使用ProGuard來(lái)剔除不需要的代碼
  • 謹(jǐn)慎使用第三方libraries

考慮不同的實(shí)現(xiàn)方式來(lái)優(yōu)化內(nèi)存占用

  • 注意Activity的泄露
  • 考慮使用Applicaiton Context代替Activity Context
  • 注意臨時(shí)Bitmap對(duì)象的及時(shí)回收
  • 注意監(jiān)聽器的注銷
  • 注意緩存容器里的對(duì)象泄露
  • 注意Webview的泄露

注意Cursor對(duì)象的及時(shí)關(guān)閉

  • 復(fù)用系統(tǒng)自帶的資源
  • ListView中對(duì)ConvertView的復(fù)用
  • Bitmap對(duì)象的復(fù)用
  • 避免在ondraw方法里執(zhí)行對(duì)象的創(chuàng)建

StringBuilder代替String

  • 使用更加輕量的數(shù)據(jù)結(jié)構(gòu)
  • 避免在Android里使用enum
  • 減少Bitmap對(duì)象的內(nèi)存占用

使用更小的圖片

  • 減少對(duì)象的內(nèi)存占用
  • 內(nèi)存對(duì)象的重復(fù)利用
  • 避免對(duì)象的內(nèi)存泄露
  • 內(nèi)存使用策略的優(yōu)化

以上就是Android 虛擬機(jī)中的內(nèi)存分配與OOM問(wèn)題詳解的詳細(xì)內(nèi)容,更多關(guān)于Android虛擬機(jī)內(nèi)存分配OOM的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論