什么是OOM
? ? ? 移動端內存有限,手機給每個應用分配大小有限(Google 源生OS分配的內存為16M或者24M,但是不同廠家的ROM會修改)。當你使用的內存空間接近閥值,實例化新對象,需要分配新的內存空間是。就會報Out of Memory。
產生OOM的主要情況
1.同時加載大量大內存對象。主要體現在加載大量Bitmap(如幀動畫、RecyclerView),內存一下沖破閥值,產生OOM。
2.內存泄露。內存泄漏(Memory Leak)是指程序中己動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。當內存泄露到一定的量,接近閥值的時候,再去new新對象時就會OOM。
OOM的監測
查找內存泄漏可以使用Android Studio 自帶的Android Profiler工具(Android Studio3.0之后不僅可以監測內存,還可以監測CPU和network),也可以使用Square產品的LeadCanary(帖一個使用說明地址http://www.lxweimin.com/p/7db231163168)。
ps:看過一個面試問題。OOM能不能try/catch ? try/catch可以避免這次報錯,但是沒有真正處理內存問題,還是可能再下一分配內存的位置觸發。
OOM的解決方式
加載大量圖片
? 1. Bitmap壓縮。BitmapFactory,可以修改質量。比如把ARGB.8888改成RGB.565,bitmap占用內存會縮小一半(但是去掉了透明度,有些圖片沒法直接轉。Glide就默認設置的是RGB.565)。Options.inSampleSize可以壓縮圖片比例。
? 2. 緩存。LruCache是常用的第三方框架的圖片緩存處理方式。LruCache使用一個LinkedHashMap簡單的實現內存的緩存,沒有軟引用,都是強引用。如果添加的數據大于設置的最大值,就刪除最先緩存的數據來調整內存。
? 3. 軟引用&弱引用。當一個對象只有軟引用的時候,如果內存不足就會回收。當一個對象只有弱引用的時候,不管當前內存空間足夠與否,都會回收它的內存。不過,由于垃圾回收器是一個優先級很低的線程, 因此不一定會很快發現那些只具有弱引用的對象。
? 4.替換.png圖片。用較小的圖片替換.png,比如jpg或者svg等。
內存泄露
? ? 1.靜態變量導致內存泄露。如一個靜態變量持有當前Activity對象(但是很少有人會這么干吧)。
? ? 2.單例模式導致的內存泄露。單例模式的特點是它的生命周期與Application一致。所以單例模式實例化對象時,要用Application.context。
? ? 3.非靜態內部類和匿名內部類導致的內存泄露。非靜態內部類和匿名內部類隱式持有外部類的引用。Handler經常會不注意的時候寫成匿名內部類,就造成內存泄露。
? ? 4.資源未關閉導致的內存泄露。資源性對象比如Cursor,Stream、File文件等往往都用了一些緩沖,不使用的時候,應該及時關閉它們,否則會造成內存泄漏。
? ? 5.屬性動畫導致的內存泄露。屬性動畫中的無限循環動畫,如果沒有在onDestroy中停止,盡管界面上看不到動畫效果,但是Activity還是被View持有,會導致內存泄露。
? ? 6.集合容器導致內存泄露。當不需要對象時,并沒有把它的引用從集合中清理掉,也是一種內存泄露。
一些相關的細節
? ? 1.StringBuffer、StringBuilder和String一樣,也用來代表字符串。String類是不可變類,任何對String的改變都會引發新的String對象的生成;StringBuffer、StringBuilder則是可變類,任何對它所指代的字符串的改變都不會產生新的對象。StringBuffer考慮了線程安全,StringBuilder沒有。但是單線程StringBuilder效率更高。
? ? 2.內存抖動是由于短時間內有大量對象進出新生代區導致的,它伴隨著頻繁的GC。如在view的OnDraw方法中實例化對象,或者在循環中實例化不必要的對象。內存抖動可能會導致UI線程被頻繁阻塞,畫面卡頓。
總結
? ? OOM是同時加載大量大內存圖片或者gc無法及時回收對象,達到內存閥值之后的報錯。需要通過寫代碼時注意和后期監測來避免。