轉載請標明出處:http://www.javashuo.com/article/p-qbvajwlu-da.htmlphp
泄漏,泄漏,漏~html
內存泄漏怎麼破,什麼是內存泄漏?與內存溢出有什麼區別?android
內存泄漏(Memory Leak):是指程序中己動態分配的堆內存因爲某種緣由程序未釋放或沒法釋放,形成系統內存的浪費,致使程序運行速度減慢甚至系統崩潰等嚴重後果。shell
內存溢出(out of memory):是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;數組
內存泄漏不必定會引發奔潰,可是內存溢出必定會app
Java裏頭有GC垃圾回收機制,他是怎麼判斷該不應回收呢?eclipse
Q1:某對象沒有任何引用的時候才進行回收?ide
A:不。沒法往上追溯到GCroot引用點的纔回收。測試
Q2:某對象被別的對象引用就不能進行回收?this
A:不。軟引用,弱引用,虛引用均可以
哪些能夠做爲GCroot引用點:
adb命令驗證是否存在內存泄漏:
一、打開要測試的apk,而後返回退出到主界面
二、AS的Terminal中輸入命令:
adb shell dumpsys meminfo com.status.mattest -d
com.status.mattest爲包名
而後即可以看到內存的一些狀況
拉到下面能夠看到:
咱們退出APK以後,對象本應該都被回收,然而這裏能夠看到,還有view以及Activity佔用着內存,由此能夠知道內存泄漏了。
咱們點擊AS上的按鈕,進行分析。
運行apk後會出現該界面:
咱們點擊MEMORY進入內存分析界面:
這時候咱們須要進行剛剛的操做,打開apk,而後返回鍵退出,而後點擊上圖中垃圾桶形狀的圖標進行垃圾回收,多點幾回,直到內存沒有什麼變化了。
而後點擊上圖中相似於下載按鈕的圖標獲取內存快照。
獲取完以後,左邊會出現下面這圖,Head Dump即是獲取內存快照後出現的,咱們能夠點擊紅圈中的圖形進行保存
跟着出現的還有這個窗口。
咱們能夠經過包來分類查看本身的項目.
Shallow Heap(淺堆) 表示該對象自身佔用的堆內存,不包括它引用的對象。
針對非數組類型的對象,它的大小就是對象與它全部的成員變量大小的總和。
Retained Heap(深堆) 表示當前對象大小+當前對象可直接或間接引用到的對象的大小總和。
換句話說,Retained Size就是當前對象被GC後,從Heap上總共能釋放掉的內存。
不過,釋放的時候還要排除被GC Roots直接或間接引用的對象。他們暫時不會被被當作Garbage。
從圖中能夠看出,出現了內存泄漏的是CustomUtils,MainActivity,MainActivity$1表明MaiActivity裏面的一個方法。
點擊MainActivity,能夠看到這麼對東西,眼花繚亂,咱們根本不知道是哪一個引發了內存泄漏。那咋辦。鋪墊結束,mat該上場了。
MAT
下載mat https://www.eclipse.org/mat/downloads.php
安裝步驟很簡單。
打開MAT後,點擊File -> Open Heap Dump 打開剛剛保存的內存快照,會發現打不開。
由於咱們保存的內存快照是不能直接在MAT上打開的,須要進行轉化。
在AS的Terminal窗口輸入:hprof-conv -z A.hprof B.hprof
A.hprof爲剛剛保存後的快照文件,B.hprof爲轉換後的文件,也就是咱們要在MAT上打開的文件
而後咱們在MAT上將其打開。
點擊Finish
點擊Histogram
能夠經過包名來分類查看
從下圖中能夠看到引發內存泄漏的類,Objects那一列不爲0的就是,與咱們以前看到的同樣。
MainActivity右鍵
選擇圖中的選項,意思是過濾掉虛引用,軟引用,弱引用
以後能夠看到引用的路徑,CustomUtils裏面的instance引用了MainActivity的context,致使了MainActivity內存泄漏。
打開代碼看一下
package com.status.mattest; import android.content.Context; public class CustomUtils { private static CustomUtils instance; private CustomUtils(Context context) { this.mContext = context; } private Context mContext; public static CustomUtils getInstance(Context context) { if (instance == null) { instance = new CustomUtils(context); } return instance; } }
MainActivity中:
package com.status.mattest; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CustomUtils customUtils = CustomUtils.getInstance(this); textView = findViewById(R.id.tv); textView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this, Test1Activity.class)); } }); } }
MainActivity中調用了單例CustomUtils,而且把context傳了進去,而咱們知道instance做爲靜態對象,是GCroot引用點,因此activity關閉了它也無法被回收,那麼最爲被instance引用的Activity天然也沒法被回收,因此致使了內存泄漏。
解決:
把單例模式裏面的context換爲全局application的context,也就是說單例裏面的context的週期應該與進程同樣,而不可以與應用同樣。當咱們關閉應用的時候,進程還在,並無被殺死。