国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android Studio+MAT實(shí)戰(zhàn)內(nèi)存泄漏

2019-10-22 18:19:18
字體:
供稿:網(wǎng)友

對(duì)于內(nèi)存泄漏,在Android中如果不注意的話,還是很容易出現(xiàn)的,尤其是在Activity中,比較容易出現(xiàn),下面我就說下自己是如何查找內(nèi)存泄露的。

首先什么是內(nèi)存泄漏?

內(nèi)存泄漏就是一些已經(jīng)不使用的對(duì)象還存在于內(nèi)存之中且垃圾回收機(jī)制無法回收它們,導(dǎo)致它們常駐內(nèi)存,會(huì)使內(nèi)存消耗越來越大,最終導(dǎo)致程序性能變差。
其中在Android虛擬機(jī)中采用的是根節(jié)點(diǎn)搜索算法枚舉根節(jié)點(diǎn)判斷是否是垃圾,虛擬機(jī)會(huì)從GC Roots開始遍歷,如果一個(gè)節(jié)點(diǎn)找不到一條到達(dá)GC Roots的路線,也就是沒和GC Roots 相連,那么就證明該引用無效,可以被回收,內(nèi)存泄漏就是存在一些不好的調(diào)用導(dǎo)致一些無用對(duì)象和GC Roots相連,無法被回收。

既然知道了什么是內(nèi)存泄漏,自然就知道如何去避免了,就是我們?cè)趯懘a的時(shí)候盡量注意產(chǎn)生對(duì)無用對(duì)象長(zhǎng)時(shí)間的引用,說起來簡(jiǎn)單,但是需要足夠的經(jīng)驗(yàn)才能達(dá)到,所以內(nèi)存泄漏還是比較容易出現(xiàn)的,既然不容易完全避免,那么我們就要能發(fā)現(xiàn)程序中出現(xiàn)的內(nèi)存泄漏并修復(fù)它,
下面我就說說如何發(fā)現(xiàn)內(nèi)存泄漏的吧。

查找內(nèi)存泄漏:

比如說下面這個(gè)代碼:

public class MainActivity extends AppCompatActivity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    String string = new String();  }  public void click(View view){    Intent intent = new Intent();    intent.setClass(getApplicationContext(),SecondActivity.class);    startActivity(intent);  }}
public class SecondActivity extends AppCompatActivity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_second);    Runnable runnable = new Runnable() {      @Override      public void run() {        try {          Thread.sleep(8000000L);        } catch (InterruptedException e) {          e.printStackTrace();        }      }    };    new Thread(runnable).start();  }}

 

每次跳轉(zhuǎn)到這個(gè)Activity中時(shí)都會(huì)調(diào)用一個(gè)線程,然后這個(gè)線程會(huì)執(zhí)行runnable的run方法 由于Runnable是一個(gè)匿名內(nèi)部對(duì)象 所以握有SecondActivity的引用,因此很簡(jiǎn)單的兩個(gè)Activity,可由MainActivity跳轉(zhuǎn)到SecondActivity中,下面我們從MainActivity跳到SecondActivity 然后從SecondActivity返回MainActivity,連續(xù)這樣5次 ,最終返回MainActivity,按照常理來說,我們從SecondActivity返回MainActivity之后 SecondActivity就該被銷毀回收,可實(shí)際可能并不是這樣。

這時(shí)候要判斷發(fā)沒發(fā)生內(nèi)存溢出就要使用工具了!下面有兩種方式

1.利用MAT工具查找

首先打開AS中的Android Device Monitor工具 具體位置如下圖:

Android,Studio,MAT,內(nèi)存泄漏

打開后會(huì)出現(xiàn)如下的界面

Android,Studio,MAT,內(nèi)存泄漏

先選中你要檢測(cè)的應(yīng)用的包名,然后點(diǎn)擊下圖畫圈的地方,會(huì)在程序包名后標(biāo)記一個(gè)圖標(biāo)

Android,Studio,MAT,內(nèi)存泄漏

接下來要做的就是操作我們的app 來回跳轉(zhuǎn)5次。

之后點(diǎn)擊下圖的圖標(biāo) 就可導(dǎo)出hprof文件進(jìn)行分析了

Android,Studio,MAT,內(nèi)存泄漏

導(dǎo)出文件如下圖所示:

Android,Studio,MAT,內(nèi)存泄漏

得到了hprof文件 我們就可以利用MAT工具進(jìn)行分析了,

打開MAT工具

如果沒有 可以在下面網(wǎng)址下載

MAT工具下載地址

Android,Studio,MAT,內(nèi)存泄漏

界面如下圖所示:

Android,Studio,MAT,內(nèi)存泄漏

打開我們先前導(dǎo)出的hprof文件 ,不出意外會(huì)報(bào)下面的錯(cuò)誤

