Android內存機制分析下篇:分析APP內存使用情況
上面一篇文章說了Android應用運行在dalvik里面分配的堆和棧內存區別,以及程序中什么代碼會在哪里運行。今天主要是講解一下Android里面如何分析我們程序內存使用情況。以便后續可以分析我們程序瓶頸,優化方案。
1、APP默認分配內存大小
在Android里,程序內存被分為2部分:native和dalvik,dalvik就是我們普通的java使用內存,也就是我們上一篇文章分析堆棧的時候使用的內存。我們創建的對象是在這里面分配的,對于內存的限制是 native+dalvik 不能超過最大限制。android程序內存一般限制在16M,也有的是24M(早期的Android系統G1,就是只有16M)。具體看定制系統的設置,在Linux初始化代碼里面Init.c,可以查到到默認的內存大小。有興趣的朋友,可以分析一下虛擬機啟動相關代碼。這塊比較深入,目前我也沒時間去分析,后面有空會去鉆研一下。
- gDvm.heapSizeStart = 2 * 1024 * 1024; // heap初始化大小為2M
- gDvm.heapSizeMax = 16 * 1024 * 1024; // 最大的heap為16M
2、Android的GC如何回收內存
Android的一個應用程序的內存泄露對別的應用程序影響不大。為了能夠使得Android應用程序安全且快速的運行,Android的每個應用程序都會使用一個專有的Dalvik虛擬機實例來運行,它是由Zygote服務進程孵化出來的,也就是說每個應用程序都是在屬于自己的進程中運行的。Android為不同類型的進程分配了不同的內存使用上限,如果程序在運行過程中出現了內存泄漏的而造成應用進程使用的內存超過了這個上限,則會被系統視為內存泄漏,從而被kill掉,這使得僅僅自己的進程被kill掉,而不會影響其他進程(如果是system_process等系統進程出問題的話,則會引起系統重啟)。
做應用開發的時候,你需要了解系統的GC(垃圾回收)機制是如何運行的,Android里面使用有向圖作為遍歷回收內存的機制。Java將引用關系考慮為圖的有向邊,有向邊從引用者指向引用對象。線程對象可以作為有向圖的起始頂點,該圖就是從起始頂點開始的一棵樹,根頂點可以到達的對象都是有效對象,GC不會回收這些對象。如果某個對象 (連通子圖)與這個根頂點不可達(注意,該圖為有向圖),那么我們認為這個(這些)對象不再被引用,可以被GC回收。
因此對于我們已經不需要使用的對象,我們可以把它設置為null,這樣當GC運行的時候,就好遍歷到你這個對象已經沒有引用,會自動把該對象占用的內存回收。我們沒法像C++那樣馬上釋放不需要的內存,但是我們可以主動告訴系統,哪些內存可以回收了。
3、查看應用內存使用情況
下面我們看看如何在開發過程中查看我們程序運行時內存使用情況。我們可以通過ADB的一個命令查看:
- //$package_name:應用包名
- //$pid:應用進程ID,可以用PS命令查看
- adb shell dumpsys meminfo $package_name or $pid
上面是我使用包名查看Gallery例子的內存使用情況圖,里面信息很多,不過我們主要關注的是native和Davilk的使用情況。(Android2.X和Android4.X查看的信息排序是不一樣的,內容差不多,不過排布有差異,我上面是4.0的截圖)
Android底層內核是基于Linux的,而Linux里面相對Window來說,有一點很特別的是,會盡量使用系統內存加載一些緩存數據或者進程間共享數據。Linux本著不用白不用的原則,會盡量使用系統內存,加快我們應用的運行速度。當然,如果我們期待某個需要大內存的應用,系統也能馬上釋放出一定的內存使用,這是系統內部調度實現。因此嚴格來說,我們要準備計算Linux下某個進程內存大小比較困難。 因為有paging out to disk(換頁),所以如果你把所有映射到進程的內存相加,它可能大于你的內存的實際物理大小。
- dalvik:是指dalvik所使用的內存。
- native:是被native堆使用的內存。應該指使用C\C++在堆上分配的內存。
- other:是指除dalvik和native使用的內存。但是具體是指什么呢?至少包括在C\C++分配的非堆內存,比如分配在棧上的內存。puzlle!
- Pss:它是把共享內存根據一定比例分攤到共享它的各個進程來計算所得到進程使用內存。網上又說是比例分配共享庫占用的內存,也就是上面所說的進程共享問題。
- PrivateDirty:它是指非共享的,又不能換頁出去(can not be paged to disk )的內存的大小。比如Linux為了提高分配內存速度而緩沖的小對象,即使你的進程結束,該內存也不會釋放掉,它只是又重新回到緩沖中而已。
- SharedDirty:參照PrivateDirty我認為它應該是指共享的,又不能換頁出去(can not be paged to disk )的內存的大小。比如Linux為了提高分配內存速度而緩沖的小對象,即使所有共享它的進程結束,該內存也不會釋放掉,它只是又重新回到緩沖中而已。
上面針對meminfo里面的信息給出解析,這些很多我是參考了網上一些文章,所以如果有理解不到位的,歡迎各位指出。
4、程序中獲取內存信息
通過ActivityManager獲取相關信息,下面是一個例子代碼:
- private void displayBriefMemory()
- {
- final ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
- ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
- activityManager.getMemoryInfo(info);
- Log.i(tag,"系統剩余內存:"+(info.availMem >> 10)+"k");
- Log.i(tag,"系統是否處于低內存運行:"+info.lowMemory);
- Log.i(tag,"當系統剩余內存低于"+info.threshold+"時就看成低內存運行");
- }
另外通過Debug的getMemoryInfo(Debug.MemoryInfo memoryInfo)可以得到更加詳細的信息。跟我們在ADB Shell看到的信息一樣比較詳細。
5、總結
今天主要是分析了如何獲取我們應用的內存使用情況信息,關于這方面的信息,其實還有其他一些方法。另外還介紹APP應用的默認內存已經Android的GC回收,不過上面只是很淺薄地分析了一下,讓大家有個印象。這些東西真要深入分析得花不少精力。因為我們的目的只是解決OOM問題,所以目前沒打算深入分析,后面有時間進行Android系統分析的時候,我們再深入分析。下一次我們用以前寫的Gallery例子講解如何避免OOM問題,以及內存優化方法。
PS:如發現哪里寫錯了,煩請各位留言指出,謝謝!
相關文章: