Android 優化二 Java內存分配機制及內存泄漏

Java內存分配機制及內存泄漏 目錄介紹php

  • 1.JVM內存管理
  • 1.1 JVM內存管理圖
  • 1.2 Java採用GC進行內存管理。
  • 2.JVM內存分配的幾種策略
  • 2.1 靜態的
  • 2.2 棧式的
  • 2.3 堆式的
  • 2.4 堆和棧的區別
  • 2.5 得出結論
  • 2.6 舉個例子
  • 2.7 調用 System.gc();進行內存回收
  • 3.GC簡單介紹
  • 3.1 內存垃圾回收機制
  • 3.2 關於GC介紹
  • 3.3 如何監聽GC過程
  • 3.4 GC過程與對象的引用類型關係
  • 4.內存泄漏簡單介紹
  • 4.1 內存泄漏的定義
  • 4.2 內存泄漏與內存溢出的區別
  • 4.3 內存泄漏帶來的影響
  • 4.4 典型內存泄漏案例

關於內存泄漏筆記

0.本人寫的綜合案例

  • 案例

  • 說明及截圖

  • 模塊:新聞,音樂,視頻,圖片,唐詩宋詞,快遞,天氣,記事本,閱讀器等等
  • 接口:七牛,阿里雲,天行,乾貨集中營,極速數據,追書神器等等

思惟導圖

Java內存分配機制.png

1.JVM內存管理

1.1 JVM內存管理 Image.pngjava

1.2 Java採用GC進行內存管理。git

  • Android虛擬機的垃圾回收採用的是根搜索算法。GC會從根節點(GC Roots)開始對heap進行遍歷。到最後,部分沒有直接或者間接引用到GC Roots的就是須要回收的垃圾,會被GC回收掉。而內存泄漏出現的緣由就是存在了無效的引用,致使原本須要被GC的對象沒有被回收掉。 深刻的JVM內存管理知識,推薦《深刻理解Java虛擬機》。

2.JVM內存分配的幾種策略。

2.1 靜態的github

  • 靜態的存儲區,內存在程序編譯的時候就已經分配好了,這塊內存在程序整個運行期間都一直存在 它主要存放靜態數據、全局的static數據和一些常量。

2.2 棧式的算法

  • 在執行方法時,方法一些內部變量的存儲均可以放在棧上面建立,方法執行結束的時候這些存儲單元就會自動被註釋掉。棧 內存包括分配的運算速度很快,由於內在在處理器裏面。固然容量有限,而且棧式一塊連續的內存區域,大小是由操做系統決定的,他先進後出,進出完成不會產生碎片,運行效率高且穩定

2.3 堆式的數組

  • 也叫動態內存 。咱們一般使用new 來申請分配一個內存。這裏也是咱們討論內存泄漏優化的關鍵存儲區。GC會根據內存的使用狀況,對堆內存裏的垃圾內存進行回收。堆內存是一塊不連續的內存區域,若是頻繁地new/remove會形成大量的內存碎片,GC頻繁的回收,致使內存抖動,這也會消耗咱們應用的性能

2.4 堆和棧的區別併發

  • 在函數中(說明是局部變量)定義的一些基本類型的變量和對象的引用變量都是在函數的棧內存中分配。
  • 當在一段代碼塊中定義一個變量時,java就在棧中爲這個變量分配內存空間,當超過變量的做用域後,java會自動釋放掉爲該變量分配的內存空間,該內存空間能夠馬上被另做他用。
  • 堆內存用於存放全部由new建立的對象(內容包括該對象其中的全部成員變量)和數組。在堆中分配的內存,由java虛擬機自動垃圾回收器來管理。
  • 在堆中產生了一個數組或者對象後,還能夠在棧中定義一個特殊的變量,這個變量的取值等於數組或者對象在堆內存中的首地址,在棧中的這個特殊的變量就變成了數組或者對象的引用變量,之後就能夠在程序中使用棧內存中的引用變量來訪問堆中的數組或者對象,引用變量至關於爲數組或者對象起的一個別名,或者代號。

2.5 得出結論函數

  • 1.局部變量的基本數據類型和引用,存儲於棧中,引用的對象實體存儲於堆中。由於它們屬於方法中的變量,生命週期隨方法而結束。 2.成員變量所有存儲與堆中(包括基本數據類型,引用和引用的對象實體),由於它們屬於類,類對象終究是要被new出來使用的。 3.咱們所說的內存泄露,只針對堆內存,他們存放的就是引用指向的對象實體。

2.6 舉個例子工具

public class Sample() {
    int s1 = 0;
    Sample mSample1 = new Sample();
    public void method() {
        int s2 = 1;
        Sample mSample2 = new Sample();
    }
}
Sample mSample3 = new Sample();
Sample 類的局部變量 s2 和引用變量 mSample2 都是存在於棧中,但 mSample2 指向的對象是存在於堆上的。
mSample3 指向的對象實體存放在堆上,包括這個對象的全部成員變量 s1 和 mSample1,而它本身存在於棧中。

2.7 調用 System.gc();進行內存回收post

  • 咱們知道能夠調用 System.gc();進行內存回收,可是GC不必定會執行。面對GC的機制,咱們是否無能爲力?其實咱們能夠經過聲明一些引用標記來讓GC更好對內存進行回收。

Image.png

  • 小技巧 成員變量所有存儲在堆中(包括基本數據類型,引用及引用的對象實體),由於他們屬於類,類對象最終仍是要被new出來的 局部變量的基本數據類型和引用存在棧中,應用的對象實體存儲在堆中。由於它們屬於方法當中的變量,生命週期會隨着方法一塊兒結束

3.GC工做原理

3.1 內存垃圾回收機制

  • 是從程序的主要運行對象(如靜態對象/寄存器/棧上指向的堆內存對象等)開始檢查引用鏈,當遍歷一遍後獲得上述這些沒法回收的對象和他們所引用的對象鏈,組成沒法回收的對象集合,而其餘孤立對象(集)就做爲垃圾回收
  • GC爲了可以正確釋放對象,必須監控每個對象的運行狀態,包括對象的申請、引用、被引用、賦值等,GC都須要進行監控。監視對象狀態是爲了更加準確地、及時地釋放對象,而釋放對象的根本原則就是該對象再也不被引用。

3.2 關於GC介紹

  • 有幾個函數能夠訪問GC,例如運行GC的函數System.gc(),可是根據Java語言規範定義,該函數不保證JVM的垃圾收集器必定會執行。由於不一樣的JVM實現者可能使用不一樣的算法管理GC
  • 一般GC的線程的優先級別較低。JVM調用GC的策略也有不少種,有的是內存使用到達必定程度時,GC纔開始工做,也有定時執行的,有的是平緩執行GC,有的是中斷式執行GC。但一般來講,咱們不須要關心這些。
  • 經過關鍵字 new 爲每一個對象申請內存空間 (基本類型除外),全部的對象都在堆 (Heap)中分配空間

3.3 如何監聽GC過程

  • 系統每進行一次GC操做時,都會在LogCat中打印一條日誌,咱們只要去分析這條日誌就能夠了,日誌的基本格式以下 D/dalvikvm: <GC_Reason> <Amount_freed>, <Heap_stats>, <Pause_time> 第一部分GC_Reason,這個是觸發此次GC操做的
緣由,通常狀況下一共有如下幾種觸發GC操做的緣由:
- GC_CONCURRENT: 當咱們應用程序的堆內存快要滿的時候,系統會自動觸發GC操做來釋放內存。
- GC_FOR_MALLOC: 當咱們的應用程序須要分配更多內存,但是現有內存已經不足的時候,系統會進行GC操做來釋放內存。
- GC_HPROF_DUMP_HEAP: 當生成HPROF文件的時候,系統會進行GC操做,關於HPROF文件咱們下面會講到。
- GC_EXPLICIT: 這種狀況就是咱們剛纔提到過的,主動通知系統去進行GC操做,好比調用System.gc()方法來通知系統。或者在DDMS中,經過工具按鈕也是能夠顯式地告訴系統進行GC操做的。

第二部分Amount_freed,表示系統經過此次GC操做釋放了多少內存 第三部分Heap_stats中會顯示當前內存的空閒比例以及使用狀況(活動對象所佔內存 / 當前程序總內存) 第四部分Pause_time表示此次GC操做致使應用程序暫停的時間。關於這個暫停的時間,Android在2.3的版本當中進行過一次優化,在2.3以前GC操做是不能併發進行的,也就是系統正在進行GC,那麼應用程序就只能阻塞住等待GC結束。雖然說這個阻塞的過程並不會很長,也就是幾百毫秒,可是用戶在使用咱們的程序時仍是有可能會感受到略微的卡頓。而自2.3以後,GC操做改爲了併發的方式進行,就是說GC的過程當中不會影響到應用程序的正常運行,可是在GC操做的開始和結束的時候會短暫阻塞一段時間,不過優化到這種程度,用戶已是徹底沒法察覺到了

3.4 GC過程與對象的引用類型關係

  • Java對引用的分類Strong reference, SoftReference, WeakReference, PhatomReference

Image.png

  • 軟引用和弱引用 在Android應用的開發中,爲了防止內存溢出,在處理一些佔用內存大並且聲明週期較長的對象時候,能夠儘可能應用軟引用和弱引用技術 軟/弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。 利用這個隊列能夠得知被回收的軟/弱引用的對象列表,從而爲緩衝器清除已失效的軟/弱引用。

  • 內存泄漏的緣由: 堆內存中的長生命週期的對象持有短生命週期對象的強/軟引用,儘管短生命週期對象已經再也不須要,可是由於長生命週期對象持有它的引用而致使不能被回收,這就是Java中內存泄露的根本緣由

4.內存泄漏簡單介紹

4.1 內存泄漏的定義

  • 當一個對象已經不須要使用了,本該被回收時,而有另一個正在使用的對象持有它的引用,從而致使了對象不能被GC回收。這種致使了本該被回收的對象不能被回收而停留在堆內存中,就產生了內存泄漏

4.2 內存泄漏與內存溢出的區別

  • 內存泄漏(Memory Leak) 進程中某些對象已經沒有使用的價值了,可是他們卻還能夠直接或間接地被引用到GC Root致使沒法回收。當內存泄漏過多的時候,再加上應用自己佔用的內存,日積月累最終就會致使內存溢出OOM

  • 內存溢出(OOM) 當應用的heap資源超過了Dalvik虛擬機分配的內存就會內存溢出

4.3 內存泄漏帶來的影響

  • 應用卡頓 泄漏的內存影響了GC的內存分配,過多的內存泄漏會影響應用的執行效率

  • 應用異常(OOM) 過多的內存泄漏,最終會致使 Dalvik分配的內存,出現OOM

4.4 典型內存泄漏案例

  • 案例代碼
Vector v = new Vector(10);
for (int i = 1; i < 100; i++) {
    Object o = new Object();
    v.add(o);
    o = null; 
}
  • 分析 在這個例子中,咱們循環申請Object對象,並將所申請的對象放入一個 Vector 中,若是咱們僅僅釋放引用自己,那麼 Vector 仍然引用該對象,因此這個對象對 GC 來講是不可回收的。所以,若是對象加入到Vector 後,還必須從 Vector 中刪除,最簡單的方法就是將 Vector 對象設置爲 null。

其餘說明

相關文章
相關標籤/搜索