Android,Studio,MAT,內(nèi)存泄漏

這是因?yàn)镸AT是用來分析java程序的hprof文件的 與Android導(dǎo)出的hprof有一定的格式區(qū)別,因此我們需要把導(dǎo)出的hprof文件轉(zhuǎn)換一下,sdk中提供給我們轉(zhuǎn)換的工具 hprof-conv.exe 在下圖的位置

Android,Studio,MAT,內(nèi)存泄漏

接下來我們cd到這個(gè)路徑下執(zhí)行這個(gè)命令轉(zhuǎn)換我們的hprof文件即可,如下圖

Android,Studio,MAT,內(nèi)存泄漏

其中 hprof-conv 命令 這樣使用

hprof-conv 源文件 輸出文件

比如 hprof-conv E:/aaa.hprof E:/output.hprof

就是 把a(bǔ)aa.hprof 轉(zhuǎn)換為output.hprof輸出 output.hprof就是我們轉(zhuǎn)換之后的文件,圖中 mat2.hprof就是我們轉(zhuǎn)換完的文件。

接下來 我們用MAT工具打開轉(zhuǎn)換之后的mat2.hprof文件即可 ,打開后不報(bào)錯(cuò) 如下圖所示:

Android,Studio,MAT,內(nèi)存泄漏

之后我們就可以查看當(dāng)前內(nèi)存中存在的對(duì)象了,由于我們內(nèi)存泄漏一般發(fā)生在Activity中,因此只需要查找Activity即可。

點(diǎn)擊下圖中標(biāo)記的QQL圖標(biāo) 輸入 select * from instanceof android.app.Activity

類似于 SQL語句 查找 Activity相關(guān)的信息 點(diǎn)擊 紅色嘆號(hào)執(zhí)行后 如下圖所示:

Android,Studio,MAT,內(nèi)存泄漏

接下來 我們就可以看到下面過濾到的Activity信息了

如上圖所示, 其中內(nèi)存中還存在 6個(gè)SecondActivity實(shí)例,但是我們是想要全部退出的,這表明出現(xiàn)了內(nèi)存泄漏

其中 有 Shallow size 和 Retained Size兩個(gè)屬性

Shallow Size對(duì)象自身占用的內(nèi)存大小,不包括它引用的對(duì)象。針對(duì)非數(shù)組類型的對(duì)象,它的大小就是對(duì)象與它所有的成員變量大小的總和。當(dāng)然這里面還會(huì)包括一些java語言特性的數(shù)據(jù)存儲(chǔ)單元。針對(duì)數(shù)組類型的對(duì)象,它的大小是數(shù)組元素對(duì)象的大小總和。Retained SizeRetained Size=當(dāng)前對(duì)象大小+當(dāng)前對(duì)象可直接或間接引用到的對(duì)象的大小總和。(間接引用的含義:A->B->C, C就是間接引用)不過,釋放的時(shí)候還要排除被GC Roots直接或間接引用的對(duì)象。他們暫時(shí)不會(huì)被被當(dāng)做Garbage。

接下來 右擊一個(gè)SecondActivity

Android,Studio,MAT,內(nèi)存泄漏

選擇 with all references

打開如下圖所示的頁面

Android,Studio,MAT,內(nèi)存泄漏

查看下圖的頁面

看到 this0引用了這個(gè)Activitythis0是表示 內(nèi)部類的意思,也就是一個(gè)內(nèi)部類引用了Activity 而 this$0又被 target引用 target是一個(gè)線程,原因找到了,內(nèi)存泄漏的原因 就是 Activity被 內(nèi)部類引用 而內(nèi)部類又被線程使用 因此無法釋放,我們轉(zhuǎn)到這個(gè)類的代碼處查看

public class SecondActivity extends AppCompatActivity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_second);    Runnable runnable = new Runnable() {      @Override      public void run() {        try {          Thread.sleep(8000000L);        } catch (InterruptedException e) {          e.printStackTrace();        }      }    };    new Thread(runnable).start();  }}確實(shí) 在

確實(shí) 在 SecondActivity中 存在Runnable 內(nèi)部類對(duì)象,然后又被線程 使用,而線程要執(zhí)行8000秒 因此 SecondActivity對(duì)象被引用 無法釋放,導(dǎo)致了內(nèi)存溢出。

要解決這種的內(nèi)存溢出,要及時(shí)在Activity退出時(shí)結(jié)束線程(不過不大好結(jié)束。。),或者良好的控制線程執(zhí)行的時(shí)間即可。

這樣我們就找出了這個(gè)程序中的內(nèi)存溢出。

2.直接利用Android Studio的 Monitor Memory 查找內(nèi)存溢出

還是利用上面那個(gè)程序,我就簡(jiǎn)單點(diǎn)說了。

首先 在手機(jī)上運(yùn)行程序,打開AS的 Minotor 界面 查看Memory 圖像

