Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android性能優化之MAT內存檢測

Android性能優化之MAT內存檢測

編輯:關於Android編程

前言

1.什麼是內存洩漏?

這個問題說來話太長了,似乎盤古開天劈地以來就有的話題一樣,這個問題一直在困擾著程序員,
內存洩露 memory leak,是指程序在申請內存後,無法釋放已申請的內存空間,一次內存洩露危害可以忽略,但內存洩露堆積後果很嚴重,無論多少內存,遲早會被占光。

memory leak會最終會導致out of memory!

內存溢出就是你要求分配的內存超出了系統能給你的,系統不能滿足需求,於是產生溢出。

內存洩漏可以分為4類:

常發性內存洩漏。發生內存洩漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存洩漏。 偶發性內存洩漏。發生內存洩漏的代碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測內存洩漏至關重要。 一次性內存洩漏。發生內存洩漏的代碼只會被執行一次,或者由於算法上的缺陷,導致總會有一塊僅且一塊內存發生洩漏。比如,在類的構造函數中分配內存,在析構函數中卻沒有釋放該內存,所以內存洩漏只會發生一次。 隱式內存洩漏。程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這裡並沒有發生內存洩漏,因為最終程序釋放了所有申請的內存。但是對於一個服務器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存洩漏為隱式內存洩漏。

從用戶使用程序的角度來看,內存洩漏本身不會產生什麼危害,作為一般的用戶,根本感覺不到內存洩漏的存在。真正有危害的是內存洩漏的堆積,這會最終消耗盡系統所有的內存。從這個角度來說,一次性內存洩漏並沒有什麼危害,因為它不會堆積,而隱式內存洩漏危害性則非常大,因為較之於常發性和偶發性內存洩漏它更難被檢測到 .

2.為什麼會發生內存洩漏?

眾所周知java不同於c語言的一個顯著的區別就是java擁有垃圾回收器機制,程序開發的時候程序員一般不需要考慮對象的釋放管理,也就是不會涉及到人工干預回收對象的使用內存,既然如此?那為什麼會發生內存洩漏呢?要回答這個問題又要引出另外一個話題,既什麼樣的對象GC才會回收?當然是GC發現通過任何reference chain(引用鏈)無法訪問某個對象的時候,該對象即被回收。在解釋這個問題之前我們需要了解一些GC回收機制中的一些概念,以便更好的對此問題刨析。

(1)GC Root

JAVA虛擬機通過可達性(Reachability)來判斷對象是否存活,基本思想:以”GC Roots”的對象作為起始點向下搜索,搜索形成的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連(即不可達的),則該對象被判定為可以被回收的對象,反之不能被回收。

GC Roots可以是以下任意對象

一個在current thread(當前線程)的call stack(調用棧)上的對象(例如方法參數和局部變量)
線程自身或者system class loader(系統類加載器)加載的類
native code(本地代碼)保留的活動對象

從最強到最弱,不同的引用(可到達性)級別反映了對象的生命周期。 強(strong)、軟(soft)、弱(weak)、虛(phantom)引用。

Strong references

普通的java引用,我們通常new的對象就是:
Object obj = new Object();
如果一個對象通過一串強引用鏈可達,那麼它就不會被垃圾回收。你肯定不希望自己正在使用的引用被垃圾回收器回收吧。但對於集合中的對象,應在不使用的時候移除掉,否則會占用更多的內存,導致內存洩漏。

Soft reference

當對象是Soft reference可達時,gc會向系統申請更多內存,而不是直接回收它,當內存不足的時候才回收它。因此Soft reference適合用於構建一些緩存系統,比如圖片緩存。

WeakReference

WeakReference不會強制對象保存在內存中。它擁有比較短暫的生命周期,允許你使用垃圾回收器的能力去權衡一個對象的可達性。在垃圾回收器掃描它所管轄的內存區域過程中,一旦gc發現對象是weakReference可達,就會把它放到ReferenceQueue中,等下次gc時回收它。
WeakReference weakWidget = new WeakReference(widget);
系統為我們提供了WeakHashMap,和HashMap類似,只是其key使用了weak reference。如果WeakHashMap的某個key被垃圾回收器回收,那麼entity也會自動被remove。

由於WeakReference被GC回收的可能性較大,因此,在使用它之前,你需要通過weakObj.get()去判斷目的對象引用是否已經被回收.

Reference queque

一旦WeakReference.get()返回null,它指向的對象就會被垃圾回收,那麼WeakReference對象就沒有用了,意味著你應該進行一些清理。比如在WeakHashMap中要把回收過的key從Map中刪除掉,避免無用的的weakReference不斷增長。
ReferenceQueue可以讓你很容易地跟蹤dead references。WeakReference類的構造函數有一個ReferenceQueue參數,當指向的對象被垃圾回收時,會把WeakReference對象放到ReferenceQueue中。這樣,遍歷ReferenceQueue可以得到所有回收過的WeakReference。

Phantom reference

和soft,weak Reference區別較大,它的get()方法總是返回null。這意味著你只能用PhantomReference本身,而得不到它指向的對象。當WeakReference指向的對象變得弱可達(weakly reachable)時會立即被放到ReferenceQueue中,這在finalization、garbage collection之前發生。理論上,你可以在finalize()方法中使對象“復活”(使一個強引用指向它就行了,gc不會回收它)。但沒法復活PhantomReference指向的對象。而PhantomReference是在garbage collection之後被放到ReferenceQueue中的,沒法復活。

對象無用了,但仍然可達(未釋放),垃圾回收器無法回收。這就是內存洩漏發生的原因。

3.MAT視圖分析

tips:MAT需要的是HPROF格式的heap dump二進制文件

這裡寫圖片描述

Shallow Heap

Shallow size就是對象本身占用內存的大小,不包含對其他對象的引用,也就是對象頭加成員變量(不是成員變量的值)的總和。在32位系統上,對象頭占用8字節,int占用4字節,不管成員變量(對象或數組)是否引用了其他對象(實例)或者賦值為null它始終占用4字節。故此,對於String對象實例來說,它有三個int成員(3*4=12字節)、一個char[]成員(1*4=4字節)以及一個對象頭(8字節),總共3*4 +1*4+8=24字節。

class String{
    public final class String {8 Bytes header
    private char value[]; 4 Bytes
    private int offset; 4 Bytes
    private int count; 4 Bytes
    private int hash = 0; 4 Bytes
…}
"Shallow size“ of a String ==24 Bytes

對象頭:JVM虛擬機的對象頭包括兩部分信息:Mark Word(標記字段)和 Klass Pointer(類型指針)
Mark Word 用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標志、線程持有的鎖、偏向線程 ID、偏向時間戳等等。JVM 對象頭一般占用兩個機器碼,在 32-bit JVM 上占用 64bit, 在 64-bit JVM 上占用 128bit 即 16 bytes(暫不考慮開啟壓縮指針的場景)。另外,如果對象是一個 Java 數組,那在對象頭中還必須有一塊用於記錄數組長度的數據,因為虛擬機可以通過普通 Java 對象的元數據信息確定 Java 對象的大小,但是從數組的元數據中無法確定數組的大小。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved