1. 程式人生 > >7.如何避免OOM異常

7.如何避免OOM異常

1)什麼是OOM OOM,全稱“Out Of Memory”,翻譯成中文就是“記憶體用完了”,來源於java.lang.OutOfMemoryError。

2)為什麼會OOM?

為什麼會沒有記憶體了呢?原因不外乎有兩點:

1)分配的少了:比如虛擬機器本身可使用的記憶體(一般通過啟動時的VM引數指定)太少。

2)應用用的太多,並且用完沒釋放,浪費了。此時就會造成記憶體洩露或者記憶體溢位。

記憶體洩露:申請使用完的記憶體沒有釋放,導致虛擬機器不能再次使用該記憶體,此時這段記憶體就洩露了,因為申請者不用了,而又不能被虛擬機器分配給別人用。

記憶體溢位:申請的記憶體超出了JVM能提供的記憶體大小,此時稱之為溢位。

在c/c++裡面由於不存在垃圾回收機制,所以一旦我們使用了動態定義陣列例如:new等,都需要在使用完畢後執行設定delete,否則處理大資料時會發生記憶體溢位。

而在Java語言中,由於存在了垃圾自動回收機制,所以,我們一般不用去主動釋放不用的物件所佔的記憶體,也就是理論上來說,是不會存在“記憶體洩露”的。但是,如果編碼不當,比如,將某個物件的引用放到了全域性的Map中,雖然方法結束了,但是由於垃圾回收器會根據物件的引用情況來回收記憶體,導致該物件不能被及時的回收。如果該種情況出現次數多了,就會導致記憶體溢位,比如系統中經常使用的快取機制。Java中的記憶體洩露,不同於C++中的忘了delete,往往是邏輯上的原因洩露。

還有,雖然安卓自帶了GC機制,但是GC機制只在記憶體空間緊張的時候才會被觸發,也就是系統分配給該應用的記憶體空間所剩不多的時候,才會回收掉系統認為不會使用的物件和軟引用,如果應用突然在短時間內申請了很大的記憶體空間,而這個空間已經超過了剩餘記憶體,這個時候GC機制來沒來得及觸發,或者觸發後記憶體仍然不夠用,這個時候就會報OOM了。

而造成OOM的情況大致可以歸為以下幾類:

1.

資源物件沒關閉造成的記憶體洩露,try catch finally中將資源回收放到finally語句可以有效避免OOM。資源性物件比如:
1-1,Cursor
1-2,呼叫registerReceiver後未呼叫unregisterReceiver()
1-3,未關閉InputStream/OutputStream
1-4,Bitmap使用後未呼叫recycle()

2.

作用域不一樣,導致物件不能被垃圾回收器回收,比如:
2-1,非靜態內部類會隱式地持有外部類的引用,
2-2,Context洩露
概括一下,避免Context相關的記憶體洩露,記住以下事情:
   1、 不要保留對Context-Activity長時間的引用(對Activity的引用的時候,必須確保擁有和Activity一樣的生命週期)
   2、嘗試使用Context-Application來替代Context-Activity 3、如果你不想控制內部類的生命週期,應避免在Activity中使用非靜態的內部類,而應該使用靜態的內部類,並在其中建立一個對Activity的弱引用。
      這種情況的解決辦法是使用一個靜態的內部類,其中擁有對外部類的WeakReference。
2-3,Thread 引用其他物件也容易出現物件洩露。
2-4,onReceive方法裡執行了太多的操作
3.

記憶體壓力過大
  3-1,圖片資源載入過多,超過記憶體使用空間,例如Bitmap 的使用
  3-2,重複建立view,listview應該使用convertview和viewholder

3)處理方法

1.使用快取技術,比如LruCache、DiskLruCache、物件重複並且頻繁呼叫可以考慮物件池
2.對於引用生命週期不一樣的物件,可以用軟引用或弱引用SoftReferner WeakReferner

附上一個區分java裡的4種引用java的4種引用方式
3.對於資源物件 使用finally 強制關閉
4.記憶體壓力過大就要統一的管理記憶體