內(nèi)存泄漏的主要原因
- 生命周期長的對象持有了生命周期短的對象蟋软,導(dǎo)致生命周期短的對象無法釋放內(nèi)存。(即長的不能持有短的泊碑。)
context
情形
情景描述
- 生命周期長的對象保留了生命周期短的
context
瓤介,如 單例類 保留了Activity
的context
。
解決方案
- 在獲取到生命周期短的
context
時漂辐,不保留此context
衡蚂,而通過Context.getApplicationContext()
獲取應(yīng)用程序的context
窿克。由于保留應(yīng)用程序的context
的對象生命周期不如應(yīng)用程序長,所以不會出現(xiàn)問題毛甲。
用static
存儲內(nèi)部類對象
情景描述
-
mResource
的生命周期是跟應(yīng)用程序一樣長的年叮,而且由于TestResource
是 內(nèi)部類 ,會保留 外部類 對象的引用(即Activity
)玻募,所以Activity
無法釋放只损,內(nèi)存泄漏。
public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager == null){
mManager = new TestResource();
}
//...
}
class TestResource {
//...
}
}
解決方案
把 內(nèi)部類(Inner Class) 改為 嵌套類(Nested Class) 七咧。
線程相關(guān)
情景描述
- 都是因為 內(nèi)部類 對象(此處為 匿名內(nèi)部類 )保留了 外部類 對象的引用導(dǎo)致的跃惫。
情景一
// 無法保證此匿名內(nèi)部類所持有的外部類對象的生命長于10秒
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
}.execute();
情景二
// 無法保證此段代碼的匿名內(nèi)部類所持有的外部類對象的生命長于10秒
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
解決方案
- 改用 嵌套類(Nested Class) ,如果需要 外部類 的引用艾栋,則通過 構(gòu)造方法 傳遞進去爆存,然后使用 弱引用 保留。
Handler情形
情景描述
-
線程 保留了
Looper
的引用蝗砾。 -
Looper
保留了MessageQueue
的引用先较。 -
MessageQueue
保留了Message
的引用携冤。 -
Message
保留了Handler
的引用。 - 導(dǎo)致
Handler
的生命周期可能與 線程 一樣長闲勺。 - 若此時同時滿足以下兩個條件:
-
Handler
為 內(nèi)部類(Inner Class噪叙,包括匿名內(nèi)部類) ,即會保留 外部類 對象的引用霉翔。 -
外部類 對象的生命周期短于 線程 ,如
Activity
苞笨。
-
- 就會導(dǎo)致此 外部類 對象在 線程 釋放之前無法釋放债朵,引發(fā) 內(nèi)存泄漏 。
解決方案
思路一:破壞條件1
- 讓
Handler
成為 嵌套類(Nested Class) 瀑凝,若要訪問原來的 外部類 序芦,就只需要使用 弱引用 來保留 外部類 對象的引用。
// 將Handler定義為嵌套類(static內(nèi)部類)以避免內(nèi)存泄漏粤咪。
private static class RequestHandler extends Handler {
private WeakReference<Activity> mWeakRef;
// 可以在構(gòu)造函數(shù)中傳入外部類對象谚中,用WeakReference保留此對象的引用,這樣可以避免內(nèi)存泄漏寥枝。
public RequestHandler(Activity activity) {
mWeakRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
if (msg.what == /* 消息 */) {
// 先判斷WeakReference指向的對象還存在宪塔。
if (mWeakRef.get() == null) {
return;
}
// 使用WeakReference.get()來獲取對象,并使用
Activity activity = mWeakRef.get();
}
}
}
思路二:破壞條件2
-
外部類 的對象的生命周期不短于 線程 囊拜,例如
Activity
就不能作為Handler
的 外部類 某筐,而 線程 可以。
資源未釋放
- 對于使用了
BraodcastReceiver
冠跷,ContentObserver
南誊,File
,Cursor
蜜托,Stream
抄囚,Bitmap
等資源的使用,應(yīng)該在Activity
銷毀時及時關(guān)閉或者注銷橄务,否則這些資源將不會被回收幔托,造成 內(nèi)存泄漏 。
LeakCanary
內(nèi)存泄漏檢測工具仪糖。