本文目錄:
- 內(nèi)存泄漏的定義、表現(xiàn)、危害、情景,及避免OOM的技巧
- Memory Analyzer Tool(MAT)簡(jiǎn)述、下載、安裝
- 內(nèi)存泄漏解決實(shí)戰(zhàn)
- 解決方法小結(jié)
內(nèi)存泄漏的定義、表現(xiàn)、危害、情景,及避免OOM的技巧
定義
-
Android內(nèi)存泄漏指的是進(jìn)程中某些對(duì)象(垃圾對(duì)象)已經(jīng)沒(méi)有使用價(jià)值了,
但是它們卻可以直接或間接地引用到gc roots導(dǎo)致無(wú)法被GC回收。
無(wú)用的對(duì)象占據(jù)著內(nèi)存空間,使得實(shí)際可使用內(nèi)存變小,形象地說(shuō)法就是內(nèi)存泄漏了。
表現(xiàn)
-
內(nèi)存抖動(dòng)、可用內(nèi)存逐漸變少
上一篇博客寫(xiě)到,
內(nèi)存抖動(dòng)可能是
因?yàn)?code>代碼邏輯問(wèn)題 導(dǎo)致內(nèi)存被不斷地進(jìn)行分配和回收;
當(dāng)然一個(gè)地方它的內(nèi)存一直在抖動(dòng),
還有可能是由于內(nèi)存泄漏引起的,
比如說(shuō),內(nèi)存泄漏導(dǎo)致可用內(nèi)存逐漸減少,
這時(shí)候系統(tǒng)為了增加可用內(nèi)存,就會(huì)一直不斷地進(jìn)行GC,
導(dǎo)致內(nèi)存一直在抖動(dòng)??!
危害
內(nèi)存不足 -- GC頻繁 -- OOM
可能出現(xiàn)、需要注意的情景
-
.
1. 非靜態(tài)內(nèi)部類(lèi)的靜態(tài)實(shí)例
(“類(lèi)”是這個(gè)類(lèi)的類(lèi)型,實(shí)例是new 出來(lái)的實(shí)例)
非靜態(tài)內(nèi)部類(lèi)會(huì)維持一個(gè)到外部類(lèi)實(shí)例的引用,
如果非靜態(tài)內(nèi)部類(lèi)的實(shí)例是靜態(tài)的,
就會(huì)間接長(zhǎng)期維持著外部類(lèi)的引用,阻止被回收掉。
解決辦法:
使用靜態(tài)內(nèi)部類(lèi),
靜態(tài)內(nèi)部類(lèi)實(shí)例,不會(huì)維持一個(gè)到外部類(lèi)實(shí)例的引用!
2.多線(xiàn)程相關(guān)的匿名內(nèi)部類(lèi)和非靜態(tài)內(nèi)部類(lèi)
匿名內(nèi)部類(lèi)同樣會(huì)持有外部類(lèi)的引用,
如果在線(xiàn)程中執(zhí)行耗時(shí)操作
就有可能發(fā)生內(nèi)存泄漏,導(dǎo)致外部類(lèi)無(wú)法被回收,直到耗時(shí)任務(wù)結(jié)束,
解決辦法:
在頁(yè)面退出時(shí)結(jié)束線(xiàn)程中的任務(wù)
3. Handler臨時(shí)性?xún)?nèi)存泄露
Handler導(dǎo)致的內(nèi)存泄漏也可以被歸納為非靜態(tài)內(nèi)部類(lèi)實(shí)例(這里特指Handler實(shí)例)導(dǎo)致的;
Handler通過(guò)發(fā)送Message與主線(xiàn)程交互,
Message發(fā)出之后是存儲(chǔ)在MessageQueue中的,
有些Message也不是馬上就被處理的。
在Message中存在一個(gè)target,是指向Handler的一個(gè)引用,
如果Message在Queue中存在的時(shí)間過(guò)長(zhǎng),
就會(huì)導(dǎo)致Handler無(wú)法被回收。
如果Handler是非靜態(tài)的,
則會(huì)導(dǎo)致Handler的外部類(lèi),如Activity或者Service不會(huì)被回收。
由于AsyncTask內(nèi)部也是Handler機(jī)制,同樣存在內(nèi)存泄漏的風(fēng)險(xiǎn)。
此種內(nèi)存泄露,一般是臨時(shí)性的。
解決辦法:
A.使用靜態(tài)handler,外部類(lèi)引用使用弱引用處理
B.在退出頁(yè)面時(shí)移除消息隊(duì)列中的消息
4.Context導(dǎo)致內(nèi)存泄漏
根據(jù)場(chǎng)景確定使用Activity的Context還是Application的Context,
因?yàn)槎?code>生命周期不同,
對(duì)于不必須使用Activity的Context的場(chǎng)景(Dialog),一律采用Application的Context?。。?br>單例模式是最常見(jiàn)的發(fā)生此泄漏的場(chǎng)景,
比如傳入一個(gè)Activity的Context被靜態(tài)類(lèi)引用,導(dǎo)致無(wú)法回收
5.靜態(tài)View導(dǎo)致泄漏
使用靜態(tài)View可以避免每次啟動(dòng)Activity都去讀取并渲染View?。?!
但是靜態(tài)View會(huì)持有Activity的引用,導(dǎo)致無(wú)法回收?。?!
解決辦法:
在Activity銷(xiāo)毀的時(shí)候?qū)?code>靜態(tài)View設(shè)置為null
(View一旦被加載到界面中將會(huì)持有一個(gè)Context對(duì)象的引用,
在這里,這個(gè)context對(duì)象是我們的Activity,?。。?br> 聲明一個(gè)靜態(tài)變量引用這個(gè)View,也就引用了activity)
6.WebView導(dǎo)致的內(nèi)存泄漏
WebView只要使用一次,內(nèi)存就不會(huì)被釋放,
所以WebView都存在內(nèi)存泄漏的問(wèn)題,!??!
通常的解決辦法:
為WebView單開(kāi)一個(gè)進(jìn)程,
使用AIDL進(jìn)行通信,根據(jù)業(yè)務(wù)需求在合適的時(shí)機(jī)釋放掉?。?/strong>
7. 資源對(duì)象未關(guān)閉
資源性對(duì)象如Cursor、File、Socket等,
內(nèi)部往往都使用了緩沖,容易造成內(nèi)存泄漏,
應(yīng)該在使用后及時(shí)關(guān)閉。
未在finally中關(guān)閉,
會(huì)導(dǎo)致異常情況下資源對(duì)象未被釋放的隱患。
8.集合中的對(duì)象未清理
我們通常把一些對(duì)象的引用加入到了集合容器(比如ArrayList)中,
當(dāng)我們不需要集合中的某個(gè)對(duì)象時(shí),
如果沒(méi)有把它的引用從集合中清理掉,這個(gè)集合就會(huì)越來(lái)越大。
如果這個(gè)集合是static的話(huà),那情況就更嚴(yán)重了。
所以要在退出程序之前,
需要調(diào)用集合實(shí)例的clear() 將集合里的東西clear掉,
然后將集合實(shí)例置為null,再退出程序。
9.Bitmap導(dǎo)致內(nèi)存泄漏
bitmap是比較占內(nèi)存的,所以一定要在不使用的時(shí)候及時(shí)進(jìn)行清理;
同時(shí)避免靜態(tài)變量持有大的bitmap對(duì)象;
10.監(jiān)聽(tīng)器未關(guān)閉,注冊(cè)對(duì)象未反注冊(cè)
很多需要register和unregister的系統(tǒng)服務(wù)
要在合適的時(shí)候進(jìn)行unregister,手動(dòng)添加的listener也需要及時(shí)移除
11. 類(lèi)的靜態(tài)變量持有大數(shù)據(jù)對(duì)象
靜態(tài)變量長(zhǎng)期維持到大數(shù)據(jù)對(duì)象的引用,阻止垃圾回收。
如何避免OOM?
1.Bitmap優(yōu)化
Bitmap非常消耗內(nèi)存,
而且在A(yíng)ndroid中,讀取bitmap時(shí),
一般分配給虛擬機(jī)的圖片堆棧只有8M,所以經(jīng)常造成OOM問(wèn)題。
所以有必要針對(duì)Bitmap的使用作出優(yōu)化:
1.1. 圖片顯示:加載合適尺寸的圖片,比如顯示縮略圖的地方不要加載大圖。
1.2. 圖片回收:使用完bitmap,及時(shí)使用Bitmap.recycle()回收。
問(wèn)題:Android不是自身具備垃圾回收機(jī)制嗎?此處為何要手動(dòng)回收。
Bitmap對(duì)象不是new生成的,而是通過(guò)BitmapFactory生產(chǎn)的。
通過(guò)源碼可發(fā)現(xiàn)是通過(guò)調(diào)用JNI生成Bitmap對(duì)象(nativeDecodeStream()等方法)。
所以,
加載bitmap到內(nèi)存里包括兩部分,
Dalvik(ART)內(nèi)存和Linux kernel內(nèi)存。
前者會(huì)被虛擬機(jī)自動(dòng)回收。
而后者必須通過(guò)recycle()方法,
內(nèi)部調(diào)用nativeRecycle()讓linux kernel回收。
1.3. 捕獲OOM異常:程序中設(shè)定如果發(fā)生OOM的應(yīng)急處理方式。
1.4. 圖片緩存:內(nèi)存緩存、硬盤(pán)緩存等
1.5. 圖片壓縮:直接使用ImageView顯示Bitmap時(shí)會(huì)占很多資源,
尤其當(dāng)圖片較大時(shí)容易發(fā)生OOM。
可以使用BitMapFactory.Options對(duì)圖片進(jìn)行壓縮。
1.6. 圖片像素(質(zhì)量):android默認(rèn)顏色模式為ARGB_8888, 顯示質(zhì)量最高,占用內(nèi)存最大。
若要求不高時(shí)可采用RGB_565等模式。
還可以使用WebP;
圖片大小:圖片長(zhǎng)度 * 寬度 * 單位像素 所占據(jù)字節(jié)數(shù)
ARGB_4444:每個(gè)像素占用2byte內(nèi)存
ARGB_8888:每個(gè)像素占用4byte內(nèi)存 (默認(rèn))
RGB_565:每個(gè)像素占用2byte內(nèi)存
1.7. 考慮使用inBitmap;圖片優(yōu)化之inBitmap
2. 巧用對(duì)象引用類(lèi)型
強(qiáng)引用 strong:Object object=new Object()。
當(dāng)內(nèi)存不足時(shí),Java虛擬機(jī)寧愿拋出OOM內(nèi)存溢出異常,
也不會(huì)輕易回收強(qiáng)引用對(duì)象來(lái)解決內(nèi)存不足問(wèn)題;
軟引用 soft:只有當(dāng)內(nèi)存達(dá)到某個(gè)閾值時(shí)才會(huì)去回收,常用于緩存;
弱引用 weak:只要被GC線(xiàn)程掃描到了就進(jìn)行回收;
虛引用如果想要避免OOM發(fā)生,則使用軟引用對(duì)象,即當(dāng)內(nèi)存快不足時(shí)進(jìn)行回收;
如果想盡快回收某些占用內(nèi)存較大的對(duì)象,例如bitmap,可以使用弱引用,能被快速回收。
不過(guò)如果要對(duì)bitmap作緩存就不要使用弱引用,因?yàn)楹芸炀蜁?huì)被GC回收,導(dǎo)致緩存失敗。
3. 使用 池 pool 內(nèi)存對(duì)象的重復(fù)利用
-
對(duì)象池:如果某個(gè)對(duì)象在創(chuàng)建時(shí),需要較大的資源開(kāi)銷(xiāo),
那么可以將其放入對(duì)象池,
即將對(duì)象保存起來(lái),下次需要時(shí)直接取出使用,
而不用再次創(chuàng)建對(duì)象。
當(dāng)然,維護(hù)對(duì)象池也需要一定開(kāi)銷(xiāo),故要衡量。
a.
ListView/GridView源碼可以看到重用的情況ConvertView的復(fù)用。
RecyclerView中Recycler源碼。
b.Bitmap的復(fù)用
Listview等要顯示大量圖片。
需要使用LRU緩存機(jī)制來(lái)復(fù)用圖片。
-
線(xiàn)程池:與對(duì)象池差不多,
將線(xiàn)程對(duì)象放在池中供反復(fù)使用,減少反復(fù)創(chuàng)建線(xiàn)程的開(kāi)銷(xiāo)。
4. 使用更加輕量的數(shù)據(jù)結(jié)構(gòu):
考慮適當(dāng)?shù)那闆r下,
使用更加高效的安卓專(zhuān)門(mén)為手機(jī)研發(fā)的數(shù)據(jù)結(jié)構(gòu)類(lèi)
ArrayMap/SparseArray/SparseLongMap/SparseIntMap/SparseBoolMap去
替代HashMap等傳統(tǒng)數(shù)據(jù)結(jié)構(gòu)。
HashMap.put(string,Object);Object o = map.get(string);會(huì)導(dǎo)致一些沒(méi)必要的自動(dòng)裝箱和拆箱。
HashMap,HashMap更耗內(nèi)存,
因?yàn)樗枰?code>額外的實(shí)例對(duì)象來(lái)記錄Mapping操作,
SparseArray更加高效,
因?yàn)樗苊饬?code>Key Value的自動(dòng)裝箱,和裝箱后的解箱操作;
5. StringBuilder替代String: 在有些時(shí)候,
代碼中會(huì)需要使用到大量的字符串拼接的操作,
這種時(shí)候有必要考慮使用StringBuilder來(lái)替代頻繁的“+”
6.避免在類(lèi)似onDraw這樣的方法中創(chuàng)建對(duì)象,
因?yàn)樗鼤?huì)迅速占用大量?jī)?nèi)存,引起頻繁的GC甚至內(nèi)存抖動(dòng)
參考文章
Memory Analyzer Tool(MAT)簡(jiǎn)述、下載、安裝
一個(gè)強(qiáng)大的
Java Heap 工具,
相對(duì)于Memory Profiler(MP)的簡(jiǎn)單分析,
MAT可以對(duì)Java內(nèi)存做一個(gè)深入的分析;MP不能確認(rèn)問(wèn)題,確認(rèn)問(wèn)題需要MAT
MAT下載鏈接
這里可以下載獨(dú)立版的MAT工具;
MAT安裝及使用教程
文件下載下來(lái)的是一個(gè)zip壓縮包,解壓壓縮包,
進(jìn)入到如下目錄,
雙擊對(duì)應(yīng)的exe文件,
即可開(kāi)始使用:
工具初始界面如下:
- 轉(zhuǎn)換:hprof-conv 原文件路徑 轉(zhuǎn)換后文件路徑
內(nèi)存泄漏解決實(shí)戰(zhàn)
- 程序使用Bitmap來(lái)舉例,
因?yàn)閷?duì)于一般程序,最大的問(wèn)題往往都在Bitmap上,
因?yàn)樗牡膬?nèi)存非常多,
將其作為內(nèi)存泄漏的案例,效果會(huì)比較明顯;
定義類(lèi)似觀(guān)察者的一個(gè)接口CallBack
以及類(lèi)似被觀(guān)察者的一個(gè)實(shí)現(xiàn)類(lèi)CallBackManager:
public interface CallBack {
void dpOperate();
}
----
public class CallBackManager {
public static ArrayList<CallBack> sCallBacks = new ArrayList<>();
public static void addCallBack(CallBack callBack) {
sCallBacks.add(callBack);
}
public static void removeCallBack(CallBack callBack) {
sCallBacks.remove(callBack);
}
}
activity_memoryleak.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_memoryleak"
android:layout_width="50dp"
android:layout_height="50dp" />
</LinearLayout>
MemoryLeakActivity.java:
/**
* 模擬內(nèi)存泄露的Activity
*/
public class MemoryLeakActivity extends AppCompatActivity implements CallBack{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memoryleak);
ImageView imageView = findViewById(R.id.iv_memoryleak);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.splash);
imageView.setImageBitmap(bitmap);
CallBackManager.addCallBack(this);
}
@Override
public void dpOperate() {
// do sth
}
}
dpOperate()模擬操作業(yè)務(wù)的邏輯方法;
在反復(fù)地退出關(guān)閉和打開(kāi)進(jìn)入 以上界面的時(shí)候,
就可能引起內(nèi)存泄漏;-
運(yùn)行程序,打開(kāi)
MP,初始曲線(xiàn)圖是平穩(wěn)的:
-
建立一個(gè)簡(jiǎn)單的界面,如
MainActivity,
可以點(diǎn)擊進(jìn)入MemoryLeakActivity,
然后不斷地在MainActivity和MemoryLeakActivity之間切換,
即反復(fù)地退出關(guān)閉和打開(kāi)進(jìn)入 MemoryLeakActivity,
這時(shí)候可以看到MP的圖示,內(nèi)存曲線(xiàn)在階梯狀上升,
也就是說(shuō)我們的可用內(nèi)存在逐漸減少了,?。?!
出現(xiàn)了這種情況,我們基本就可以斷定,界面可能就是出現(xiàn)了內(nèi)存泄漏?。。。?/strong>
MP工具這里,
只能幫我們大致斷定這個(gè)界面是出現(xiàn)了內(nèi)存泄漏,
但是它沒(méi)有辦法幫助我們 斷定那個(gè)地方有 內(nèi)存泄漏,
來(lái)讓我們有的放矢 修改代碼;
這里就需要MAT上場(chǎng)了;-
首先需要點(diǎn)擊堆轉(zhuǎn)儲(chǔ)按鈕,
MP工具會(huì)記錄一段時(shí)間的內(nèi)存分配情況,
然后我們可以對(duì)這段記錄進(jìn)行Dump,
下載成文件保存在本地: -
保存成功:
-
接著,
在A(yíng)S的下方的Terminal終端欄,
Android studio 進(jìn)入 adb 命令 ---使用terminal 終端 進(jìn)入sdk 找到 platform-tools 目錄進(jìn)入即可
-
使用cd指令,
進(jìn)入到配套SDK目錄下的platform-tools目錄,
回車(chē),到達(dá)工具目錄; -
接著在使用platform-tools目錄目錄下,
使用hprof-conv工具指令,
轉(zhuǎn)化堆轉(zhuǎn)儲(chǔ)保存下來(lái)的文件: -
回車(chē)后,轉(zhuǎn)換成功:
-
接著如文首下載好獨(dú)立版本的MAT工具,
使用MAT工具,打開(kāi)我們剛剛轉(zhuǎn)化完的文件
(工具界面左上角File ---> openFile ---> 選擇文件打開(kāi)): -
打開(kāi)之后,MAT 就會(huì)對(duì)我們的 堆轉(zhuǎn)儲(chǔ)轉(zhuǎn)換后的文件 進(jìn)行分析:
-
接下來(lái)目的是通過(guò)MAT來(lái)找到內(nèi)存泄漏的位置,
點(diǎn)擊左下角有個(gè)Histogram:
-
進(jìn)入Histogram界面后,可以看到最上面有個(gè)匹配搜索:
-
這里搜索我們Activity的名字:MemoryLeak
可以看到搜索結(jié)果,
可以看到這里Objects這里顯示著有18個(gè),
也就是說(shuō)現(xiàn)在內(nèi)存當(dāng)中,竟存在著18個(gè)MemoryLeakActivity的實(shí)例
(emmmmm,也就是我們剛剛在試驗(yàn)的時(shí)候,
反復(fù)退出進(jìn)入了18次MemoryLeakActivity),
這是非常不合理的!
后面是
Shallow Heap:堆中 此類(lèi)型所有實(shí)例 的總大?。ㄒ宰止?jié)為單位)
Retained Heap:為此 類(lèi)型的所有實(shí)例 而 保留的內(nèi)存總大小(以字節(jié)為單位)
接下來(lái),點(diǎn)擊搜索出來(lái)的實(shí)例,右鍵,
選擇List objects -> with incoming references,
(with incoming reference
incoming 指過(guò)來(lái)
即指的是引用到該選中實(shí)例的實(shí)例,即查看本實(shí)例被誰(shuí)引用;
with outcoming references
outcoming 指出去
被該選中實(shí)例引用的實(shí)例,即查看本實(shí)例引用了誰(shuí);)
下面是點(diǎn)擊后彈出的搜索結(jié)果:
選擇第一個(gè)結(jié)果Item,右鍵,
Merge Shortest Path To GC Roots ---> exclude all phantom/weak/soft etc. references:
查看這個(gè)對(duì)象跟GC Root 之間的一個(gè)路徑---exclude(除了)虛、弱引用、軟引用之外,即只看強(qiáng)引用。
(從GC上說(shuō),除了強(qiáng)引用外,
其他的引用在JVM需要的情況下是都可以 被GC掉的?。?!
所以?。。?br> 如果一個(gè)對(duì)象始終無(wú)法被GC,就是因?yàn)閺?qiáng)引用的存在,?。?!
從而導(dǎo)致在GC的過(guò)程中一直得不到回收,
因此就內(nèi)存溢出了)
下面是分析結(jié)果:我們可以看到,
它從左上角往下是一個(gè)GC路徑,
可以看到最后一個(gè)行的item其圖標(biāo)左下角有一個(gè)小圓圈,
這是我們真正要關(guān)注的地方,
如上圖所示,也就是說(shuō)最終MAT這里給了我們的一個(gè)信息,
即上述所說(shuō)的17個(gè) MemoryLeakActivity 實(shí)例實(shí)際上
是被CallBackManager這個(gè)類(lèi)中的sCallBack這個(gè)實(shí)例(對(duì)象)給引用了,
至此,我們便可以回到代碼中,尋找這個(gè)CallBackManager,去修改對(duì)應(yīng)的代碼;
- 可以看到果然我們一開(kāi)始便定義了一個(gè)
sCallBacks實(shí)例,
MemoryLeakActivity 實(shí)例就一直被它所引用著?。。?!
因?yàn)檫@里sCallBacks實(shí)例它被static修飾著,?。?!
Android中被static修飾著的變量,它的生命周期是跟APP的整個(gè)周期一樣長(zhǎng)的,
所以我們打開(kāi)進(jìn)入MemoryLeakActivity的時(shí)候,
onCreate()中我們就把當(dāng)前的一個(gè)MemoryLeakActivity實(shí)例加到sCallBacks這個(gè)List實(shí)例中去,而每次退出銷(xiāo)毀MemoryLeakActivity的時(shí)候,
卻沒(méi)有在sCallBacks中移除剛剛添加的這個(gè)MemoryLeakActivity實(shí)例,
而且MemoryLeakActivity被銷(xiāo)毀的時(shí)候,我們沒(méi)有退出APP,
所以sCallBacks實(shí)例也一直存在,
如此一來(lái),
每次反復(fù)進(jìn)入打開(kāi)和退出銷(xiāo)毀 MemoryLeakActivity的,
sCallBacks實(shí)例就會(huì)多持有一個(gè)MemoryLeakActivity實(shí)例,
多次進(jìn)出MemoryLeakActivity累積下來(lái),
sCallBacks實(shí)例持有的引用就只增不減,
而且這里的持有都是強(qiáng)應(yīng)用持有,不會(huì)被GC回收的,
那無(wú)用的對(duì)象占據(jù)的內(nèi)存空間就越來(lái)越大,實(shí)際可使用內(nèi)存逐漸變小,導(dǎo)致內(nèi)存泄漏了!!
解決方法的話(huà),
就是在onDestroy()中,
將MemoryLeakActivity自身從sCallBacks實(shí)例中移除了,
這樣每次退出銷(xiāo)毀MemoryLeakActivity的時(shí)候,
onCreate()中添加進(jìn)sCallBacks實(shí)例的MemoryLeakActivity自身,就會(huì)在銷(xiāo)毀前被移除,
由此解決內(nèi)存泄漏:
/**
* 模擬內(nèi)存泄露的Activity
*/
public class MemoryLeakActivity extends AppCompatActivity implements CallBack{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_memoryleak);
ImageView imageView = findViewById(R.id.iv_memoryleak);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.splash);
imageView.setImageBitmap(bitmap);
CallBackManager.addCallBack(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
CallBackManager.removeCallBack(this);
}
@Override
public void dpOperate() {
// do sth
}
}
解決方法小結(jié)
使用MP初步觀(guān)察,
發(fā)現(xiàn)不斷上升或者居高不下的內(nèi)存曲線(xiàn),
即可用內(nèi)存逐漸減少的現(xiàn)象,
便可以判斷這個(gè)地方是可能出現(xiàn)了內(nèi)存泄漏;使用
MP的堆轉(zhuǎn)儲(chǔ),
將一段時(shí)間內(nèi)的分配情況記錄成文件,
導(dǎo)出并保存這份文件,
基于A(yíng)S的Terminal終端欄,
使用hprof-conv工具指令
轉(zhuǎn)化堆轉(zhuǎn)儲(chǔ)保存下來(lái)的文件;使用
MAT打開(kāi)(OpenFile)并分析hprof-conv的轉(zhuǎn)化生成的文件;點(diǎn)擊進(jìn)入
Histogram界面,
篩選可疑實(shí)例;
觀(guān)察Objects欄值是否異常;點(diǎn)擊對(duì)應(yīng)的實(shí)例,右鍵,
選擇List objects -> with incoming references,
查看本實(shí)例被誰(shuí)引用;
彈出的搜索結(jié)果界面;
在上述彈出的搜索結(jié)果界面中,
選擇一個(gè)結(jié)果Item,右鍵,
Merge Shortest Path To GC Roots ---> exclude all phantom/weak/soft etc. references:
查看這個(gè)對(duì)象跟 GC Root 之間的一個(gè)路徑---exclude(除了)虛、弱引用、軟引用之外,即只看強(qiáng)引用。
彈出分析結(jié)果界面,界面中 根據(jù)圖示(橙紅小圓圈),
可以找到引用了選中實(shí)例的實(shí)例對(duì)象及其所在的類(lèi)文件名;根據(jù)上述定位的
類(lèi)文件名以及持有引用的對(duì)象名,
找到相應(yīng)的位置,
排查并修改代碼,
解決問(wèn)題;

























