移動(dòng)系統(tǒng)對(duì)資源的限制和要求
移動(dòng)操作系統(tǒng)現(xiàn)對(duì)于PC端的一個(gè)首要特點(diǎn)就是資源有限,比如內(nèi)存、電池、網(wǎng)絡(luò)的不確定等等,這些資源相對(duì)于PC端來(lái)說(shuō)都很有限,當(dāng)然對(duì)于發(fā)展到今天的移動(dòng)端設(shè)備來(lái)說(shuō),這個(gè)已經(jīng)不算是一個(gè)大問(wèn)題了,不過(guò),在移動(dòng)端開(kāi)發(fā)中,針對(duì)資源有限的問(wèn)題都是一個(gè)不得不正視的問(wèn)題。本文主要針對(duì)內(nèi)存這一塊進(jìn)行闡述,針對(duì)移動(dòng)網(wǎng)絡(luò)的特點(diǎn)、電池優(yōu)化的問(wèn)題、有后續(xù)系列闡述文章。
移動(dòng)操作系統(tǒng)的幾個(gè)主要特點(diǎn)
- 移動(dòng)設(shè)備需要便攜,所以需要攜帶電池,需要專(zhuān)門(mén)的電池管理,這一點(diǎn),筆者當(dāng)年首次學(xué)習(xí)Symbian系統(tǒng)時(shí)卻有體會(huì);
- 移動(dòng)設(shè)備通常情況下,屏幕比較小,但是需要展示的信息一點(diǎn)也不少,所以,就需要在界面設(shè)計(jì)以及交互設(shè)計(jì)上(UI UE)需要更多的想象力,需要更好的更人性化的設(shè)計(jì),需要面對(duì)的情況也更復(fù)雜;
- 由于移動(dòng)設(shè)備的大部分都需要電池供電,所以,在選擇CPU時(shí),也就必須考慮好CPU的功耗問(wèn)題,復(fù)雜指令集的基本不建議在考慮之列。
- 同樣由于電池所限,使用的內(nèi)存(memory)和存儲(chǔ)相對(duì)于PC桌面系統(tǒng)來(lái)說(shuō)都相當(dāng)受限制。
- 由于移動(dòng)設(shè)備資源所限,大部分移動(dòng)設(shè)備操作系統(tǒng),都需要分時(shí)復(fù)用來(lái)管理所有的需要運(yùn)行的程序(通常叫App)。
- 現(xiàn)階段流行的移動(dòng)操作系統(tǒng)主要是ios和android,其中大部分程序都運(yùn)行在后臺(tái)情況下,都由系統(tǒng)層面管理App所占用的各類(lèi)資源,比如內(nèi)存,當(dāng)程序后臺(tái)運(yùn)行時(shí),大部分情況下內(nèi)存都會(huì)被回收。
- 移動(dòng)操作系統(tǒng)一般情況下都會(huì)限制每個(gè)應(yīng)用所消耗的資源總量。
- 作為App開(kāi)發(fā)者,管理好自身App所消耗的資源是一個(gè)應(yīng)盡的義務(wù)。
- 管理
Android內(nèi)存管理機(jī)制淺析
Android是2007年Google在收購(gòu)基礎(chǔ)上推出的基于Linux操作系統(tǒng)的開(kāi)源移動(dòng)操作系統(tǒng),該平臺(tái)由操作系統(tǒng)、中間件、用戶界面和應(yīng)用軟件組成。

