先解釋下內(nèi)存泄露和內(nèi)存溢出,這兩個比較容易混淆的概念:
內(nèi)存泄露:程序在向系統(tǒng)申請分配內(nèi)存空間后(new)修陡,在使用完畢后未釋放邀桑。結(jié)果導致一直占據(jù)該內(nèi)存單元,我們和程序都無法再使用該內(nèi)存單元工窍,直到程序結(jié)束割卖,這是內(nèi)存泄露。比如當一個對象已經(jīng)不需要再使用了患雏,本該被回收時鹏溯,而另外一個正在使用的對象持有它的引用從而導致它不能被回收,這就導致本該被回收的對象不能被回收而停留在堆內(nèi)存中淹仑,內(nèi)存泄漏就產(chǎn)生了丙挽。
內(nèi)存溢出:程序向系統(tǒng)申請的內(nèi)存空間超出了系統(tǒng)能給的。比如加載一張大圖片時候攻人,需要的內(nèi)存超過系統(tǒng)分配的最大內(nèi)存取试,導致程序崩潰;
大量的內(nèi)存泄露會導致內(nèi)存溢出(oom)怀吻。
1. observer 我們在使用監(jiān)聽器的時候瞬浓,往往是addxxxlistener,但是當我們不需要的時候蓬坡,忘記removexxxlistener猿棉,就容易內(nèi)存leak。
廣播沒有unregisterrecevier
2.對于使用了BraodcastReceiver屑咳,ContentObserver萨赁,F(xiàn)ile,Cursor兆龙,Stream杖爽,Bitmap等資源的使用,應(yīng)該在Activity銷毀時及時關(guān)閉或者注銷,否則這些資源將不會被回收慰安,造成內(nèi)存泄漏
3.Handler造成的內(nèi)存泄漏
Handler的使用造成的內(nèi)存泄漏問題應(yīng)該說最為常見了腋寨,平時在處理網(wǎng)絡(luò)任務(wù)或者封裝一些請求回調(diào)等api都應(yīng)該會借助Handler來處理,對于Handler的使用代碼編寫一不規(guī)范即有可能造成內(nèi)存泄漏化焕,如下示例:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData(){
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
這種創(chuàng)建Handler的方式會造成內(nèi)存泄漏萄窜,由于mHandler是Handler的非靜態(tài)匿名內(nèi)部類的實例,所以它持有外部類Activity的引用撒桨,我們知道消息隊列是在一個Looper線程中不斷輪詢處理消息查刻,那么當這個Activity退出時消息隊列中還有未處理的消息或者正在處理消息,而消息隊列中的Message持有mHandler實例的引用凤类,mHandler又持有Activity的引用穗泵,所以導致該Activity的內(nèi)存資源無法及時回收,引發(fā)內(nèi)存泄漏谜疤,所以另外一種做法為:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
創(chuàng)建一個靜態(tài)Handler內(nèi)部類火欧,然后對Handler持有的對象使用弱引用,這樣在回收時也可以回收Handler持有的對象茎截,這樣雖然避免了Activity泄漏,不過Looper線程的消息隊列中還是可能會有待處理的消息赶盔,所以我們在Activity的Destroy時或者Stop時應(yīng)該移除消息隊列中的消息企锌,更準確的做法如下:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
使用mHandler.removeCallbacksAndMessages(null);是移除消息隊列中所有消息和所有的Runnable。當然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();來移除指定的Runnable和Message于未。
4.單例
因為單例的靜態(tài)特性使得單例的生命周期和應(yīng)用的生命周期一樣長撕攒,這就說明了如果一個對象已經(jīng)不需要使用了,而單例對象還持有該對象的引用烘浦,那么這個對象將不能被正扯镀海回收,這就導致了內(nèi)存泄漏闷叉。
5.