Android,Studio,MAT,內(nèi)存泄漏

點(diǎn)擊 小卡車圖標(biāo)(圖中1位置圖標(biāo)) 可以觸發(fā)一次 GC

Android,Studio,MAT,內(nèi)存泄漏

點(diǎn)擊 圖中2位置圖標(biāo)可以查看hprof文件

Android,Studio,MAT,內(nèi)存泄漏

左邊是 內(nèi)存中的對(duì)象,在里面找 Activity 看存不存在我們希望已經(jīng)回收的Activity 如果 出現(xiàn)我們期望已經(jīng)回收的Activity,單擊 就會(huì)在右邊顯示它的總的個(gè)數(shù),點(diǎn)擊右邊的某個(gè),可以顯示 它的GC Roots的樹關(guān)系圖 ,查看關(guān)系圖就可以找出發(fā)生內(nèi)存泄漏的位置(類似于第一種方式)

這樣就完成了內(nèi)存泄漏的查找。

其中內(nèi)存泄漏產(chǎn)生的原因在Android中大致分為以下幾種:

1.static變量引起的內(nèi)存泄漏

因?yàn)閟tatic變量的生命周期是在類加載時(shí)開始 類卸載時(shí)結(jié)束,也就是說static變量是在程序進(jìn)程死亡時(shí)才釋放,如果在static變量中 引用了Activity 那么 這個(gè)Activity由于被引用,便會(huì)隨static變量的生命周期一樣,一直無法被釋放,造成內(nèi)存泄漏。

解決辦法:

在Activity被靜態(tài)變量引用時(shí),使用 getApplicationContext 因?yàn)锳pplication生命周期從程序開始到結(jié)束,和static變量的一樣。

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

類似于上述例子中的情況,線程執(zhí)行時(shí)間很長(zhǎng),及時(shí)Activity跳出還會(huì)執(zhí)行,因?yàn)榫€程或者Runnable是Acticvity內(nèi)部類,因此握有Activity的實(shí)例(因?yàn)閯?chuàng)建內(nèi)部類必須依靠外部類),因此造成Activity無法釋放。

AsyncTask 有線程池,問題更嚴(yán)重

解決辦法:

1.合理安排線程執(zhí)行的時(shí)間,控制線程在Activity結(jié)束前結(jié)束。

2.將內(nèi)部類改為靜態(tài)內(nèi)部類,并使用弱引用WeakReference來保存Activity實(shí)例 因?yàn)槿跻?只要GC發(fā)現(xiàn)了 就會(huì)回收它 ,因此可盡快回收

3.BitMap占用過多內(nèi)存

bitmap的解析需要占用內(nèi)存,但是內(nèi)存只提供8M的空間給BitMap,如果圖片過多,并且沒有及時(shí) recycle bitmap 那么就會(huì)造成內(nèi)存溢出。

解決辦法:

及時(shí)recycle 壓縮圖片之后加載圖片

4.資源未被及時(shí)關(guān)閉造成的內(nèi)存泄漏

比如一些Cursor 沒有及時(shí)close 會(huì)保存有Activity的引用,導(dǎo)致內(nèi)存泄漏

解決辦法:

在onDestory方法中及時(shí) close即可

5.Handler的使用造成的內(nèi)存泄漏

由于在Handler的使用中,handler會(huì)發(fā)送message對(duì)象到 MessageQueue中 然后 Looper會(huì)輪詢MessageQueue 然后取出Message執(zhí)行,但是如果一個(gè)Message長(zhǎng)時(shí)間沒被取出執(zhí)行,那么由于 Message中有 Handler的引用,而 Handler 一般來說也是內(nèi)部類對(duì)象,Message引用 Handler ,Handler引用 Activity 這樣 使得 Activity無法回收。

解決辦法:

依舊使用 靜態(tài)內(nèi)部類+弱引用的方式 可解決

其中還有一些關(guān)于 集合對(duì)象沒移除,注冊(cè)的對(duì)象沒反注冊(cè),代碼壓力的問題也可能產(chǎn)生內(nèi)存泄漏,但是使用上述的幾種解決辦法一般都是可以解決的。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到Android開發(fā)頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 富顺县| 双辽市| 申扎县| 加查县| 泰州市| 兴仁县| 毕节市| 元江| 石家庄市| 漯河市| 承德县| 当阳市| 禄劝| 兰溪市| 波密县| 邵阳县| 岳西县| 恭城| 武乡县| 从化市| 剑川县| 玉屏| 辽宁省| 成都市| 贵溪市| 辽阳市| 海淀区| 望都县| 和静县| 湖南省| 肇东市| 五家渠市| 盐池县| 辽宁省| 长岛县| 清远市| 赤水市| 云龙县| 驻马店市| 石泉县| 贵港市|