Java使用有向圖機制榛斯,通過GC自動檢查內存中的對象(什么時候檢查由虛擬機決定)阶牍,如果GC發(fā)現一個或一組對象為不可到達狀態(tài)查描,則將該對象從內存中回收宵蛀。也就是說痴鳄,一個對象不被任何引用所指向瘟斜,則該對象會在被GC發(fā)現的時候被回收;另外痪寻,如果一組對象中只包含互相的引用螺句,而沒有來自它們外部的引用(例如有兩個對象A和B互相持有引用,但沒有任何外部對象持有指向A或B的引用)橡类,這仍然屬于不可到達蛇尚,同樣會被GC回收。
Java內存泄漏指的是進程中某些對象(垃圾對象)已經沒有使用價值了顾画,但是它們卻可以直接或間接地引用到導致無法被GC回收取劫。無用的對象占據著內存空間,使得實際可使用內存變小研侣,形象地說法就是內存泄漏了谱邪。
2.Android中使用Handler造成內存泄露原因分析
(1)Handler使用方法
Handler mHandler =newHandler() {@OverridepublicvoidhandleMessage(Message msg){super.handleMessage(msg);? ? }};
在使用handler時,這是一段很常見的代碼义辕。但是虾标,它卻會造成嚴重的內存泄漏問題。在實際編寫中灌砖,我們往往會得到如下警告:
This Handler class should bestaticorleaks mightoccur(anonymous android.os.Handler)
此處理程序類應為bestatic璧函,否則可能會發(fā)生泄漏(匿名android.os.Handler)
Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
由于此處理程序被聲明為內部類,因此它可能會阻止外部類被垃圾收集基显。如果處理程序對主線程以外的線程使用循環(huán)器或MessageQueue蘸吓,則沒有問題。如果處理程序正在使用主線程的Looper或MessageQueue撩幽,則需要修復處理程序聲明库继,如下所示:將處理程序聲明為靜態(tài)類;在外部類中窜醉,實例化外部類的WeakReference宪萄,并在實例化處理程序時將此對象傳遞給處理程序;使用WeakReference對象對外部類的成員進行所有引用榨惰。
(2)內部類mHandler
簡單的內部類如下:
class OuterClass{
class InnerClass{}
}
以上代碼mHandler讓人并不覺得是內部類拜英,它并不像InnerClass那樣形象,但是其實以下句柄實現一個繼承Handler的類琅催,也就是自定義了一個類居凶,那么明顯它就是一個內部類虫给。其實它是屬于內部類一種:匿名內部類Anonymous Inner Class
{
@Override
public void handleMessage(Message msg){?
super.handleMessage(msg);? ??
?}
}
(3)Handler造成內存泄漏分析
當Android應用程序啟動時,Framework會為該應用程序的主線程創(chuàng)建一個Looper對象侠碧。這個Looper對象包含一個簡單的消息隊列Message Queue抹估,并且能夠循環(huán)的處理隊列中的消息。這些消息包括大多數應用程序Framework事件弄兜,例如Activity生命周期方法調用药蜻、點擊事件等,這些消息都會被添加到消息隊列中并被逐個處理挨队。另外谷暮,主線程的Looper對象會伴隨該應用程序的整個生命周期。
當在主線程中初始化Handler時盛垦,該Handler就會自動和主線程Looper的消息隊列關聯(lián)起來湿弦。所有發(fā)送到消息隊列的消息Message都會擁有一個對Handler的引用,所以當Looper來處理消息時腾夯,會據此回調Handler的handleMessage(Message)方法來分發(fā)處理該消息颊埃。
在Java里,非靜態(tài)內部類和匿名內部類都會潛在的引用它們所屬的外部類蝶俱。但是班利,靜態(tài)內部類不會引用外部類對象。
當使用內部類(包括匿名內部類)來創(chuàng)建Handler的時候榨呆,Handler對象會持有外部類對象(通常是一個Activity)的引用(不然怎么通過Handler來操作Activity中的View罗标?)。而Handler通常會伴隨著一個耗時的后臺線程(例如從網絡拉取圖片)一起出現积蜻,這個后臺線程在任務執(zhí)行完畢(例如圖片下載完畢)之后闯割,通過消息機制通知Handler,然后Handler把圖片更新到界面竿拆。然而宙拉,如果用戶在網絡請求過程中關閉了Activity,正常情況下丙笋,Activity不再被使用谢澈,它就有可能在GC檢查時被回收掉,但由于這時線程尚未執(zhí)行完御板,而該線程持有Handler的引用(不然它怎么發(fā)消息給Handler锥忿?),這個Handler又持有Activity的引用怠肋,就導致該Activity無法被回收(即內存泄露)缎谷,直到網絡請求結束(例如圖片下載完畢)。
如果執(zhí)行了Handler的postDelayed()方法,該方法會將Handler裝入一個Message列林,并把這條Message推到MessageQueue中,那么在你設定的delay到達之前酪惭,會有一條MessageQueue -> Message -> Handler -> Activity的鏈希痴,導致Activity被持有引用而無法被GC回收。
(4)內存泄漏的危害
虛擬機占用內存過高春感,導致OOM(內存溢出)砌创,程序出錯。對于Android應用來說鲫懒,就是用戶打開一個Activity嫩实,使用完之后關閉它,內存泄露窥岩;又打開甲献,又關閉,又泄露颂翼;幾次之后晃洒,程序占用內存超過系統(tǒng)限制。
3.Handler導致內存泄漏解決方法
方法一:通過程序邏輯進行保護
1.在關閉Activity的時候停掉后臺線程朦乏。線程停掉了球及,就相當于切斷了Handler和外部連接的線,Activity自然會在合適的時候被GC回收呻疹。
2.如果Handler是被delay的Message持有了引用吃引,那么使用Handler的removeCallbacks()方法,把消息對象從消息隊列移除就行了刽锤。
方法二:將Handler聲明為靜態(tài)類
靜態(tài)類不持有外部類的對象镊尺,所以Activity可以隨意被回收。代碼如下:
private static class MyHandler extends Handler{
@Override
public void handleMessage(Message msg){?
super.handleMessage(msg);? ?
??}
}
但其實沒這么簡單姑蓝。使用了以上代碼之后鹅心,你會發(fā)現,由于Handler不再持有外部類對象的引用纺荧,導致程序不允許你在Handler中操作Activity中的對象了旭愧。所以你需要在Handler中增加一個對Activity的弱引用(WeakReference):
private final MyHandler mHandler =new MyHandler(this);?
private static class MyHandler extends Handler{?
private final WeakReference mActivity;
public MyHandler(Activity activity){? ?
?? mActivity =newWeakReference(activity);? ? }
@Override
public void handleMessage(Message msg){
final Activity activity = mActivity.get();
if(activity !=null) {
// doSomething
}?
?? }?
?}
對于匿名類Runnable,同樣可以將其設置為靜態(tài)類:
private static final Runnable mRunnable =newRunnable() {
@Override
?public void run(){?
// doSomething
}??
};
4.什么是WeakReference宙暇?
WeakReference弱引用输枯,與強引用(即我們常說的引用)相對,它的特點是占贫,GC在回收時會忽略掉弱引用桃熄,即就算有弱引用指向某對象,但只要該對象沒有被強引用指向(實際上多數時候還要求沒有軟引用型奥,但此處軟引用的概念可以忽略)瞳收,該對象就會在被GC檢查到時回收掉碉京。對于上面的代碼,用戶在關閉Activity之后螟深,就算后臺線程還沒結束谐宙,但由于僅有一條來自Handler的弱引用指向Activity,所以GC仍然會在檢查的時候把Activity回收掉界弧。這樣凡蜻,內存泄露的問題就不會出現了。
鏈接:http://www.reibang.com/p/daffbb9ddc17
來源:簡書