可視化Java垃圾回收

jopen 10年前發布 | 12K 次閱讀 Java

        英文原文: Visualizing Java Garbage Collection

        垃圾回收,就像雙陸棋一樣,只需幾分鐘來學習,但要用一生來精通。

        Ben Evans 是一名資深培訓師兼顧問,他在演講可視化垃圾回收中從基礎談起討論了垃圾回收。

        以下是對其演講的簡短總結。

        基礎

        當談到釋放不再使用的內存,垃圾回收已經在很大程度上取代了早期技術,比如手動內存管理和引用計數。

        這是件好事,因為內存管理令人厭煩,學究式地簿記是計算機擅長的,而不是人擅長的。在這方面,語言的運行時環境比人強。

        現代的垃圾回收非常高效,遠遠超過早期語言中典型的手工分配。通常,具有其它語言背景的人只盯著垃圾回收造成的中斷,卻沒有完全理解自動內存管理發生作用的上下文環境。

        標記&清除是 Java(及其它運行時環境)用于垃圾回收的基本算法。

        在標記&清除算法中,引用會從每個線程棧的楨指向程序的堆。所以,從棧開始,循著指針找到所有可能的引用,然后再循著這些引用遞歸下去。

        當遞歸完成,就找到了所有的活對象,其它的都是垃圾。

        請注意,人們經常漏掉的一點是,運行時環境本身也有一個“分配清單(allocation list)”,上面列出了指向每個對象的指針,該列表由垃圾回收器負責維護,并幫助垃圾回收器進行垃圾清理。因此,運行時環境總是可以找出由它創建但尚未回收的對象。

可視化Java垃圾回收

        圖一

        上面插圖中所示的棧只是一個與單個應用程序線程相關的棧;每個應用程序線程都有一個類似的棧,每個棧本身都有一組指向堆的指針。

        如果垃圾回收器試圖在應用程序運行過程中獲取活對象的快照,那么它就要追蹤運動著的目標,那樣很容易漏掉一些嚴重超時的對象分配,因而無法獲得 一個準確的快照。因此,“Stop the World”是有必要的;也就是,停止應用程序線程足夠長的時間,以便捕獲活對象的快照。

        下面是垃圾回收器必須遵循的兩條黃金法則:

  1. 垃圾回收器必須回收所有的垃圾。
  2. 垃圾回收器必須從不回收任何活對象。

        但這兩條規則并不是對等的;如果違反了第二條規則,結果會使數據遭到破壞。

        另一方面,如果違反了第一條規則,則會是另一種情況,系統并不總是能夠回收所有的垃圾,但最終會回收所有的垃圾,那么這是可以接受的,而實際上,這是垃圾回收器的基本原理。

        HotSpot

        現在,我們來說下 HotSpot,它實際上是一個C、C++以及許多特定于平臺的匯編程序組成的混合體。

        當人們想到解釋器,就會想到一個很大的 while 循環,其中包含一個很長的 switch 語句。但 HotSpot 解釋器比那個要復雜的多(由于性能原因)。在開始閱讀 JDK 源代碼的時候,就會發現 HotSpot 中實在是有許多匯編程序代碼。

        對象創建

        Java 會預先分配大量的連續空間,就是我們所說的“堆”。之后,HotSpot 完全在用戶空間里管理這塊內存。

        如果一個 Java 進程占用了大量的系統(或內核)時間,那么毫無疑問,它不是在進行垃圾回收——因為所有的垃圾回收內存“簿記(bookkeeping)”都是在用戶空間進行的。

        內存池

可視化Java垃圾回收

        圖二

        “永久代(PermGen)”是一個存儲區域,用于保存那些需要在程序生存期內一直存活的東西,如類的元數據。不過,隨著應用程序服務器的出 現,它們有自己的類加載器,并且需要重新加載類的元數據,永久代作為一個優化決策開始顯得糟糕,所幸,它在 Java 8 中消失了。

        Java 8 將會使用一個名為“元空間(Metaspace)”的新概念。元空間與永久代并不完全相同。它在堆的外面,由操作系統管理。這意味著,它不會在 Java 堆中,而是在本地內存里。目前,這還不是一個非常好的消息,因為沒有多少工具能夠讓用戶輕松地查看本地內存。所以,永久代消失是件好事,但工具趕上這個變 化還需要一些時間。

        Java 堆布局

        現在,我們來看下 Java 堆。注意堆空間之間的虛擬空間。它們提供了一點浮動量,以允許對內存池進行一定量的尺寸調整,又不用為任何對象移動付出代價。

