目錄
簡(jiǎn)單描述:沒(méi)有用的對(duì)象無(wú)法回收的現(xiàn)象就是內(nèi)存泄露
如果程序發(fā)生了內(nèi)存泄露,則會(huì)帶來(lái)以下這些問(wèn)題
- 應(yīng)用可用的內(nèi)存減少,增加了堆內(nèi)存的壓力
- 降低了應(yīng)用的性能,比如會(huì)觸發(fā)更頻繁的 GC
- 嚴(yán)重的時(shí)候可能會(huì)導(dǎo)致內(nèi)存溢出錯(cuò)誤,即 OOM Error
OOM 發(fā)生很有可能是內(nèi)存泄露導(dǎo)致的,但并非所有的 OOM 都是由內(nèi)存泄露引起的,內(nèi)存泄露也并不一定引起 OOM。
一、Java 的內(nèi)存管理和引用類型
二、Android 中內(nèi)存泄露的常見(jiàn)場(chǎng)景 & 解決方案
1、單例模式強(qiáng)引用Context造成的內(nèi)存泄露
過(guò)多的單例會(huì)讓內(nèi)存占用過(guò)多,而且單例模式由于其 靜態(tài)特性,其生命周期 = 應(yīng)用程序的生命周期,不正確地使用單例模式也會(huì)造成內(nèi)存泄露。
單例模式
public class SingleInstanceTest {
private static SingleInstanceTest sInstance;
private Context mContext;
private SingleInstanceTest(Context context){
this.mContext = context;
}
public static SingleInstanceTest newInstance(Context context){
if(sInstance == null){
sInstance = new SingleInstanceTest(context);
}
return sInstance;
}
}
上面是一個(gè)比較簡(jiǎn)單的單例模式用法,需要外部傳入一個(gè) Context 來(lái)獲取該類的實(shí)例,如果此時(shí)傳入的 Context 是 Activity 的話,此時(shí)單例就有持有該 Activity 的強(qiáng)引用(直到整個(gè)應(yīng)用生命周期結(jié)束)。這樣的話,即使該 Activity 退出,該 Activity 的內(nèi)存也不會(huì)被回收,這樣就造成了內(nèi)存泄露,特別是一些比較大的 Activity,甚至還會(huì)導(dǎo)致 OOM(Out Of Memory)。
解決方法: 使用 getApplicationContext() 單例模式引用的對(duì)象的生命周期 = 應(yīng)用生命周期
public class SingleInstanceTest {
private static SingleInstanceTest sInstance;
private Context mContext;
private SingleInstanceTest(Context context){
this.mContext = context.getApplicationContext();
}
public static SingleInstanceTest newInstance(Context context){
if(sInstance == null){
sInstance = new SingleInstanceTest(context);
}
return sInstance;
}
}
2、普通類強(qiáng)引用Context造成的內(nèi)存泄露
public class Sample {
private Context mContext;
public Sample(Context context){
this.mContext = context;
}
public Context getContext() {
return mContext;
}
}
// 外部調(diào)用
Sample sample = new Sample(MainActivity.this);
解決方法: 使用 弱引用 WeakReference
public class Sample {
private WeakReference<Context> mWeakReference;
public Sample(Context context){
this.mWeakReference = new WeakReference<>(context);
}
public Context getContext() {
if(mWeakReference.get() != null){
return mWeakReference.get();
}
return null;
}
}
被弱引用關(guān)聯(lián)的對(duì)象只能存活到下一次垃圾回收之前,也就是說(shuō)即使 Sample 持有 Activity 的引用,但由于 GC 會(huì)幫我們回收相關(guān)的引用,被銷(xiāo)毀的 Activity 也會(huì)被回收內(nèi)存,這樣我們就不用擔(dān)心會(huì)發(fā)生內(nèi)存泄露了。
3、非靜態(tài)內(nèi)部類 / 匿名類
4、靜態(tài)集合類 / 靜態(tài)變量 / static
static List<Object> objectList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Object obj = new Object();
objectList.add(obj);
obj = null;
}
靜態(tài)變量的生命周期和應(yīng)用程序一致,而且他們所引用的對(duì)象 Object 也不能釋放,這樣便造成了內(nèi)存泄露。
解決方法:在集合元素使用之后從集合中刪除,等所有元素都使用完之后,將集合置空。
objectList.clear();
objectList = null;
三、內(nèi)存泄露排查工具
1、Android Lint
Lint 是 Android Studio 提供的 代碼掃描分析工具,它可以幫助我們發(fā)現(xiàn)代碼機(jī)構(gòu) / 質(zhì)量問(wèn)題,同時(shí)提供一些解決方案,檢測(cè)內(nèi)存泄露當(dāng)然也不在話下,使用也是非常的簡(jiǎn)單,可以參考下這篇文章:Android 性能優(yōu)化:使用 Lint 優(yōu)化代碼、去除多余資源
2、leakcanary
LeakCanary 是 Square 公司開(kāi)源的「Android 和 Java 的內(nèi)存泄漏檢測(cè)庫(kù)」,Square 出品,必屬精品,功能很強(qiáng)大,使用也很簡(jiǎn)單。建議直接看 Github 上的說(shuō)明:leakcanary,也可以參考這篇文章:Android內(nèi)存優(yōu)化(六)LeakCanary使用詳解