移動(dòng)開(kāi)發(fā)的內(nèi)存管理

移動(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的系統(tǒng)架構(gòu)和其操作系統(tǒng)一樣,采用了分層的架構(gòu)。[
Android結(jié)構(gòu)

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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容