天天看點

淺談 maxMemory , totalMemory , freeMemory 和 OOM 與 native Heap目錄:

作者:林冠宏 / 指尖下的幽靈
掘金: https://juejin.im/user/587f0dfe128fe100570ce2d8
部落格: http://www.cnblogs.com/linguanh/
GitHub : https://github.com/af913337456/
騰訊雲專欄: https://cloud.tencent.com/developer/user/1148436/activities

回答記憶體管理類面試問題可以說出下面這些内容,加分。

前言: 站在巨人的肩膀上,總結此文。

目錄:

  • Java runtime 三個計算記憶體函數
  • OOM 的說法,為什麼大型遊戲能申請那麼多記憶體?
  • 如何繞過dalvikvm heap size的限制 ?
  • Bitmap配置設定在native heap還是dalvik heap上?

1,Java runtime 三個計算記憶體函數:

maxMemory

擷取目前 APP 最大能夠申請的記憶體,在 Java Heap 部分。

totalMemory

擷取目前 APP 已經從系統拿到的記憶體,包含使用上了的和沒有用上的,因為一般申請會申請多一部分,它總是慢慢按需要從系統拿取。

freeMemory

擷取目前 APP 拿到的記憶體中,還沒用上的,即是可以被 gc 回收的。

計算此刻 APP 在 Java Heap 層次已經使用了的記憶體

usedMemory

usedMemory = totalMemory - freeMemory
           

2,OOM 的說法,為什麼大型遊戲能申請那麼多記憶體?

在不同的 Android 系統版本中,OOM 的判斷是不一樣的。

  • 通俗來說,OOM 是目前程序共申請的記憶體綜和超過一個限制,而被抛出。
  • 專業來說,Android為每個程序設定Dalvik Heap Size門檻值,這個門檻值在不同的裝置上會因為RAM大小不同而各有差異。如果APP想要配置設定的記憶體超過這個門檻值,就會發生OOM。
  • Android

    3.x以前

    ,Bitmap配置設定在Native heap中,而在

    3.x之後

    ,Bitmap配置設定在Dalvik或ART的Java heap中。
  • Android 2.x系統,當dalvik allocated + native allocated + 新配置設定的大小 >= dalvik heap 最大值時候就會發生OOM,也就是說在2.x系統中,考慮native heap對每個程序的記憶體限制。
  • Android 3.x系統,廢除了native的計數器,類似bitmap的配置設定改到dalvik的java heap中申請,隻要allocated + 新配置設定的記憶體 >= dalvik heap 最大值的時候就會發生OOM(art運作環境的統計規則還是和dalvik保持一緻),也就是說在3.x系統中,不考慮native heap對每個程序的記憶體限制,native heap隻會收到本機總記憶體(包括RAM以及SWAP區或分頁檔案)的限制。

這也是為什麼有些APP(比如大型遊戲)可以超過 Dalvik Heap Size 這個值?那是因為Java記憶體又分為Java Heap和Native Heap,3.X 後 Native Heap是不受該值限制的。像C/C++的記憶體都是在Native Heap中配置設定的。另外Bitmap是在Java Heap中配置設定的,我們開發過程中經常遇到由Bitmap引起的OOM,這就是一個例子。

3,如何繞過dalvikvm heap size的限制 ?

  • 建立子程序,上面說了,記憶體配置設定按程序來。再使用程序通訊

建立一個新的程序,那麼我們就可以把一些對象配置設定到新程序的heap上了,進而達到一個應用程式使用更多的記憶體的目的,當然,建立子程序會增加系統開銷,而且并不是所有應用程式都适合這樣做,視需求而定。

建立子程序的方法:使用android:process标簽

  • 按不同的系統版本,使用 jni 在native heap上申請空間(推薦使用)

3.X 後的系統 native heap的增長并不受dalvik vm heapsize的限制,隻要RAM有剩餘空間,程式員可以一直在native heap上申請空間,當然如果 RAM快耗盡,memory killer會殺程序釋放RAM。大家使用一些軟體時,有時候會閃退,就可能是軟體在native層申請了比較多的記憶體導緻的。比如,我就碰到過UC web在浏覽内容比較多的網頁時閃退,原因就是其native heap增長到比較大的值,占用了大量的RAM,被memory killer殺掉了。

  • 使用顯存(作業系統預留RAM的一部分作為顯存)

使用OpenGL textures等API,texture memory不受dalvik vm heapsize限制,這個我沒有實踐過。再比如Android中的GraphicBufferAllocator申請的記憶體就是顯存。

4,Bitmap配置設定在native heap還是dalvik heap上?

上面說了,不同的系統版本不同,那麼在 3.X 及其之後,為什麼在 java heap 而不是在 native heap 。請看下面源碼。

主要的檔案

framework/base/graphic/java/Android/graphics/BitmapFactory.java  
framework/base/core/jni/Android/graphics/BitmapFactory.cpp  
framework/base/core/jni/Android/graphics/Graphics.cpp             

BitmapFactory.java 裡面有幾個decode***方法用來建立bitmap,最終都會調用:

private staticnative Bitmap nativeDecodeStream(InputStream is, byte[] storage,Rect padding,Options opts);
           

nativeDecodeStream()

會調用到

BitmapFactory.cpp

中的

deDecode

方法,最終會調用到

Graphics.cpp

createBitmap

方法。

createBitmap方法的實作:

jobjectGraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,  
                                  boolisMutable, jbyteArray ninepatch, int density)  
{  
    SkASSERT(bitmap);  
    SkASSERT(bitmap->pixelRef());  
   
    jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,  
           static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)),  
            buffer, isMutable, ninepatch,density);  
    hasException(env); // For the side effectof logging.  
    return obj;  
}             

從代碼中可以看到

bitmap

對象是通過

env->NewOject(...)

建立的,到這裡疑惑就解開了,bitmap對象是虛拟機建立的,JNIEnv的NewOject方法傳回的是java對象,并不是native對象,是以它會配置設定到

dalvik heap

中。

如果您認為這篇文章還不錯或者有所收獲,您可以通過掃描一下下面的支付寶二維碼 打賞我一杯咖啡【物質支援】,也可以點選右下角的【推薦】按鈕【精神支援】,因為這兩種支援都是我繼續寫作,分享的最大動力

淺談 maxMemory , totalMemory , freeMemory 和 OOM 與 native Heap目錄:

繼續閱讀