日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 内存详细分析

發布時間:2023/12/19 Android 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android 内存详细分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載地址:http://blog.csdn.net/hnulwt/article/details/44900811


最近在網上看了不少Android內存管理方面的博文,但是文章大多都是就單個方面去介紹內存管理,沒有能全局把握,缺乏系統性闡述,而且有些觀點有誤。

這樣對Android內存管理進行局部性介紹,很難使讀者建立系統性概念,無法真正理解內存管理,對提高系統優化和系統穩定性分析方面的能力是不夠的。

? ? 我結合自己的一些思考和理解,從宏觀層面上,對內存管理做一個全局性的介紹,在此與大家交流分享。


首先,回顧一下基礎知識,基礎知識是理解系統機制的前提和關鍵:

1、? 進程的地址空間

在32位操作系統中,進程的地址空間為0到4GB,

示意圖如下:



?

圖1

?

這里主要說明一下Stack和Heap:

Stack空間(進棧和出棧)由操作系統控制,其中主要存儲函數地址、函數參數、局部變量等等,所以Stack空間不需要很大,一般為幾MB大小。

Heap空間的使用由程序員控制,程序員可以使用malloc、new、free、delete等函數調用來操作這片地址空間。Heap為程序完成各種復雜任務提供內存空間,所以空間比較大,一般為幾百MB到幾GB。正是因為Heap空間由程序員管理,所以容易出現使用不當導致嚴重問題。

?

2、進程內存空間和RAM之間的關系

進程的內存空間只是虛擬內存(或者叫作邏輯內存),而程序的運行需要的是實實在在的內存,即物理內存(RAM)。在必要時,操作系統會將程序運行中申請的內存(虛擬內存)映射到RAM,讓進程能夠使用物理內存。

RAM作為進程運行不可或缺的資源,對系統性能和穩定性有著決定性影響。另外,RAM的一部分被操作系統留作他用,比如顯存等等,內存映射和顯存等都是由操作系統控制,我們也不必過多地關注它,進程所操作的空間都是虛擬地址空間,無法直接操作RAM。

示意圖如下:


圖2

基礎知識介紹到這里,如果讀者理解以上知識有障礙,請好好惡補一下基礎知識,基礎理論知識至關重要。?

?

3、? Android中的進程

(1)???native進程:采用C/C++實現,不包含dalvik實例的進程,/system/bin/目錄下面的程序文件運行后都是以native進程形式存在的。如圖 ? ? ? ? ? 3,/system/bin/surfaceflinger、/system/bin/rild、procrank等就是native進程。

?

(2)???java進程:Android中運行于dalvik虛擬機之上的進程。dalvik虛擬機的宿主進程由fork()系統調用創建,所以每一個java進程都是存在于一個native進程中,因此,java進程的內存分配比native進程復雜,因為進程中存在一個虛擬機實例。如圖3,Android系統中的應用程序基本都是java進程,如桌面、電話、聯系人、狀態欄等等。




圖3

?

?

4、? Android中進程的堆內存

圖1和圖4分別介紹了native process和java process的結構,這個是我們程序員需要深刻理解的,進程空間中的heap空間是我們需要重點關注的。heap空間完全由程序員控制,我們使用的malloc、C++ new和java new所申請的空間都是heap空間, C/C++申請的內存空間在native heap中,而java申請的內存空間則在dalvik?heap中。




圖4

?

5、? Android的 java程序為什么容易出現OOM

這個是因為Android系統對dalvik的vm heapsize作了硬性限制,當java進程申請的java空間超過閾值時,就會拋出OOM異常(這個閾值可以是48M、24M、16M等,視機型而定),可以通過adb shell getprop | grep dalvik.vm.heapgrowthlimit查看此值。

也就是說,程序發生OMM并不表示RAM不足,而是因為程序申請的java heap對象超過了dalvik vm heapgrowthlimit。也就是說,在RAM充足的情況下,也可能發生OOM。

