內存泄露(Memory leak),是指程序在向系統申請分配內存空間後(new),在使用完畢後未釋放。結果致使一直佔據該內存單元,咱們和程序都沒法再使用該內存單元,直到程序結束,這是內存泄露。java
JVM(Java虛擬機)
是一個虛構出來的運行Java程序的運行時環境,是經過在實際的計算機上仿真模擬各類計算機功能的實現。它具備完善的硬件架構(如處理器、堆棧、寄存器等),還具備相應的指令系統,使用JVM就是使Java程序支持與操做系統無關。
理論上在任何操做系統中,只要有對應的JVM,便可運行Java程序。android
ART(android虛擬機)
是在Android系統上運行Android程序的虛擬機,其指令集是基於寄存器架構的,執行特有的文件格式-dex字節碼來完成對象生命週期管理、堆棧管理、線程管理、安全異常管理、垃圾回收等重要功能。
其實ART就是在JVM基礎上專門爲android移動設備定製的一套虛擬機方案。數組
JAVA是在JVM所虛擬出的內存環境中運行的,JVM的內存可分爲三個區:
堆(heap)、棧(stack)和方法區(method)。android-studio
棧(stack)
是簡單的數據結構,但在計算機中使用普遍。棧最顯著的特徵是:LIFO(Last In, First Out, 後進先出),棧中只存放基本類型和對象的引用(不是對象)安全
堆(heap)
堆內存用於存放由new建立的對象和數組。在堆中分配的內存,由java虛擬機自動垃圾回收器來管理。JVM只有一個堆區(heap)被全部線程共享,堆中不存放基本類型和對象引用,只存放對象自己。數據結構
方法區(method)
又叫靜態區,跟堆同樣,被全部的線程共享。方法區包含全部的class和static變量架構
那麼問題來了?究竟哪部分的內存會致使內存泄漏呢?
在JAVA中JVM的棧記錄了方法的調用,每一個線程擁有一個棧。app
在線程的運行過程中,執行到一個新的方法調用,就在棧中增長一個內存單元,即幀(frame)。在frame中,保存有該方法調用的參數、局部變量和返回地址。jvm
然而JAVA中的局部變量只能是基本類型變量(int),或者對象的引用。因此在棧中只存放基本類型變量和對象的引用。引用的對象保存在堆中。工具
當某方法運行結束時,該方法對應的frame將會從棧中刪除,frame中全部局部變量和參數所佔有的空間也隨之釋放。 線程回到原方法繼續執行,當全部的棧都清空的時候,程序也就隨之運行結束。
而對於堆內存,堆存放着普通變量。在JAVA中堆內存不會隨着方法的結束而清空,因此在方法中定義了局部變量,在方法結束後變量依然存活在堆中。
綜上所述,棧(stack)能夠自行清除不用的內存空間。可是若是咱們不停的建立新對象,堆(heap)的內存空間就會被消耗盡。因此內存泄漏會發生在堆區。
JAVA引入了垃圾回收(garbage collection,簡稱GC)去處理堆內存的回收。
垃圾回收(garbage collection,簡稱GC)能夠自動清空堆中再也不使用的對象。
在JAVA中對象是經過引用使用的。若是再沒有引用指向該對象,那麼該對象就無從處理或調用該對象,這樣的對象稱爲不可到達(unreachable)。
垃圾回收用於釋放不可到達的對象所佔據的內存。
根據上圖能夠知道:因爲obj4沒有root指向它,因此GC會釋放它所佔據的內存,obj7因爲還有其餘引用指向它,因此得不到釋放(若是持有對象的引用,垃圾回收器是沒法在內存中回收這個對象)
因此內存泄露的真因是:
持有對象的強引用,且沒有及時釋放,進而形成內存單元一直被佔用,浪費空間,形成內存溢出
內存泄漏對於app沒有直接危害,即便有發現內存泄漏的狀況,也不必定會當即引發app崩潰,可是經過累積效應,應用會爆出各類問題:
一、內存得不到釋放,慢慢的會形成app內存溢出,致使崩潰
二、內存泄漏同時可能會觸發系統頻繁GC,發生內存抖動,會致使系統性能問題(卡頓不流暢)
操做步驟:
DDMS是Android SDK中自帶的調試工具
須要注意的是:新版本的SDK中,DDMS工具已經集成到了Android device mointor中
操做步驟:
LeakCanary是Square公司基於MAT開源的一個工具,用於檢測Android App的內存泄漏,咱們能夠經過集成LeakCanary提供的jar包到本身的項目工程中,一旦檢測到內存泄漏問題,LeakCanary會自動dump內存信息,經過另一個進程分析內存泄漏信息並展現出來,能夠隨時發現和定位內存泄漏問題。
在測試過程當中,咱們能夠結合Monkey健壯性測試工具自動化執行,測試結束後,LeakCanary自動展現內存泄漏問題: