移動(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è)大問題了,不過琅绅,在移動(dòng)端開發(fā)中扶欣,針對(duì)資源有限的問題都是一個(gè)不得不正視的問題。本文主要針對(duì)內(nèi)存這一塊進(jìn)行闡述,針對(duì)移動(dòng)網(wǎng)絡(luò)的特點(diǎn)料祠、電池優(yōu)化的問題骆捧、有后續(xù)系列闡述文章。
移動(dòng)操作系統(tǒng)的幾個(gè)主要特點(diǎn)
- 移動(dòng)設(shè)備需要便攜髓绽,所以需要攜帶電池敛苇,需要專門的電池管理,這一點(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的功耗問題鳖粟,復(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所占用的各類資源航攒,比如內(nèi)存磺陡,當(dāng)程序后臺(tái)運(yùn)行時(shí),大部分情況下內(nèi)存都會(huì)被回收漠畜。
- 移動(dòng)操作系統(tǒng)一般情況下都會(huì)限制每個(gè)應(yīng)用所消耗的資源總量币他。
- 作為App開發(fā)者,管理好自身App所消耗的資源是一個(gè)應(yīng)盡的義務(wù)憔狞。
- 管理
Android內(nèi)存管理機(jī)制淺析
Android是2007年Google在收購(gòu)基礎(chǔ)上推出的基于Linux操作系統(tǒng)的開源移動(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)用開發(fā)
在Android平臺(tá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)行過程中可能會(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)行過程中的停下執(zhí)行GC捉腥,不可避免的的會(huì)影響程序的運(yùn)行氓拼,一旦GC邊界條件被觸發(fā),系統(tǒng)就會(huì)停止當(dāng)前進(jìn)程抵碟,開始GC桃漾。
關(guān)于GC的詳細(xì)機(jī)制詳見,ref:
- Android Runtime(ART)的進(jìn)一步改進(jìn)措施
1) ART中暫停次數(shù)相比于Dalvik有減少拟逮,從兩次減為一次;
2)ART GC 一樣有暫停中斷撬统,不一樣之處在于,ART在有些階段比如引用過程敦迄,sytem sweeping過程恋追,等階段中可以并發(fā)的執(zhí)行GC。
DVM算法簡(jiǎn)析
- 程序運(yùn)行過程中罚屋,不斷申請(qǐng)新的對(duì)象消耗內(nèi)存苦囱,直到用完所有,然后創(chuàng)建新的對(duì)象需要內(nèi)存的時(shí)候脾猛,暫停運(yùn)行撕彤,出發(fā)GC 回收,器原理就是從GC Roots開始猛拴,將整個(gè)內(nèi)存遍歷羹铅,保留所有被直接以及間接用的內(nèi)存區(qū)域,余下的被回收愉昆。
該算法可以解決內(nèi)存的問題睦裳,釋放內(nèi)存。 - 但是缺點(diǎn)一樣存在撼唾,1 從GC Roots開始的遍歷是一個(gè)遞歸調(diào)用廉邑,這個(gè)過程本身會(huì)消耗很多資源,一方面在內(nèi)存不多時(shí)候消耗內(nèi)存遞歸倒谷,另一方面蛛蒙,如果遍歷非常深的話,消耗的時(shí)間資源也很明顯渤愁。所有后來(lái)的android系統(tǒng)優(yōu)化了這個(gè)過程牵祟,另開啟線程逐步釋放內(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)存的問題存在
/*
* 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;
}