這樣的設計似乎有些不合理,但是Google為什么這樣做呢?這樣設計的目的是為了讓Android系統能同時讓比較多的進程常駐內存,這樣程序啟動時就不用每次都重新加載到內存,能夠給用戶更快的響應。迫使每個應用程序使用較小的內存,移動設備非常有限的RAM就能使比較多的app常駐其中。但是有一些大型應用程序是無法忍受vm heapgrowthlimit的限制的,后面會介紹如何讓自己的程序跳出vm heapgrowthlimit的限制。

?

6、? Android如何應對RAM不足

在第5點中提到:java程序發生OMM并不是表示RAM不足,如果RAM真的不足,會發生什么呢?這時Android的memory killer會起作用,當RAM所剩不多時,memory killer會殺死一些優先級比較低的進程來釋放物理內存,讓高優先級程序得到更多的內存。我們在分析log時,看到的進程被殺的log,如圖5,往往就是屬于這種情況。




圖5

7、? 如何查看RAM使用情況

可以使用adb shell cat /proc/meminfo查看RAM使用情況:

MemTotal:????????396708 kB

MemFree:???????????4088 kB

Buffers:???????????5212 kB

Cached:??????????211164 kB

SwapCached:???????????0 kB

Active:??????????165984 kB

Inactive:????????193084 kB

Active(anon):????145444 kB

Inactive(anon):?????248 kB

Active(file):?????20540 kB

Inactive(file):??192836 kB

Unevictable:???????2716 kB

Mlocked:??????????????0 kB

HighTotal:????????????0 kB

HighFree:?????????????0 kB

LowTotal:????????396708 kB

LowFree:???????????4088 kB

SwapTotal:????????????0 kB

SwapFree:?????????????0 kB

Dirty:????????????????0 kB

Writeback:????????????0 kB

AnonPages:???????145424 kB

……

……

這里對其中的一些字段進行解釋:

MemTotal:可以使用的RAM總和(小于實際RAM,操作系統預留了一部分)

MemFree:未使用的RAM

Cached:緩存(這個也是app可以申請到的內存)

HightTotal:RAM中地址高于860M的物理內存總和,只能被用戶空間的程序使用。

HightFree:RAM中地址高于860M的未使用內存

LowTotal:RAM中內核和用戶空間程序都可以使用的內存總和(對于512M的RAM: lowTotal= MemTotal)

LowFree: RAM中內核和用戶空間程序未使用的內存(對于512M的RAM: lowFree = MemFree)

?

8、? 如何查看進程的內存信息

(1)、使用adb shell dumpsys meminfo + packagename/pid:

從圖6可以看出,com.example.demo作為java進程有2個heap,native heap和dalvik heap,

native heap size為159508KB,dalvik heap size為46147KB



?

圖6?

?

?

(2)、使用adb shell procrank查看進程內存信息

???? ?? 如圖7:



圖7

?

解釋一些字段的意思:

VSS- Virtual Set Size 虛擬耗用內存(包含共享庫占用的內存)

RSS- Resident Set Size 實際使用物理內存(包含共享庫占用的內存)

PSS- Proportional Set Size 實際使用的物理內存(比例分配共享庫占用的內存)

USS- Unique Set Size 進程獨自占用的物理內存(不包含共享庫占用的內存)

一般來說內存占用大小有如下規律:VSS >= RSS >= PSS >= USS

?

注意:dumpsys meminfo可以查看native進程和java進程,而procrank只能查看java進程。

?

9、? 應用程序如何繞過dalvikvm heapsize的限制

對于一些大型的應用程序(比如游戲),內存使用會比較多,很容易超超出vm heapsize的限制,這時怎么保證程序不會因為OOM而崩潰呢?

(1)、創建子進程

?????????????? 創建一個新的進程,那么我們就可以把一些對象分配到新進程的heap上了,從而達到一個應用程序使用更多的內存的目的,當然,創建子進程會增加系統開銷,而且并不是所有應用程序都適合這樣做,視需求而定。

創建子進程的方法:使用android:process標簽

(2)、使用jni在native heap上申請空間(推薦使用)

