內(nèi)存檢測問題

1.如何檢測內(nèi)存問題

內(nèi)存泄漏:在基于Java的運行中,內(nèi)存泄漏是一種編程錯誤,它會導致應用程序?qū)σ呀?jīng)不需要再使用對象的引用。所以,無法回收該系統(tǒng)給該對象分配的內(nèi)存。最終導致OOM(OutOfMemoryError 內(nèi)存泄漏) 崩潰。

簡單來說就是:一些對象有著有限的生命周期。當這些對象所要做的事情完成了,我們希望他們會被回收掉。但是如果有一系列對這個對象的引用,那么在我們期待這個對象生命周期結(jié)束的時候被收回的時候,它是不會被回收的。它還會占用內(nèi)存,這就造成了內(nèi)存泄露。持續(xù)累加,內(nèi)存很快被耗盡。

通過工具來檢測是否錯在內(nèi)存泄漏的問題:

1. 通過Android Studio 自帶的工具 Android Profilter 找到MEMORY這個欄目 進行檢測


2.LeakCanary

第一步使用LeakCanary 我們需要先導入兩個依賴


debugImplementation'com.squareup.leakcanary:leakcanary-android:1.5.4'

releaseImplementation'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'

第二步在我們自定義繼承Application類的onCreate()方法里面

如果需要具體的檢查內(nèi)存泄漏的時候:

完成以上操作之后會在我們的手機或者模擬器上生成此應用


LeakCanary會找到并修復 多個內(nèi)存泄漏問題 將OOM崩潰的幾率降低94%。

詳解:

https://blog.csdn.net/qq_20280683/article/details/77964208

https://www.cnblogs.com/fuyaozhishang/p/7753013.html


2.內(nèi)存溢出和泄漏的區(qū)別及常見內(nèi)存問題

1.內(nèi)存泄漏 memory leak

指程序在申請內(nèi)存后,被某個對象一直持有,無法釋放已申請的內(nèi)存空間 一次內(nèi)存泄漏危害可以忽略,但是內(nèi)存泄漏堆積后果是很嚴重的。無論你有多少內(nèi)存,遲早被占光。

內(nèi)存泄漏又分好幾種情況:內(nèi)存泄漏的分類:以發(fā)生的方式來分類,內(nèi)存泄漏可

以分為 4 類:

1. 常發(fā)性內(nèi)存泄漏。

發(fā)生內(nèi)存泄漏的代碼會被多次執(zhí)行到,每次被執(zhí)行的時候都會導致一塊內(nèi)存泄

漏。

2. 偶發(fā)性內(nèi)存泄漏。

發(fā)生內(nèi)存泄漏的代碼只有在某些特定環(huán)境或操作過程下才會發(fā)生。常發(fā)性和偶發(fā)

性是相對的。對于特定的環(huán)境,偶發(fā)性的也許就變成了常發(fā)性的。所以測試環(huán)境

和測試方法對檢測內(nèi)存泄漏至關(guān)重要。

3. 一次性內(nèi)存泄漏。

發(fā)生內(nèi)存泄漏的代碼只會被執(zhí)行一次,或者由于算法上的缺陷,導致總會有一塊

僅且一塊內(nèi)存發(fā)生泄漏。比如,在類的構(gòu)造函數(shù)中分配內(nèi)存,在析構(gòu)函數(shù)中卻沒

有釋放該內(nèi)存,所以內(nèi)存泄漏只會發(fā)生一次。

4. 隱式內(nèi)存泄漏。

程序在運行過程中不停的分配內(nèi)存,但是直到結(jié)束的時候才釋放內(nèi)存。嚴格的說

這里并沒有發(fā)生內(nèi)存泄漏,因為最終程序釋放了所有申請的內(nèi)存。但是對于一個

服務器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內(nèi)存也可能導致最終

耗盡系統(tǒng)的所有內(nèi)存。所以,我們稱這類內(nèi)存泄漏為隱式內(nèi)存泄漏

內(nèi)存泄漏出現(xiàn)的一些實例:

1.單例--生命周期

分析:因為單例的生命周期和應用程序一樣,如果單例對象持有了某個不再需要

的對象的引用,(比如 Activity 的 context),那么這個 Activty 在單例沒有被

釋放前將不會被釋放。

解決:我們可以讓單例的引用為 Application 的 context

2.Handler

我們經(jīng)常會在 activity 中這樣使用 handler:

class MyHandler extends Handler{

...

}//使用

MyHandler mHandler=new MyHandler(this);

分析:由于 myHandler 是 Handler 的非靜態(tài)匿名內(nèi)部類的實例,所以它持有外部

類 Activity 的引用,Looper 線程不斷輪詢處理消息,Activity 退出時如果消息隊列

里還有未處理的消息,消息隊列的 Message 持有 mHandler 的引用,mHandler 又

持有 Activity 的引用,所以導致 Activity 無法及時被 GC 回收。從而造成內(nèi)存泄漏

解決方法:

1.創(chuàng)建靜態(tài) Handler 的匿名內(nèi)部類 static class MyHandler extends Handler

2.把對 Handler 持有的對象的使用弱引用 WeakReference context;

3. 在 Activity 銷 毀 時 移 除 消 息 隊 列 中 的 任 務 或 消

