Android 內(nèi)存管理機(jī)制
內(nèi)存管理
- 進(jìn)程(由Application FrameWork和Linux內(nèi)核管理)
- 對象,變量(由Dalvik虛擬機(jī)管理)
針對進(jìn)程的內(nèi)存策略
- 內(nèi)存分配策略:由 ActivityManagerService 集中管理 所有進(jìn)程的內(nèi)存分配
- 內(nèi)存回收策略:
步驟1:Application Framework 決定回收的進(jìn)程類型
Android中的進(jìn)程 是托管的芙沥;當(dāng)進(jìn)程空間緊張時(shí)而昨,會 按進(jìn)程優(yōu)先級低->>高的順序 自動回收進(jìn)程(前臺-可見-服務(wù)-后臺-空)
步驟2:Linux 內(nèi)核真正回收具體進(jìn)程
ActivityManagerService 對 所有進(jìn)程進(jìn)行評分(評分存放在變量adj中)
更新評分到Linux 內(nèi)核
由Linux 內(nèi)核完成真正的內(nèi)存回收
針對對象找田、變量的內(nèi)存策略
Android對于對象,變量的內(nèi)存策略同java
內(nèi)存管理 = 對象 / 變量的內(nèi)存分配 + 內(nèi)存釋放
內(nèi)存分配策略
- 對象 / 變量的內(nèi)存分配 由程序自動負(fù)責(zé)
-
共有3種:靜態(tài)分配躺孝、棧式分配、堆式分配惧眠,分別面向靜態(tài)變量于个、局部變量 對象實(shí)例
image.png
具體實(shí)例
public class Sample {
// 該類的實(shí)例對象的成員變量s1厅篓、mSample1 & 指向?qū)ο蟠娣旁诙褍?nèi)存中
int s1 = 0;
Sample mSample1 = new Sample();
// 方法中的局部變量s2、mSample2存放在 棧內(nèi)存
// 變量mSample2所指向的對象實(shí)例存放在 堆內(nèi)存
public void method() {
int s2 = 0;
Sample mSample2 = new Sample();
}
}
// 變量mSample3的引用存放在棧內(nèi)存中
// 變量mSample3所指向的對象實(shí)例存放在堆內(nèi)存
// 該實(shí)例的成員變量s1或链、mSample1也存放在堆內(nèi)存中
Sample mSample3 = new Sample();
內(nèi)存釋放策略
對象 / 變量的內(nèi)存釋放 由Java垃圾回收器(GC)/ 幀棧 負(fù)責(zé)
Java垃圾回收器(GC)的內(nèi)存釋放 = 垃圾回收算法澳盐,主要包括:
常見的內(nèi)存問題 & 優(yōu)化方案
- 內(nèi)存泄露
- 內(nèi)存抖動
- 圖片Bitmap相關(guān)
- 代碼質(zhì)量 & 數(shù)量
- 日常不正確使用
內(nèi)存泄露
即 ML(Memory Leak)叼耙,指程序在申請內(nèi)存后筛婉,當(dāng)該內(nèi)存不需再使用但卻無法被釋放 & 歸還給程序的現(xiàn)象)癞松,容易使得應(yīng)用程序發(fā)生內(nèi)存溢出,即 OOM
常見內(nèi)存泄露原因:
- 集合類
- Static關(guān)鍵字修飾的成員變量
- 非靜態(tài)內(nèi)部類 / 匿名類
- 資源對象使用后未關(guān)閉
圖片資源Bitmap相關(guān)
內(nèi)存抖動
優(yōu)化方案:盡量避免頻繁創(chuàng)建大量厕妖、臨時(shí)的小對象
代碼質(zhì)量 & 數(shù)量
- 優(yōu)化原因
代碼本身的質(zhì)量(如 數(shù)據(jù)結(jié)構(gòu)、數(shù)據(jù)類型等) & 數(shù)量(代碼量的大腥砟堋)可能會導(dǎo)致大量的內(nèi)存問題举畸,如占用內(nèi)存大、內(nèi)存利用率低等 -
優(yōu)化方案
主要從代碼總量跋核、數(shù)據(jù)結(jié)構(gòu)、數(shù)據(jù)類型砂代、 & 數(shù)據(jù)對象引用 方面優(yōu)化,具體如下
image.png
常見使用
注:
還有1個(gè)內(nèi)存優(yōu)化的終極方案:調(diào)大虛擬機(jī)Dalvik的堆內(nèi)存大小
即 在AndroidManifest.xml的application標(biāo)簽中增加一個(gè)android:largeHeap屬性(值 = true),從而通知虛擬機(jī) 應(yīng)用程序需更大的堆內(nèi)存
但不建議 & 不鼓勵該做法
額外小技巧:
- 技巧1:獲取當(dāng)前可使用的內(nèi)存大小
調(diào)用 ActivityManager.getMemoryClass()方法可獲取當(dāng)前應(yīng)用可用的內(nèi)存大写废洹(單位 = 兆) -
技巧2:獲取當(dāng)前的內(nèi)存使用情況
在應(yīng)用生命周期的任何階段,調(diào)用 onTrimMemory()獲取應(yīng)用程序 當(dāng)前內(nèi)存使用情況(以內(nèi)存級別進(jìn)行識別)荠锭,可根據(jù)該方法返回的內(nèi)存緊張級別參數(shù) 來釋放內(nèi)存
image.png - 技巧3:當(dāng)視圖變?yōu)殡[藏狀態(tài)時(shí)证九,則釋放內(nèi)存
當(dāng)用戶跳轉(zhuǎn)到不同的應(yīng)用 & 視圖不再顯示時(shí), 應(yīng)釋放應(yīng)用視圖所占的資源
輔助內(nèi)存優(yōu)化的分析工具
- MAT(Memory Analysis Tools)
- Heap Viewer
- Allocation Tracker
- Android Studio 的 Memory Monitor
- LeakCanary