可視化Java垃圾回收

        圖三

        “弱代假設(Weak Generational Hypothesis)”

        就現狀而言,究竟為什么要將堆分成所有這些內存池?

可視化Java垃圾回收

        圖四

        有的運行時事實無法通過靜態分析推導出來。上面的插圖說明有兩組對象:一組存活時間短,一組存活時間長——所以,做額外的簿記以便利用這一事實是有意義的。在 Java 平臺中,有許多類似的作為優化寫入平臺的事實。

        演示

        Ben Evans 進行了一系列的動畫演示。第一個演示是個 Flash,說明了對象在 Eden 區和一個新生代 Survivor 空間之間移動,并最終進入老年代的過程。

        圖五是用 JavaFX 再現了同樣的過程。

可視化Java垃圾回收

        圖五

        運行時開關

        ‘強制性’參數

  • -verbose:gc——為用戶輸出一些 GC 信息
  • -Xloggc:<文件路徑>——指定日志輸出路徑,要確保磁盤有空間
  • -XX:+PringGCDetails——為輔助工具提供“最低限度信息(Minimum information)”

            ——用這個參數代替-verbose:gc

  • --XX:PrintTenuringDistribution——“過早提升(Premature promotion)”信息

        基本堆大小參數

  • -Xms —— 設置預留給堆的最小內存值
  • -Xmx —— 設置預留給堆的最大內存值
  • -XX:MaxPermSize=——設置永久代的最大內存值

            ——有利于 Spring 應用程序和應用服務器

        以前,我們被教導要把-Xms 和-Xmx 的值設的一樣大。不過這已經變了。因此,現在可以為-Xms 設置一個合理范圍內較小的值,或者根本就不設置,因為堆的適應能力現在已經非常好了。

        其它參數

  • -XX:NewRatio=N
  • -XX:NewSize=N
  • -XX:MaxNewSize=N
  • -XX:MaxHeapFreeRatio
  • -XX:MinHeapFreeRatio
  • -XX:SurvivorRatio=N
  • -XX:MaxTenuringThreshold=N

可視化Java垃圾回收

        圖六

        為什么要有日志文件

        日志文件的好處是能夠用于取證分析,可以使用戶免于為了再現問題而不得不再執行一次代碼(如果是一個罕見的生產環境錯誤,那么重現并不容易)。

        另外,它們包含的信息比針對內存的 JMX MXBeans 所能提供的信息更多,且不說輪詢 JMX 本身會引入一系列 GC 問題。

        工具

  • HP JMeter(用 Google 查詢一下)

            ——免費,非常可靠,但不再提供支持/功能增強

  • GCViewer

            ——免費,開源,但界面有點丑

  • GarbageCat

            ——名字最好聽

  • IBM GCMV

            ——支持 J9

  • jClarity Censum

            ——界面最美觀,而且最有用——不過,這是我們的偏見!

        小結

  • 需要了解一些 GC 基礎理論
  • 要讓新生代的大部分對象在年輕時死亡
  • 打開 GC 日志!——原始日志文件難以閱讀——使用工具
  • 使用工具來幫助自己調優——測量,而不是猜測

        查看完整演講視頻,請點擊這里

        關于作者

可視化Java垃圾回收

        Ben Evans 是一家 Java/JVM 性能分析創業公司 jClarity 的 CEO。在業余時間,他是倫敦 Java 社區的一名負責人,也是 Java 社區過程執行委員會成員之一。他先前的項目包括:對 Google IPO、金融交易系統做性能測試,為若干 90 年代最大的電影開發獲獎網站等等。

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