息 handler.removeCallbacksAndMessages(null);取消所有的消息的處理

3.非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實例

分析:非靜態(tài)內(nèi)部類可以自由使用外部類的所有變量和方法,非靜態(tài)內(nèi)部類,它

默認持有外部類的引用,此時如果在外部類創(chuàng)建靜態(tài) static 的內(nèi)部類的實例,或

是聲明為 static 靜態(tài)成員變量,這樣就導致內(nèi)部類的生命周期和應用程序一樣長,

導致 Activity 無法正常銷毀。

解決方法:將非靜態(tài)內(nèi)部類轉(zhuǎn)為靜態(tài)內(nèi)部類,這樣就不會隱式持有外部類。

4.線程造成的內(nèi)存泄漏

分析:我們常用的異步任務(如:AsyncTask)和 Runnable 都是匿名內(nèi)部類,所以它

們對當前的 Activity 都有一個隱式引用,若 Activity 銷毀,但是線程的任務還沒有

完成,就會造成 Activity 的 gc 無法回收。

解決方法:

1.使用靜態(tài)內(nèi)部類 將 Runnable 內(nèi)部類、AsyncTask 內(nèi)部類聲明為靜態(tài)。

2.銷毀時取消相應的任務。

5.資源未關(guān)閉

BroadcastReceiver、File、Cursor、Stream、Bitmap 及時關(guān)閉和注銷、否則不會

被回收造成內(nèi)存泄漏。

6.系統(tǒng)服務、監(jiān)聽器未注銷/移除

有一些系統(tǒng)服務或監(jiān)聽器在不需要使用的時候再及時移除或注銷

7.動畫

對于有一些屬性動畫,屬性為無限循環(huán),這時候我們可以在 onStop 中停止動畫。

2.內(nèi)存溢出 out of memory

指程序在申請內(nèi)存時,沒有足夠的內(nèi)存空間供其使用,出現(xiàn) out of memory;

比如申請了一個 integer,但給它存了 long 才能存下的數(shù),那就是內(nèi)存溢出。

內(nèi)存溢出就是你要求分配的內(nèi)存超出了系統(tǒng)能給你的,系統(tǒng)不能滿足需求,于是

產(chǎn)生溢出。

內(nèi)存溢出出現(xiàn)的情況:

1.對象內(nèi)存過大(圖片、Bitmap、XML)造成內(nèi)存超出

2.布局重復加載(比如列表控件 adapter 中沒有復用 view 等)、界面橫豎屏切換。

應用資源過多,來不及加載。

3.還有我們上面介紹的內(nèi)存泄漏,過多的內(nèi)存泄漏,也會導致虛擬機可分配的內(nèi)

存越來越少,這樣也是容易出現(xiàn) OOM。

關(guān)于避免 OOM:

A:減少 OOM 最重要的就是要盡量減少新分配出來的對象占用內(nèi)存的大小,盡

量使用更加輕量的對象。

避免內(nèi)存泄漏,見上面我們總結(jié)的一些情況(比如:善用 static、避免無關(guān)引

用無法釋放、善用 SoftReference/WeakReference/LruCache、謹慎 handler、線程

等、及時關(guān)閉無用服務、監(jiān)聽。)

如果代碼中有大量字符串拼接操作,使用 StringBuilder 代替"+"。

Bitmap 的不當處理極可能造成 OOM,其實很多 OOM 的原因都來源于此,所以

一定要十分重視對 Bitmap 的優(yōu)化。

B:一直說 OOM 的出現(xiàn)是因為應用占用的內(nèi)存(主要是指的 heap)超出了系統(tǒng)

給我們分配內(nèi)存的最大值,那有沒有可能增加系統(tǒng)為我們的 App 分配的內(nèi)存大

小。--->使用 largeHeap,會請求系統(tǒng)為 Dalvik 虛擬機分配更大的內(nèi)存空間。使用

起來也很方便,只需在 manifest 文件 application 節(jié)點加入 android:largeHeap= “ true ” 即 可 。 作 為 驗 證 , 可 以 通 過 打 印 兩 者 的 值 。

ActivityManager.getMemoryClass() 獲 得 應 用 正 常 情 況 下 內(nèi) 存 的 大 小 ,

ActivityManager.getLargeMemoryClass()可以獲得使用 largeHeap 最大的內(nèi)存大小。

但是這個東西需要慎用,不建議使用

3.內(nèi)存優(yōu)化的方案

1.對象引用。強引用、軟引用、弱引用、虛引用四種引用類型,根據(jù)業(yè)務需求合

理使用不同,選擇不同的引用類型。

2. 減少不必要的內(nèi)存開銷。注意自動裝箱,增加內(nèi)存復用,比如有效利用系統(tǒng)自

帶的資源、視圖復用、對象池、Bitmap 對象的復用。

3. 使用最優(yōu)的數(shù)據(jù)類型。比如針對數(shù)據(jù)類容器結(jié)構(gòu),可以使用 ArrayMap 數(shù)據(jù)結(jié)

構(gòu),避免使用枚舉類型,使用緩存 Lrucache 等等。

4. 圖片內(nèi)存優(yōu)化。可以設置位圖規(guī)格,根據(jù)采樣因子做壓縮,用一些圖片緩存方

式對圖片進行管理等等


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容