?? ?? nativeheap的增長并不受dalvik vm heapsize的限制,從圖6可以看出這一點,它的native heap size已經遠遠超過了dalvik heap size的限制。

只要RAM有剩余空間,程序員可以一直在native heap上申請空間,當然如果 RAM快耗盡,memory killer會殺進程釋放RAM。大家使用一些軟件時,有時候會閃退,就可能是軟件在native層申請了比較多的內存導致的。比如,我就碰到過UC web在瀏覽內容比較多的網頁時閃退,原因就是其native heap增長到比較大的值,占用了大量的RAM,被memory killer殺掉了。

(3)、使用顯存(操作系統預留RAM的一部分作為顯存)

使用OpenGL texturesAPItexture memory不受dalvik vm heapsize限制,這個我沒有實踐過。再比如Android中的GraphicBufferAllocator申請的內存就是顯存。

?

10、Bitmap分配在native heap還是dalvik heap上?

一種流行的觀點是這樣的:

Bitmap是jni層創建的,所以它應該是分配到native heap上,并且為了解釋bitmap容易導致OOM,提出了這樣的觀點:

????????????? native heap size + dalvik heapsize <= dalvik vm heapsize

詳情請看:http://devspirit.blog.163.com/blog/static/16425531520104199512427/

?

但是請大家看看圖6,native heap size為159508KB,遠遠超過dalvik vm heapsize,所以,事實證明以上觀點是不正確的。

?

正確的觀點:

大家都知道,過多地創建bitmap會導致OOM異常,且native heapsize不受dalvik限制,所以可以得出結論:

Bitmap只能是分配在dalvik heap上的,因為只有這樣才能解釋bitmap容易導致OOM。

?

但是,有人可能會說,Bitmap確實是使用java native方法創建的啊,為什么會分配到dalvik heap中呢?為了解決這個疑問,我們還是分析一下源碼:

涉及的文件:

[java]?view plaincopy
  • 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方法的實現:

    [java]?view plaincopy
  • 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中。

    ?

    11、java程序如何才能創建native對象

    必須使用jni,而且應該用C語言的malloc或者C++的new關鍵字。實例代碼如下:

    [java]?view plaincopy
  • JNIEXPORT?void?JNICALLJava_com_example_demo_TestMemory_nativeMalloc(JNIEnv?*,?jobject)??
  • {??
  • ??????????
  • ?????????void?*?p=?malloc(1024*1024*50);??
  • ???
  • ?????????SLOGD("allocate50M?Bytes?memory");??
  • ???
  • ?????????if?(p?!=NULL)??
  • ?????????{?????????
  • ???????????????????//memorywill?not?used?without?calling?memset()??
  • ???????????????????memset(p,0,?1024*1024*50);??
  • ?????????}??
  • ?????????else??
  • ???????????????????SLOGE("mallocfailure.");??
  • ???….??
  • ???….??
  • free(p);?//free?memory??
  • }??
  • 或者:

    [java]?view plaincopy
  • JNIEXPORT?voidJNICALL?Java_com_example_demo_TestMemory_nativeMalloc(JNIEnv?*,?jobject)??
  • {??
  • ??????????
  • ?????????SLOGD("allocate?50M?Bytesmemory");??
  • ?????????char?*p?=?new?char[1024?*?1024?*?50];??
  • ?????????if?(p?!=?NULL)??
  • ?????????{?????????
  • ???????????????????//memory?will?not?usedwithout?calling?memset()??
  • ???????????????????memset(p,?1,?1024*1024*50);??
  • ?????????}??
  • ?????????else??
  • ??????????????????SLOGE("newobject?failure.");??
  • ?….??
  • ….??
  • free(p);?//free?memory??
  • }??

  • 這里對代碼中的memset做一點說明:

    ?????? new或者malloc申請的內存是虛擬內存,申請之后不會立即映射到物理內存,即不會占用RAM,只有調用memset使用內存后,虛擬內存才會真正映射到RAM。

    本文旨在讓大家對Android內存管理有一個整體性的認識,著重全局性理解,希望對大家有用


    總結

    以上是生活随笔為你收集整理的Android 内存详细分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。