背景:
程序的運行離不開內存,儘管硬件技術的發展也讓手機的內存不斷增大(比如我的OPPO R7s是256M),但是不恰當的編程習慣仍然會導致內存泄漏的發生,這將是一場災難,所以我們讓然應該去避免。
內存泄漏與內存溢出的關係:
內存泄漏爲內存溢出埋下隱憂,因爲內存泄漏的實質就是需要釋放的內存被生命週期長的對象繼續持有導致得不到釋放,系統無法重新分配這些內存,隨着軟件的使用內存資源越來越少,導致內存不夠,出現溢出(OOM),進而crash掉.
獲取系統爲每個虛擬機實例(每個Application)分配的內存是多大,可以通過下面這個代碼片段獲取一下:
/**
* the maximum amount of memory that the virtual machine will attempt to use, measured in bytes.
*/
Runtime runtime = Runtime.getRuntime();
long l = runtime.maxMemory()/1024/1024;//以byte爲單位返回的
收集一些常見的小例子:
1:非靜態內部類
public class MainActivity extends AppCompatActivity {
private static InnerClass innerClassInstance;//靜態對象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_create_instance).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
innerClassInstance = new InnerClass();//創建靜態對象
finish();//銷燬這個Activity即被隱式持有的外部類
}
});
}
class InnerClass {
//這就是內部類,隱式持有外部類
}
}
分析:因爲全局變量innerClassInstance聲明成了靜態的,所以其生命週期和此應用的聲明週期一樣長,並且InnerClass持有Activity這個外部類,這就導致了Activity被銷燬後並不能被系統回收,這塊內存就無法重新分配繼續使用,這就造成了內存泄漏。
2:匿名內部類
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_create_instance).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startNewThread();//開啓一個新線程
finish();//銷燬這個Activity即被隱式持有的外部類
}
});
}
public void startNewThread() {
new Thread(new Runnable() {//匿名內部類隱式持有外部類的引用
@Override
public void run() {
//執行耗時操作的子線程,在Activity銷燬了還沒結束
}
}).start();
}
}
分析:因爲這個開啓的子線程在執行一個耗時操作,在Activity已經銷燬時還沒執行完成,隱式持有的外部類Activity,導致不能被回收。
解決辦法:匿名內部類改成靜態內部類
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_create_instance).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new MyThread().start();//開啓一個新線程
finish();//銷燬這個Activity即被隱式持有的外部類
}
});
}
private static class MyThread extends Thread {//靜態的內部類不再持有外部類Activity
@Override
public void run() {
//執行耗時操作的子線程,在Activity銷燬了還沒結束
}
}
}
3.Handler內存泄漏,注意兩點可能引起內存泄漏的地方
- 非靜態內部類(第一種情況)
- 有延遲消息未在onDestory中移除
4.Context和單例的內存泄漏
解決方案:能使用Application上下文的地方儘量使用Application的上下文;
5.Cursor,File等資源未關閉
分析:資源對象比如Cursor、File等,往往都用了緩衝,不使用的時候應該關閉它們。把他們的引用置爲null,而不關閉它們,往往會造成內存泄漏。
解決方案:在資源對象不使用時,一定要確保它已經關閉,通常在finally語句中關閉,防止出現異常時,資源未被釋放的問題。
6.Register的廣播,EventBus等等都要unRegister
7.Bitmap釋放
臨時創建的某個相對比較大的bitmap對象,在經過變換得到新的bitmap對象之後,應該儘快回收原始的bitmap,這樣能夠更快釋放原始bitmap所佔用的空間。
避免靜態變量持有比較大的bitmap對象或者其他大的數據對象,如果已經持有,要儘快置空該靜態變量。
參考資料:
劉望舒的博客