Android結(jié)構(gòu)
從架構(gòu)圖看,Android分為四個(gè)層,分別是應(yīng)用程序?qū)?、?yīng)用程序框架層、系統(tǒng)運(yùn)行庫(kù)層和Linux層。
Android應(yīng)用開(kāi)發(fā)
在Android平臺(tái)上開(kāi)發(fā)應(yīng)用程序,包括java 和C++(for Native)。
android平臺(tái)的內(nèi)存管理
一般情況下(多進(jìn)程例外)每個(gè)應(yīng)用都會(huì)由系統(tǒng)分配一個(gè)進(jìn)程(zygote)資源。這段Android代碼可以獲取當(dāng)前進(jìn)程的內(nèi)存情況。
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int memClass = activityManager.getMemoryClass();
int maxClass = activityManager.getLargeMemoryClass();
任何一個(gè)進(jìn)程最多可以獲取的內(nèi)存資源時(shí)有限的。
內(nèi)存優(yōu)化思路
強(qiáng)引用與弱引用
- 在一個(gè)Activity生命周期中,其中所有定義的變量的生命周期都和Activty保持一致,如果用強(qiáng)引用的話,那么變量所指向的內(nèi)存和Activity生命周期保持一致,但是在很多情況下,從邏輯上這個(gè)成員變量已經(jīng)不需要了,由于強(qiáng)引用其所指向的內(nèi)存區(qū)域也保持一樣的什么周期,有點(diǎn)得不償失。
- 這種情況下,可以選擇弱引用,所謂弱引用就是指其指向的內(nèi)存區(qū)域,在內(nèi)存比較緊張的時(shí)候可以被GC。這樣就可以減少內(nèi)存的使用。
WeakReference<ForeEngine> mWeakForeEngine;
ForeEngine mForeEngine;
其中:
- mForeEngine是強(qiáng)引用,mWeakForeEngine是弱引用
- mWeakForeEngine指向的內(nèi)存在mWeakForeEngine運(yùn)行過(guò)程中可能會(huì)被回收
弱引用的初始化
mWeakForeEngine = new WeakReference<ForeEngine>(foreEngine) ;
弱引用的使用
ForeEngine foreEngine = mWeakForeEngine.get();
if(foreEngine == null) {
// todo
}
關(guān)注內(nèi)存大戶Bitmap
待續(xù)
Android內(nèi)存管理原理簡(jiǎn)析
Java虛擬機(jī)模型
JVM內(nèi)存模型,java虛擬機(jī)在構(gòu)建RunTime運(yùn)行時(shí)數(shù)據(jù)內(nèi)存分配,
內(nèi)存主要分為方法區(qū),虛擬機(jī)棧,本地方法棧,堆,PCR程序計(jì)數(shù)器。

