GC那些事兒--Android內存優化第一彈

shengyu06 8年前發布 | 9K 次閱讀 JVM 安卓開發 Android開發 移動開發

引言

接 App優化之內存優化(序) , 作為App優化系列中內存優化的一個小部分.

由于內存相關知識比較生澀, 內存優化中使用到的相關工具, 也有很多專有名詞. 對Java內存管理, GC, Android內存管理, Dalvik/ART等知識有一個理論的認識, 可以讓我們更好的使用這些工具, 分析內存問題.

據此, 我們就先從理論入手, 聊聊GC那些事兒.

1, 何為GC

GC 是 garbage collection 的縮寫, 垃圾回收的意思. 也可以是 Garbage Collector, 也就是垃圾回收器.

1.1 垃圾回收器

我們先來解釋下Garbage Collector(垃圾回收器).

內存管理, 一直是編程中的一個大的問題. 在較老的語言中, 例如C++語言中, 內存管理是顯式的, 也就是說使用者自己申請內存使用, 自己釋放內存. 這就是為什么C++語言中除了構造函數, 還有析構函數. 我們在創建對象的時候調用構造函數創建, 系統會在對象結束其作用域的時候調用析構函數, 我們需要做的就是在析構函數中釋放掉我們申請的相關資源, 以便釋放內存地址.

顯然, 這種顯式的由編程人員自己控制釋放內存的方式很容易出問題, 忘了, 漏了, 都可能導致內存問題. 也不符合程序員要懶的特征.

故而, Java語言中引入了自動內存管理的機制, 也就是垃圾回收器. 大部分的現代面向對象語言, 也都是采用自動內存管理機制.

內存自動管理回收機制可以解決大部分, 但不是所有的內存問題, 這也是為什么我們要討論內存泄露.

垃圾回收器的職責

垃圾回收器有三大職責:

  1. 分配內存;

  2. 確保任何被引用的對象保留在內存中;

  3. 回收 不能通過引用關系找到 的對象的內存.

垃圾回收的一般流程

gc process

1.2 相關概念

垃圾回收(GC)

垃圾回收器中有一個進程來做上面的這些事情, 這個進程查找我們的對象引用的關系并釋放其內存, 這個進程就是garbage collection(垃圾回收), 也就是我們常說的GC.

Heap和Stack

簡單說下:

  • Heap內存是指java運行環境用來分配給對象和JRE類的內存. 是應用的內存空間.
  • Stack內存是相對于線程Thread而言的, 它保存線程中方法中短期存在的變量值和對Heap中對象的引用等.
  • Stack內存, 顧名思義, 是類Stack方式, 總是后進先出(LIFO)的.
  • 我們通常說的GC的針對Heap內存的. 因為Stack內存相當于是隨用隨銷的.

heap&stack

GC Root

直譯GC根, 我們姑且不譯了吧.

所謂GC Root我們可以理解為是一個Heap內存之外的對象, 通常包括但不僅限于如下幾種:

  • System Class 系統 Class Loader加載的類. 例如java運行環境中rt.jar中類, 比如java.util.* package中的類.

  • Thread 運行中的線程

  • JNI 中的本地/全局變量, 用戶自定義的JNI代碼或是JVM內部的.

  • Busy Monitor 任何調用了wait()或notify()方法, 或是同步化的(synchronized)的東西. 可以理解為同步監控器.

  • Java本地實例, 還在運行的Thread的stack中的方法創建的對象.

活對象/垃圾

如果這個對象是 引用可達 的, 則稱之為 活的(live) , 反之, 如果這個對象 引用不可達 , 則稱之為 死的(dead) , 也可以稱之為 垃圾(garbage) .

這個 引用可達與不可達 就是相對于GC Root來說的:

gc-roots

2, Java的內存管理機制

2.1 關于JVM

我們平常在查看我們的java版本時, 你會發現:

$ java -version
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

其中有個HotSpot VM的東西, 那么這個是什么呢? 和JVM有什么關系呢?

在此簡單說下, 以便行文:

  • JVM, Java虛擬機, 可以簡單理解為一種技術思想, 虛擬技術理念.
  • HotSpot VM 是JVM的一種實現, 包含了服務器版和桌面應用程序版, 現時由Oracle維護并發布.

我們當前使用的sun(oracle)的java版本(應該是1.3以上)都是內置的HotSpot VM實現. 所以接下來的分析也都是基于HotSpot VM的, 但是還是簡稱JVM.

2.2 JVM內存區域

JVM使用分代式的內存管理方式, 將Heap分成三代 --- 新生代, 老一代, 持久代.

Hotspot heap structure

  • Young Generation

    • 新生代.

    • 所有new的對象.

    • 該區域的內存管理使用minor garbage collection(小GC).

    • 更進一步分成Eden space, Survivor 0 和 Survivor 1 三個部分.

  • Old Generation

    • 老年區.

    • 新生代中執行小粒度的GC幸存下來的"老"對象.

    • 該區域的內存管理使用major garbage collection(大GC).

  • Permanent Generation

    • 持久代.

    • 包含應用的類/方法信息, 以及JRE庫的類和方法信息.

小GC執行非常頻繁, 而且速度特別快.

大GC一般會比小GC慢十倍以上.

大小GC都會發出"Stop the World"事件, 也就是說中斷程序運行, 直至GC完成. 這也是我們在App優化之消除卡頓中為什么說頻繁GC會造成用戶感知卡頓.

3, GC的流程

了解了內存Heap的幾個區域, 我們再來看下垃圾收集器是怎么利用這幾個區域來管理內存和回收垃圾的.

1. 創建新的對象

每當我們使用new創建一個對象時, 這個對象會被分配到 新生代Eden 區域:

object allocation

2. 當Eden區域滿時

當Eden區域內存被分配完時, 小GC 程序被觸發:

Eden filling

引用可達的對象會移到Survivor(幸存者)區域-- S0 , 然后清空Eden區域, 此時 引用不可達的對象 會直接刪除, 內存回收, 如下:

aged

3. Eden再次滿時

當Eden區域再次分配完后, 小GC 執行, 引用可達的對象 會移到Survivor(幸存者)區域, 而 引用不可達的對象 會跟隨Eden的清空而刪除回收.

需要注意的是, 這次 引用可達的對象 移動到的是 S1 的幸存者區.

而且, S0區域也會執行 小GC , 將其中還 引用可達的對象 移動到S1區, 且年齡+1. 然后清空S0, 回收其中 引用不可達的對象 .

此時, 所有 引用可達的對象 都在S1區, 且S1區的對象存在不同的年齡. 如下:

next filling

當Eden第三次滿時, S0和S1的角色互換了:

s0s1

依此循環.

4. 當Survivor區的對象年齡達到"老年線"時

上面1~3循環, Survivor區的對象年齡也會持續增長, 當其中某些對象年齡達到"老年線", 例如8歲時, 它們會"晉升"到 老年區 .

old aged

如此1~4步重復, 大體流程是這樣的

gc flow

參考

 

 

來自:http://www.jianshu.com/p/5db05db4f5ab

 

 本文由用戶 shengyu06 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!