宋平波,李云,楊豪杰
(中國電信股份有限公司廣東研究院,廣東 廣州 510630)
Android內(nèi)存管理機制研究
宋平波,李云,楊豪杰
(中國電信股份有限公司廣東研究院,廣東 廣州 510630)
本文深入分析了Android操作系統(tǒng)的內(nèi)存管理機制以及內(nèi)存回收機制,并針對Android應(yīng)用開發(fā)者提出了有效可實施的建議。
Android 內(nèi)存管理 內(nèi)存回收
伴隨國內(nèi)4G的啟動以及各大運營商移動互聯(lián)網(wǎng)流量經(jīng)營的不斷深入,移動互聯(lián)網(wǎng)應(yīng)用將再次迎來飛速增長,移動APP將進一步滲入用戶的基本生活,中國電信也在移動互聯(lián)網(wǎng)應(yīng)用方面做了大量研發(fā)工作。鑒于移動終端操作系統(tǒng)在資源分配、管理以及回收上對應(yīng)用有著更為嚴格的限制,為了能夠使移動APP高效穩(wěn)定的運行,APP開發(fā)人員有必要深入了解移動終端操作系統(tǒng)的內(nèi)存管理機制,并根據(jù)操作系統(tǒng)的限制,合理使用內(nèi)存空間,避免被操作系統(tǒng)強殺,提高APP的可靠性。
2.1 Android與Linux
Android系統(tǒng)是Google在Linux內(nèi)核基礎(chǔ)上開發(fā),針對移動設(shè)備進行了特殊優(yōu)化的開源操作系統(tǒng)。特別是在內(nèi)存管理機制上,針對移動終端資源特點及移動APP應(yīng)用特點進行了優(yōu)化,當(dāng)進程活動停止后,Android不會立刻結(jié)束這個進程,而是將進程保留在內(nèi)存中,當(dāng)用戶嘗試再次激活此進程時,可以直接復(fù)用內(nèi)存中的數(shù)據(jù)從而提升進程的啟動速度,當(dāng)系統(tǒng)需要更多內(nèi)存時,Android才會將這些進程占用的內(nèi)存釋放。
同樣,繼承自Linux的Android操作系統(tǒng)也可以通過系統(tǒng)命令“cat/proc/meminfo”查看內(nèi)存使用情況,如圖1所示。其中,MemTotal:總的可用內(nèi)存;MemFree:未被分配空閑的內(nèi)存;Buffers:buffer的大??;Cached:cache的大小;SwapCached:swap緩存的大小,Android很少使用swap,經(jīng)常為0。
2.2 Android進程回收(LMK)
(1)進程回收優(yōu)先級
1)進程優(yōu)先級及oom_adj
Android是一個多任務(wù)系統(tǒng),即可以同時運行多個程序。操作系統(tǒng)啟動運行一個程序是需要一定系統(tǒng)開銷(CPU、時間等)的。因此,如在2.1節(jié)中提到,為了應(yīng)用再次啟動時的啟動速度,當(dāng)用戶退出一個程序后,Android操作系統(tǒng)仍然會將這個程序保留在內(nèi)存中,當(dāng)程序再次被啟動時就可以快速啟動。當(dāng)系統(tǒng)內(nèi)存出現(xiàn)不足時,Android系統(tǒng)會按照一定的優(yōu)先級,對運行中的程序進行回收,以釋放內(nèi)存。
圖1 Android手機內(nèi)存使用情況
如圖2所示,Android操作系統(tǒng)將運行中的進程分為6類,并在內(nèi)存不足時按照空進程(EMPTY_APP)、內(nèi)容供應(yīng)節(jié)點(CONTENT_PROVIDER)、后臺進程(HIDDEN_APP)、次要服務(wù)進程(SECONDARY_ SERVER)、可視進程(VISIBLE_APP)和前臺進程(FOREGROUND_APP)的順序,逐一對進程進行回收。
圖2 Android進程分類
Android操作系統(tǒng)為每個進程維護一個oom_adj值,一進程的oom_adj值也就代表了它的優(yōu)先級,oom_adj值越高代表該進程優(yōu)先級越低。各進程含義及oom_adj值如下:
◆前臺進程(FOREGROUND)oom_adj:0
用戶在屏幕上可見的應(yīng)用程序進程,同時也包括(可能不可見的)系統(tǒng)進程或電話服務(wù)進程。最為直接的例子就是用戶正在使用的應(yīng)用程序進程就是前臺進程,而前臺進程的優(yōu)先級最高,也最不容易被系統(tǒng)回收。
◆可見進程(VISIBLE)oom_adj:1
應(yīng)用程序仍然可見,但是已經(jīng)不是前臺進程的應(yīng)用進程。比如:當(dāng)某個前臺應(yīng)用程序被另外一個應(yīng)用程序的通知遮蓋了一部分(通知通常在屏幕正中,但周邊仍然能見到原來的應(yīng)用程序),這時此應(yīng)用程序進程由前臺進程轉(zhuǎn)為了可見進程??梢娺M程的優(yōu)先級仍然較高,僅次于前臺進程。
◆次要服務(wù)(SECONDARY SERVER)進程oom_adj:2
次要服務(wù)指的是系統(tǒng)后臺正在運行的服務(wù)進程(包括系統(tǒng)桌面、UI等服務(wù),但電話、系統(tǒng)底層服務(wù)等主要服務(wù)不在此列,因此命名為次要服務(wù))。后臺運行(不可見)的包含服務(wù)的應(yīng)用進程是次要服務(wù)的主要組成部分,次要服務(wù)的優(yōu)先級低于可見進程。
◆后臺進程(HIDDEN)oom_adj:7
后臺進程指的是不可見的且不包含服務(wù),但仍在運行的應(yīng)用進程。當(dāng)用戶返回桌面時,之前用戶正在使用的應(yīng)用程序如果不包含服務(wù),則此應(yīng)用程序變?yōu)楹笈_進程。
◆內(nèi)容供應(yīng)節(jié)點(CONTENT PROVIDER)oom_adj:14
內(nèi)容供應(yīng)節(jié)點指為其他應(yīng)用提供數(shù)據(jù)內(nèi)容的進程,聯(lián)系人、日歷等進程屬于內(nèi)容供應(yīng)節(jié)點。
◆空進程(EMPTY)oom_adj:15
空進程指在后臺處于掛起狀態(tài)的應(yīng)用進程,沒有任何代碼在執(zhí)行。空進程的優(yōu)先級最低,在LMK工作機制中,屬于最先被回收的類別。
2)進程回收時機
除了上述進程優(yōu)先級之外,Android系統(tǒng)還維護著另外一張閥值對應(yīng)表(終端廠家根據(jù)每個型號的手機按照配置和Android版本不同,對具體的閥值做調(diào)整),如表1所示(以Google Nexus One為例):
表1 oom_adj內(nèi)存閥值表
這個表定義了一個對應(yīng)關(guān)系,每一個閥值對應(yīng)了一個進程優(yōu)先級(oom_adj),當(dāng)系統(tǒng)的可用內(nèi)存低于某個閥值時,就回收大于該閥值對應(yīng)的進程優(yōu)先級(oom_ adj)的進程。比如,當(dāng)可用內(nèi)存小于6 144*4k=24MB時,開始回收EMPTY_APP進程,直至大于24MB;同理當(dāng)可用內(nèi)存小于5 632*4k=22MB時,開始回收CONTENT_PROVIDER以及EMPTY_APP進程。
3)操作系統(tǒng)設(shè)置及修改
操作系統(tǒng)在init.rc文件中分別定義了各個進程的優(yōu)先級以及內(nèi)存閥值,如下方所示:
# Define the oom_adj values for the classes of processes
# that can be killed by the kernel
setprop ro.FOREGROUND_APP_ADJ 0
setprop ro.VISIBLE_APP_ADJ 1
setprop ro.SECONDARY_SERVER_ADJ 2
setprop ro.HIDDEN_APP_ADJ 7
setprop ro.CONTENT_PROVIDER_ADJ 14
setprop ro.EMPTY_APP_ADJ 15
# Define the memory thresholds at which the above process
# classes will be killed. In pages (4k)
setprop ro.FOREGROUND_APP_MEM 1536
setprop ro.VISIBLE_APP_MEM 2048
setprop ro.SECONDARY_SERVER_MEM 4096
setprop ro.HIDDEN_APP_MEM 5120
setprop ro.CONTENT_PROVIDER_MEM 5632
setprop ro.EMPTY_APP_MEM 6144
同時,也可以通過修改/sys/module/lowmemory killer/parameters/minfree配置文件來修改各級進程回收的內(nèi)存閥值,比如通過執(zhí)行“echo “1836,2048,40 96,5120,5360,8192”>/sys/module/lowmemorykiller/ parameters/minfree”,可以將操作系統(tǒng)回收EMPTY_ APP進程的內(nèi)存閥值修改為32M。
(2)進程回收(Low Memory Killer,LMK)
在(1)小節(jié)中了解到,即使當(dāng)用戶退出應(yīng)用程序之后,應(yīng)用程序的進程也還是存在于系統(tǒng)中,這樣是為了方便程序的再次啟動,但若此,隨著打開的程序數(shù)量的增加,系統(tǒng)的內(nèi)存會變得不足,就需要殺掉一部分進程以釋放內(nèi)存空間,至于是否需要殺死一些進程和哪些進程需要被殺死,是通過Low Memory Killer(LMK)機制來進行判定的。
Android的Low Memory Killer(LMK)基于Linux的OOM機制,在Linux中,內(nèi)存是以頁面為單位進行分配的,當(dāng)申請頁面分配時如果內(nèi)存不足會通過以下流程選擇bad進程來殺掉從而釋放內(nèi)存。
在Low Memory Killer中通過進程的oom_adj與占用內(nèi)存的大小決定要殺死的進程,oom_adj越小越不容易被殺死。參考Andoird源碼中l(wèi)owmemorykiller.c的lowmem_shrink函數(shù),計算哪些進程該回收,并發(fā)送SIGKILL信號將該進程殺死。
3.1 操作系統(tǒng)限制
絕大多數(shù)Android應(yīng)用程序本質(zhì)上是一個java進程,運行在Linux內(nèi)核之上的dalvik(可以理解為Android的JVM)虛擬機中,每個Android應(yīng)用的虛擬機均為各自獨立的,因此Android又類似于Java的內(nèi)存管理。
每個虛擬機所能使用的內(nèi)存按照Android版本以及手機配置各有不同,可以通過下面的系統(tǒng)命令查看每臺手機上每個應(yīng)用能使用內(nèi)存數(shù)量的限制:
getprop | grep dalvik
[dalvik.vm.heapgrowthlimit]: [64m]
[dalvik.vm.heapsize]: [128m]
3.2 垃圾收集GC(Garbage Collector)
Java語言引入了垃圾收集機制,程序員不需要像在C/C++中一樣,手工釋放不需要的對象或內(nèi)存區(qū)域,Java虛擬機可以跟蹤內(nèi)存中的所有對象并可以發(fā)現(xiàn)和回收不再使用的對象。從而極大地減少了內(nèi)存泄漏發(fā)生的可能性。
垃圾收集算法的核心思想是:虛擬機對內(nèi)存中的對象進行監(jiān)控,當(dāng)對象不再被使用(引用數(shù)降為0)時,將對象標為垃圾對象,并在合適的時候?qū)ο筢尫挪⒒厥掌湔加玫膬?nèi)存空間。垃圾回收對于系統(tǒng)性能有著較高的影響,因此開發(fā)人員需要對垃圾回收的算法、回收時機以及相關(guān)系統(tǒng)參數(shù)做深入的了解
3.3 位圖(Bitmap)資源
客戶端應(yīng)用為了良好的視覺體驗,經(jīng)常使用大量的圖片進行美化,圖片在內(nèi)存中以位圖(Bitmap)對象來存放。使用Bitmap對象,需要緊記以下2點:
(1)Bitmap對象占內(nèi)存量僅與圖片的長寬像素數(shù)有關(guān),與原圖的圖片格式或壓縮比例無關(guān)。因此,壓縮JPG圖片的質(zhì)量,而不減少圖片尺寸,是無法降低該圖片占用的內(nèi)存量的。
(2)根據(jù)Android SDK文檔:“Bitmap data is not allocated in the VM heap. There is a reference to it in the VM heap (which is small), but the actual data is allocated in the Native heap by the underlying Skia graphics library … The Nat?ve heap is shared between running applications, so the amount of free space depends on what other applications are running and their bitmap usage …”,可知Bitmap對象的數(shù)據(jù)并非存放在VM Heap中,而是存放在Native Heap中,而Native Heap的空間是與其他應(yīng)用程序共享的,因此即使應(yīng)用程序申請的內(nèi)存在3.1小節(jié)所述限額之內(nèi),也有可能申請不到內(nèi)存并產(chǎn)生OutOfMemoryError錯誤。
在第2和第3部分中,描述了Android操作系統(tǒng)的內(nèi)存管理機制以及Android操作系統(tǒng)對于Android應(yīng)用的各種限制,為了讓應(yīng)用能夠更加穩(wěn)定地運行在Android操作系統(tǒng)之上,應(yīng)用開發(fā)者需要根據(jù)Android操作系統(tǒng)的特性,有針對性地優(yōu)化自己的應(yīng)用。
4.1 針對LMK機制
首先需要明確一點,沒有任何辦法能夠保證第三方應(yīng)用不被LMK機制回收。但當(dāng)應(yīng)用不希望被操作系統(tǒng)LMK機制主動回收時,建議采用一些優(yōu)化機制:
(1)盡量提高進程的優(yōu)先級。在應(yīng)用中實現(xiàn)一個Service,可以將進程變?yōu)榇我?wù)(SECONDARY SERVER),在LMK機制中擁有較高的優(yōu)先級,可以降低LMK命中的幾率。
(2)應(yīng)用進入后臺運行時,盡量在應(yīng)用中保留一個Activity(界面),因為在相同優(yōu)先級的情況下,所有Activity均已關(guān)閉的進程會優(yōu)先被回收。
(3)依賴于其他優(yōu)先級高的進程。當(dāng)一個進程被其他更高優(yōu)先級的進程所依賴或者為其他更高優(yōu)先級進程提供服務(wù)時,系統(tǒng)會讓此進程享有與高優(yōu)先級進程同樣的優(yōu)先級。例如:當(dāng)某個通信錄應(yīng)用正在向另外一個前臺進程(通過CONTENT PROVIDER提供)提供通信錄內(nèi)容時,即使這個通信錄應(yīng)用沒有任何UI在屏幕上顯示、所有Activity也已經(jīng)關(guān)閉、且CONTENT PROVIDER進程的優(yōu)先級不高,操作系統(tǒng)仍然不會回收這個通信錄應(yīng)用,因為此時通信錄應(yīng)用享有與前臺進程相同的優(yōu)先級。
4.2 針對GC機制
根據(jù)3.2小節(jié)中對GC機制的描述,不良的代碼會導(dǎo)致GC機制對程序性能產(chǎn)生影響,并有可能導(dǎo)致內(nèi)存不足等。因此程序員有必要在了解GC機制的基礎(chǔ)上,針對GC特點進行編碼,具體包括:
(1)盡量不使用靜態(tài)變量
作為全局變量,靜態(tài)變量會一直占用內(nèi)存,不會被GC回收,有可能導(dǎo)致應(yīng)用內(nèi)存不足。
(2)盡快釋放不使用的對象
盡快將不使用的對象設(shè)為null,可以使得該對象盡快被GC回收,提高了GC的效率。
(3)避免集中創(chuàng)建或者釋放大量的對象
在短時間內(nèi)大量創(chuàng)建對象,或者大量申請內(nèi)存空間,會導(dǎo)致JVM進行GC以回收內(nèi)存來應(yīng)對應(yīng)用的需求。而大量的GC請求會使得應(yīng)用運行遲緩,導(dǎo)致應(yīng)用用戶體驗下降。
(4)采用StringBuilder處理字符串累加
String是固定長的字符串對象,因此累加String對象會導(dǎo)致String對象的頻繁創(chuàng)建,盡量采用StringBuilder來處理字符串累加。
(5)盡量使用基本類型變量
基本類型變量比對象變量占用內(nèi)存更小,因此應(yīng)該盡量使用基本類型變量。
4.3 針對位圖(Bitmap)資源
由3.3小節(jié)可知,Android中存儲Bitmap數(shù)據(jù)的內(nèi)存區(qū)域是有限的,而且需要與其他應(yīng)用程序共享,即使申請的內(nèi)存總量仍在虛擬機的限額之內(nèi),也可能因為其他應(yīng)用產(chǎn)生了過多Bitmap數(shù)據(jù)而導(dǎo)致內(nèi)存不足,因此小心地使用Bitmap對象,不僅可以使自己的應(yīng)用程序更加穩(wěn)定,也會使得在Android運行環(huán)境上執(zhí)行的其他應(yīng)用程序更加穩(wěn)定。
(1)及時回收
當(dāng)應(yīng)用不再使用某個Bitmap對象時,盡量不要等待系統(tǒng)自動釋放,應(yīng)該盡快調(diào)用“recycle()”釋放Bitmap占用的內(nèi)存空間。
(2)捕獲異常
在產(chǎn)生Bitmap對象的地方捕捉OutOfMemoryError錯誤,能夠有效地減少因為內(nèi)存不足而造成的應(yīng)用程序崩潰。
(3)同樣的圖片避免生成多個Bitmap對象
很多情況下,可能需要在一個Activity里多次用到同一張圖片(如好友列表中的默認頭像等)。此時應(yīng)該盡量使用同一個Bitmap對象,不應(yīng)新建多個Bitmap對象,避免內(nèi)存的浪費。
(4)盡量避免使用大圖片
Bitmap對象占用內(nèi)存的空間=圖片長邊像素數(shù)*圖片寬邊像素數(shù)*顏色深度(也就是每位像素占用的內(nèi)存數(shù))??梢夿itmap對象占用的內(nèi)存空間與圖片的像素成正比,當(dāng)圖片太大時,很容易引發(fā)內(nèi)存不足。根據(jù)圖片顯示區(qū)域的大小,使用BitmapFactory.Options設(shè)置inSampleSize縮小圖片,可以有效地減少大圖片對內(nèi)存的影響。
移動互聯(lián)網(wǎng)正在飛速發(fā)展,Android設(shè)備正在飛速普及,本文有針對性地介紹了Android操作系統(tǒng)內(nèi)存管理方面的機制,并針對Android應(yīng)用開發(fā)者提出了有效可實施的建議。
[1] Google Inc. Android源代碼[EB/OL]. [2014-08-27]. http://code.google.com/p/android/.
[2] Google Inc. Android SDK文檔[EB/OL]. [2014-08-27]. http://developer.android.com.
[3] 池?zé)槼? Java垃圾收集的機制及調(diào)優(yōu)[J]. 計算機應(yīng)用研究, 2004(3): 144-148.
[4] 柯元旦. Android內(nèi)核剖析[M]. 北京: 電子工業(yè)出版社, 2011.
[5] Cay S Horstmann, Gray Cornell. Java核心技術(shù)[M]. 周立新,陳波,葉乃文,等,譯. 北京: 機械工業(yè)出版社, 2014. ★
宋平波:碩士畢業(yè)于英國紐卡斯爾大學(xué)通信及信號處理專業(yè),現(xiàn)任職于中國電信股份有限公司廣東研究院,主要研究方向為移動互聯(lián)網(wǎng)應(yīng)用、電子商務(wù)等電信級平臺開發(fā)。
李云:學(xué)士畢業(yè)于哈爾濱工業(yè)大學(xué)電子與通信工程系,現(xiàn)任職于中國電信股份有限公司廣東研究院,主要研究方向為移動互聯(lián)網(wǎng)應(yīng)用、搜索引擎、電子商務(wù)等電信級平臺開發(fā)。
楊豪杰:碩士畢業(yè)于中山大學(xué)軟件學(xué)院,現(xiàn)任職于中國電信股份有限公司廣東研究院,主要研究方向為移動互聯(lián)網(wǎng)應(yīng)用平臺開發(fā)。
Research on Android Memory Management
SONG Ping-bo, LI Yun, YANG Hao-jie
(Guangdong Research Institute of China Telecom Co., Ltd., Guangzhou 510630, China)
In this paper, the memory management and memory deallocation schemes of Android operating system were analyzed in depth. Then, effective suggestions targeted at Android application developers were put forward.
Android memory management memory deallocation
10.3969/j.issn.1006-1010.2015.07.020
TP311.1
A
1006-1010(2015)07-0092-05
宋平波,李云,楊豪杰. Android內(nèi)存管理機制研究[J]. 移動通信, 2015,39(7): 92-96.
2014-08-29
責(zé)任編輯:劉文竹 liuwenzhu@mbcom.cn