JVM內(nèi)存模型示意
Android的改進(jìn)
- 寄存器和棧的區(qū)別
* JVM虛擬機(jī)基于棧,DVM基于寄存器。*
- Dalvik和java虛擬的區(qū)別 dex class的區(qū)別
- dalvik art的改進(jìn)
android虛擬機(jī)都使用頁(yè)式和memory mapping方式進(jìn)行內(nèi)存管理,虛擬機(jī)分配并決定了內(nèi)存的生命周期,并且使用GC(*garbage collection*)來(lái)回收已分配但是不再使用的內(nèi)存片段,GC的主要工作主要分為兩個(gè)部分:
1)找到內(nèi)存中不再使用的數(shù)據(jù)片段
2)回收這些資源
分配內(nèi)存資源主要是分代管理,剛剛分配的內(nèi)存區(qū)域叫Young Generation。
對(duì)象在Young Generation時(shí)間比較長(zhǎng)之后,就會(huì)被劃到Older Generation,還有一個(gè)permanent generation。
盡管內(nèi)存回收的速度較快,但是由于會(huì)在程序運(yùn)行過(guò)程中的停下執(zhí)行GC,不可避免的的會(huì)影響程序的運(yùn)行,一旦GC邊界條件被觸發(fā),系統(tǒng)就會(huì)停止當(dāng)前進(jìn)程,開(kāi)始GC。
關(guān)于GC的詳細(xì)機(jī)制詳見(jiàn),ref:
- Android Runtime(ART)的進(jìn)一步改進(jìn)措施
1) ART中暫停次數(shù)相比于Dalvik有減少,從兩次減為一次;
2)ART GC 一樣有暫停中斷,不一樣之處在于,ART在有些階段比如引用過(guò)程,sytem sweeping過(guò)程,等階段中可以并發(fā)的執(zhí)行GC。
DVM算法簡(jiǎn)析
- 程序運(yùn)行過(guò)程中,不斷申請(qǐng)新的對(duì)象消耗內(nèi)存,直到用完所有,然后創(chuàng)建新的對(duì)象需要內(nèi)存的時(shí)候,暫停運(yùn)行,出發(fā)GC 回收,器原理就是從GC Roots開(kāi)始,將整個(gè)內(nèi)存遍歷,保留所有被直接以及間接用的內(nèi)存區(qū)域,余下的被回收。
該算法可以解決內(nèi)存的問(wèn)題,釋放內(nèi)存。 - 但是缺點(diǎn)一樣存在,1 從GC Roots開(kāi)始的遍歷是一個(gè)遞歸調(diào)用,這個(gè)過(guò)程本身會(huì)消耗很多資源,一方面在內(nèi)存不多時(shí)候消耗內(nèi)存遞歸,另一方面,如果遍歷非常深的話,消耗的時(shí)間資源也很明顯。所有后來(lái)的android系統(tǒng)優(yōu)化了這個(gè)過(guò)程,另開(kāi)啟線程逐步釋放內(nèi)存,盡量不影響程序的正常運(yùn)行。也就是逐步GC,還有一個(gè)叫CMS(concurrent mark sweep)。
-
下面是代碼
1 啟動(dòng)VM
\dalvik\vm\Init.cpp
/*
* VM initialization. Pass in any options provided on the command line.
* Do not pass in the class name or the options for the class.
*
* Returns 0 on success.
*/
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
其中 啟動(dòng)的內(nèi)容很多,有興趣的可以參考源代碼。
2 GC 啟動(dòng)
在main heap上采用mmap管理內(nèi)存。
dalvik\vm\alloc\alloc.cpp
/*
* Initialize the GC universe.
*
* We're currently using a memory-mapped arena to keep things off of the
* main heap. This needs to be replaced with something real.
*/
bool dvmGcStartup()
{
dvmInitMutex(&gDvm.gcHeapLock);
pthread_cond_init(&gDvm.gcHeapCond, NULL);
return dvmHeapStartup();
}
3 GC heap啟動(dòng)
dalvik\vm\alloc\Heap.cpp
初始化GC heap 并建立VM card table。
/*
* Initialize the GC heap.
*
* Returns true if successful, false otherwise.
*/
bool dvmHeapStartup()
{
GcHeap *gcHeap;
if (gDvm.heapGrowthLimit == 0) {
gDvm.heapGrowthLimit = gDvm.heapMaximumSize;
}
gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
gDvm.heapMaximumSize,
gDvm.heapGrowthLimit);
if (gcHeap == NULL) {
return false;
}
gcHeap->ddmHpifWhen = 0;
gcHeap->ddmHpsgWhen = 0;
gcHeap->ddmHpsgWhat = 0;
gcHeap->ddmNhsgWhen = 0;
gcHeap->ddmNhsgWhat = 0;
gDvm.gcHeap = gcHeap;
/* Set up the lists we'll use for cleared reference objects.
*/
gcHeap->clearedReferences = NULL;
if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit)) {
LOGE_HEAP("card table startup failed.");
return false;
}
return true;
}
4
dalvik\vm\alloc\HeapSource.cpp
為了不和zygote heap的內(nèi)存發(fā)生交集,在首次fork之前調(diào)用它,這個(gè)仍然會(huì)有一些造成小片段內(nèi)存的問(wèn)題存在
/*
* This is called while in zygote mode, right before we fork() for the
* first time. We create a heap for all future zygote process allocations,
* in an attempt to avoid touching pages in the zygote heap. (This would
* probably be unnecessary if we had a compacting GC -- the source of our
* troubles is small allocations filling in the gaps from larger ones.)
*/
bool dvmHeapSourceStartupBeforeFork()
{
HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"
HS_BOILERPLATE();
assert(gDvm.zygote);
if (!gDvm.newZygoteHeapAllocated) {
/* Ensure heaps are trimmed to minimize footprint pre-fork.
*/
trimHeaps();
/* Create a new heap for post-fork zygote allocations. We only
* try once, even if it fails.
*/
ALOGV("Splitting out new zygote heap");
gDvm.newZygoteHeapAllocated = true;
return addNewHeap(hs);
}
return true;
}