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


2022-07-13 14:26:26




