關於GC原理和性能調優實踐,看這一篇就夠了!

前言

本文介紹 GC 基礎原理和理論,GC 調優方法思路和方法,基於 Hotspot jdk1.8,學習以後你將瞭解如何對生產系統出現的 GC 問題進行排查解決。git

正文

本文的內容主要以下:github

  • GC 基礎原理,涉及調優目標,GC 事件分類、JVM 內存分配策略、GC 日誌分析等
  • CMS 原理及調優。
  • G1 原理及調優
  • GC 問題排查和解決思路

1. GC 基礎原理

1.1. GC 調優目標

大多數狀況下對 Java 程序進行 GC 調優,主要關注兩個目標:算法

  • 響應速度(Responsiveness):響應速度指程序或系統對一個請求的響應有多迅速編程

    好比,用戶訂單查詢響應時間,對響應速度要求很高的系統,較大的停頓時間是不可接受的。調優的重點是在短的時間內快速響應。後端

  • 吞吐量(Throughput):吞吐量關注在一個特定時間段內應用系統的最大工做量緩存

    例如每小時批處理系統能完成的任務數量,在吞吐量方面優化的系統,較長的 GC 停頓時間也是能夠接受的,由於高吞吐量應用更關心的是如何儘量快地完成整個任務,不考慮快速響應用戶請求安全

在 GC 調優中,GC 致使的應用暫停時間影響系統響應速度,GC 處理線程的 CPU 使用率影響系統吞吐量。bash

1.2. GC 分代收集算法

現代的垃圾收集器基本都是採用分代收集算法,其主要思想: 將 Java 的堆內存邏輯上分紅兩塊:新生代、老年代,針對不一樣存活週期、不一樣大小的對象採起不一樣的垃圾回收策略。服務器

1.2.1. 新生代(Young Generation)

新生代又叫年輕代,大多數對象在新生代中被建立,不少對象的生命週期很短。每次新生代的垃圾回收(又稱 Young GC、Minor GC、YGC)後只有少許對象存活,因此使用複製算法,只需少許的複製操做成本就能夠完成回收。多線程

**新生代內又分三個區:**一個 Eden 區,兩個 Survivor 區(S0、S1,又稱From Survivor、To Survivor),大部分對象在 Eden 區中生成。

當 Eden 區滿時,還存活的對象將被複制到兩個 Survivor 區(中的一個);當這個 Survivor 區滿時,此區的存活且不知足晉升到老年代條件的對象將被複制到另一個 Survivor 區。對象每經歷一次複製,年齡加 1,達到晉升年齡閾值後,轉移到老年代。

1.2.2. 老年代(Old Generation)

在新生代中經歷了 N 次垃圾回收後仍然存活的對象,就會被放到老年代,該區域中對象存活率高。老年代的垃圾回收一般使用「標記-整理」算法。

1.3. GC 事件分類

根據垃圾收集回收的區域不一樣,垃圾收集主要分爲:

  • Young GC
  • Old GC
  • Full GC
  • Mixed GC

1.3.1. Young GC

新生代內存的垃圾收集事件稱爲 Young GC(又稱 Minor GC),當 JVM 沒法爲新對象分配在新生代內存空間時總會觸發 Young GC。好比 Eden 區佔滿時,新對象分配頻率越高,Young GC 的頻率就越高。

Young GC 每次都會引發全線停頓(Stop-The-World),暫停全部的應用線程,停頓時間相對老年代 GC 形成的停頓,幾乎能夠忽略不計。

1.3.2. Old GC/Full GC/Mixed GC

Old GC:只清理老年代空間的 GC 事件,只有 CMS 的併發收集是這個模式。

Full GC:清理整個堆的 GC 事件,包括新生代、老年代、元空間等 。

Mixed GC:清理整個新生代以及部分老年代的 GC,只有 G1 有這個模式。

1.4. GC 日誌分析

GC 日誌是一個很重要的工具,它準確記錄了每一次的 GC 的執行時間和執行結果,經過分析 GC 日誌能夠調優堆設置和 GC 設置,或者改進應用程序的對象分配模式。

開啓的 JVM 啓動參數以下:

-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps  -XX:+PrintGCTimeStamps
複製代碼

常見的 Young GC、Full GC 日誌含義以下:

  • Young GC

  • Full GC

免費的 GC 日誌圖形分析工具推薦下面 2 個:

  • GCViewer:下載 jar 包直接運行
  • gceasy:Web 工具,上傳 GC 日誌在線使用

1.5. 內存分配策略

Java 提供的自動內存管理,能夠歸結爲解決了對象的內存分配和回收的問題。前面已經介紹了內存回收,下面介紹幾條最廣泛的內存分配策略:

1.5.1. 對象優先在 Eden 區分配

大多數狀況下,對象在先新生代 Eden 區中分配。當 Eden 區沒有足夠空間進行分配時,虛擬機將發起一次 Young GC。

1.5.2. 大對象直接進入老年代

JVM 提供了一個對象大小閾值參數(-XX:PretenureSizeThreshold,默認值爲 0,表明無論多大都是先在 Eden 中分配內存)。

大於參數設置的閾值值的對象直接在老年代分配,這樣能夠避免對象在 Eden 及兩個 Survivor 直接發生大內存複製。

1.5.3. 長期存活的對象進入老年代

對象每經歷一次垃圾回收,且沒被回收掉,它的年齡就增長 1,大於年齡閾值參數(-XX:MaxTenuringThreshold,默認 15)的對象,將晉升到老年代中。

1.5.4. 空間分配擔保

當進行 Young GC 以前,JVM 須要預估:老年代是否可以容納 Young GC 後新生代晉升到老年代的存活對象,以肯定是否須要提早觸發 GC 回收老年代空間,基於空間分配擔保策略來計算。

Young GC 以後若是成功(Young GC 後晉升對象能放入老年代),則表明擔保成功,不用再進行 Full GC,提升性能。

若是失敗,則會出現「promotion failed」錯誤,表明擔保失敗,須要進行 Full GC。

1.5.5. 動態年齡斷定

新生代對象的年齡可能沒達到閾值(MaxTenuringThreshold 參數指定)就晉升老年代。

若是 Young GC 以後,新生代存活對象達到相同年齡全部對象大小的總和大於任意 Survivor 空間(S0+S1空間)的一半,此時 S0 或者 S1 區即將容納不了存活的新生代對象。

年齡大於或等於該年齡的對象就能夠直接進入老年代,無須等到 MaxTenuringThreshold 中要求的年齡。

另外,若是 Young GC 後 S0 或 S1 區不足以容納:未達到晉升老年代條件的新生代存活對象,會致使這些存活對象直接進入老年代,須要儘可能避免。

2. CMS 原理及調優

2.1. 術語解釋

2.1.1. 可達性分析算法

用於判斷對象是否存活,基本思想是經過一系列稱爲「GC Root」的對象做爲起點(常見的 GC Root 有系統類加載器、棧中的對象、處於激活狀態的線程等),基於對象引用關係,從 GC Roots 開始向下搜索,所走過的路徑稱爲引用鏈,當一個對象到 GC Root 沒有任何引用鏈相連,證實對象再也不存活。

2.1.2. Stop The World

GC 過程當中分析對象引用關係,爲了保證分析結果的準確性,須要經過停頓全部 Java 執行線程,保證引用關係再也不動態變化,該停頓事件稱爲 Stop The World(STW)。

2.1.3. Safepoint

代碼執行過程當中的一些特殊位置,當線程執行到這些位置的時候,說明虛擬機當前的狀態是安全的,若是有須要 GC,線程能夠在這個位置暫停。

HotSpot 採用主動中斷的方式,讓執行線程在運行期輪詢是否須要暫停的標誌,若須要則中斷掛起。

2.2. CMS 算法簡介

CMS(Concurrent Mark and Sweep 併發-標記-清除),是一款基於併發、使用標記清除算法的垃圾回收算法,只針對老年代進行垃圾回收。

CMS 收集器工做時,儘量讓 GC 線程和用戶線程併發執行,以達到下降 STW 時間的目的。

經過如下命令行參數,啓用 CMS 垃圾收集器:

-XX:+UseConcMarkSweepGC
複製代碼

值得補充的是,下面介紹到的 CMS GC 是指老年代的 GC,而 Full GC 指的是整個堆的 GC 事件,包括新生代、老年代、元空間等,二者有所區分。

2.3. 新生代垃圾回收

能與 CMS 搭配使用的新生代垃圾收集器有 Serial 收集器和 ParNew 收集器。

這 2 個收集器都採用標記複製算法,都會觸發 STW 事件,中止全部的應用線程。不一樣之處在於,Serial 是單線程執行,ParNew 是多線程執行。

2.4. 老年代垃圾回收

CMS GC 以獲取最小停頓時間爲目的,儘量減小 STW 時間,能夠分爲 7 個階段:

2.4.1. 初始標記(Initial Mark)

初始標記階段的目標是標記老年代中全部存活的對象, 包括 GC Root 的直接引用, 以及由新生代中存活對象所引用的對象,觸發第一次 STW 事件。

這個過程是支持多線程的(JDK7 以前單線程,JDK8 以後並行,可經過參數 CMSParallelInitialMarkEnabled 調整)。

2.4.2. 併發標記(Concurrent Mark)

併發標記階段 GC 線程和應用線程併發執行,遍歷階段 1 初始標記出來的存活對象,而後繼續遞歸標記這些對象可達的對象。

2.4.3. 併發預清理(Concurrent Preclean)

併發預清理階段 GC 線程和應用線程也是併發執行,由於階段 2 是與應用線程併發執行,可能有些引用關係已經發生改變。

經過卡片標記(Card Marking),提早把老年代空間邏輯劃分爲相等大小的區域(Card)。

若是引用關係發生改變,JVM 會將發生改變的區域標記爲 「髒區」(Dirty Card),而後在本階段,這些髒區會被找出來,刷新引用關係,清除「髒區」標記。

2.4.4. 併發可取消的預清理(Concurrent Abortable Preclean)

併發可取消的預清理階段也不中止應用線程。本階段嘗試在 STW 的最終標記階段(Final Remark)以前儘量地多作一些工做,以減小應用暫停時間。

在該階段不斷循環處理:標記老年代的可達對象、掃描處理 Dirty Card 區域中的對象,循環的終止條件有:

  • 達到循環次數
  • 達到循環執行時間閾值
  • 新生代內存使用率達到閾值

2.4.5. 最終標記(Final Remark)

這是 GC 事件中第二次(也是最後一次)STW 階段,目標是完成老年代中全部存活對象的標記,此階段會執行:

  • 遍歷新生代對象,從新標記
  • 根據 GC Roots,從新標記
  • 遍歷老年代的 Dirty Card,從新標記

2.4.6. 併發清除(Concurrent Sweep)

併發清除階段與應用程序併發執行,不須要 STW 停頓,根據標記結果清除垃圾對象。

2.4.7. 併發重置(Concurrent Reset)

併發重置階段與應用程序併發執行,重置 CMS 算法相關的內部數據, 爲下一次 GC 循環作準備。

2.5. CMS 常見問題

2.5.1. 最終標記階段停頓時間過長

CMS 的 GC 停頓時間約 80% 都在最終標記階段(Final Remark),若該階段停頓時間過長,常見緣由是新生代對老年代的無效引用,在上一階段的併發可取消預清理階段中,執行閾值時間內未完成循環,來不及觸發 Young GC,清理這些無效引用。

經過添加參數:-XX:+CMSScavengeBeforeRemark。

在執行最終操做以前先觸發 Young GC,從而減小新生代對老年代的無效引用,下降最終標記階段的停頓。

但若是在上個階段(併發可取消的預清理)已觸發 Young GC,也會重複觸發 Young GC。

2.5.2. 併發模式失敗 & 晉升失敗

併發模式失敗:當 CMS 在執行回收時,新生代發生垃圾回收,同時老年代又沒有足夠的空間容納晉升的對象時,CMS 垃圾回收就會退化成單線程的 Full GC。全部的應用線程都會被暫停,老年代中全部的無效對象都被回收。

晉升失敗:當新生代發生垃圾回收,老年代有足夠的空間能夠容納晉升的對象,可是因爲空閒空間的碎片化,致使晉升失敗,此時會觸發單線程且帶壓縮動做的 Full GC。

併發模式失敗和晉升失敗都會致使長時間的停頓,常看法決思路以下:

  • 下降觸發 CMS GC 的閾值

    即參數 -XX:CMSInitiatingOccupancyFraction 的值,讓 CMS GC 儘早執行,以保證有足夠的空間

  • 增長 CMS 線程數,即參數 -XX:ConcGCThreads

  • 增大老年代空間

  • 讓對象儘可能在新生代回收,避免進入老年代

2.5.3. 內存碎片問題

一般 CMS 的 GC 過程基於標記清除算法,不帶壓縮動做,致使愈來愈多的內存碎片須要壓縮。

常見如下場景會觸發內存碎片壓縮:

  • 新生代 Young GC 出現新生代晉升擔保失敗(promotion failed))
  • 程序主動執行System.gc()

可經過參數 CMSFullGCsBeforeCompaction 的值,設置多少次 Full GC 觸發一次壓縮。

默認值爲 0,表明每次進入 Full GC 都會觸發壓縮,帶壓縮動做的算法爲上面提到的單線程 Serial Old 算法,暫停時間(STW)時間很是長,須要儘量減小壓縮時間。

3. G1 原理及調優

3.1. G1 簡介

G1(Garbage-First)是一款面向服務器的垃圾收集器,支持新生代和老年代空間的垃圾收集,主要針對配備多核處理器及大容量內存的機器。

G1 最主要的設計目標是:實現可預期及可配置的 STW 停頓時間。

3.2. G1 堆空間劃分

3.2.1. Region

爲實現大內存空間的低停頓時間的回收,將劃分爲多個大小相等的 Region。每一個小堆區均可能是 Eden 區,Survivor 區或者 Old 區,可是在同一時刻只能屬於某個代。

在邏輯上, 全部的 Eden 區和 Survivor 區合起來就是新生代,全部的 Old 區合起來就是老年代,且新生代和老年代各自的內存 Region 區域由 G1 自動控制,不斷變更。

3.2.2. 巨型對象

當對象大小超過 Region 的一半,則認爲是巨型對象(Humongous Object),直接被分配到老年代的巨型對象區(Humongous Regions)。

這些巨型區域是一個連續的區域集,每個 Region 中最多有一個巨型對象,巨型對象能夠佔多個 Region。

G1 把堆內存劃分紅一個個 Region 的意義在於:

  • 每次 GC 沒必要都去處理整個堆空間,而是每次只處理一部分 Region,實現大容量內存的 GC。
  • 經過計算每一個 Region 的回收價值,包括回收所需時間、可回收空間,在有限時間內儘量回收更多的垃圾對象,把垃圾回收形成的停頓時間控制在預期配置的時間範圍內,這也是 G1 名稱的由來:Garbage-First。

3.3. G1 工做模式

針對新生代和老年代,G1 提供 2 種 GC 模式,Young GC 和 Mixed GC,兩種會致使 Stop The World。

3.3.1. Young GC

當新生代的空間不足時,G1 觸發 Young GC 回收新生代空間。

Young GC 主要是對 Eden 區進行 GC,它在 Eden 空間耗盡時觸發,基於分代回收思想和複製算法,每次 Young GC 都會選定全部新生代的 Region。

同時計算下次 Young GC 所需的 Eden 區和 Survivor 區的空間,動態調整新生代所佔 Region 個數來控制 Young GC 開銷。

3.3.2. Mixed GC

當老年代空間達到閾值會觸發 Mixed GC,選定全部新生代裏的 Region,根據全局併發標記階段(下面介紹到)統計得出收集收益高的若干老年代 Region。

在用戶指定的開銷目標範圍內,儘量選擇收益高的老年代 Region 進行 GC,經過選擇哪些老年代 Region 和選擇多少 Region 來控制 Mixed GC 開銷。

3.4. 全局併發標記

全局併發標記主要是爲 Mixed GC 計算找出回收收益較高的 Region 區域,具體分爲 5 個階段:

3.4.1. 初始標記(Initial Mark)

暫停全部應用線程(STW),併發地進行標記從 GC Root 開始直接可達的對象(原生棧對象、全局對象、JNI 對象)。

當達到觸發條件時,G1 並不會當即發起併發標記週期,而是等待下一次新生代收集,利用新生代收集的 STW 時間段,完成初始標記,這種方式稱爲借道(Piggybacking)。

3.4.2. 根區域掃描(Root Region Scan)

在初始標記暫停結束後,新生代收集也完成的對象複製到 Survivor 的工做,應用線程開始活躍起來。

此時爲了保證標記算法的正確性,全部新複製到 Survivor 分區的對象,須要找出哪些對象存在對老年代對象的引用,把這些對象標記成根(Root)。

這個過程稱爲根分區掃描(Root Region Scanning),同時掃描的 Suvivor 分區也被稱爲根分區(Root Region)。

根分區掃描必須在下一次新生代垃圾收集啓動前完成(接下來併發標記的過程當中,可能會被若干次新生代垃圾收集打斷),由於每次 GC 會產生新的存活對象集合。

3.4.3. 併發標記(Concurrent Marking)

標記線程與應用程序線程並行執行,標記各個堆中 Region 的存活對象信息,這個步驟可能被新的 Young GC 打斷。

全部的標記任務必須在堆滿前就完成掃描,若是併發標記耗時很長,那麼有可能在併發標記過程當中,又經歷了幾回新生代收集。

3.4.4. 再次標記(Remark)

和 CMS 相似暫停全部應用線程(STW),以完成標記過程短暫地中止應用線程, 標記在併發標記階段發生變化的對象,和全部未被標記的存活對象,同時完成存活數據計算。

3.4.5. 清理(Cleanup)

爲即將到來的轉移階段作準備, 此階段也爲下一次標記執行全部必需的整理計算工做:

  • 整理更新每一個 Region 各自的 RSet(Remember Set,HashMap 結構,記錄有哪些老年代對象指向本 Region,key 爲指向本 Region 的對象的引用,value 爲指向本 Region 的具體 Card 區域,經過 RSet 能夠肯定 Region 中對象存活信息,避免全堆掃描)
  • 回收不包含存活對象的 Region
  • 統計計算回收收益高(基於釋放空間和暫停目標)的老年代分區集合

3.5. G1調優注意點

3.5.1. Full GC 問題

G1 的正常處理流程中沒有 Full GC,只有在垃圾回收處理不過來(或者主動觸發)時纔會出現,G1 的 Full GC 就是單線程執行的 Serial old gc,會致使很是長的 STW,是調優的重點,須要儘可能避免 Full GC。

常見緣由以下:

  • 程序主動執行 System.gc()
  • 全局併發標記期間老年代空間被填滿(併發模式失敗)
  • Mixed GC 期間老年代空間被填滿(晉升失敗)
  • Young GC 時 Survivor 空間和老年代沒有足夠空間容納存活對象

相似 CMS,常見的解決是:

  • 增大 -XX:ConcGCThreads=n 選項增長併發標記線程的數量,或者 STW 期間並行線程的數量:-XX:ParallelGCThreads=n。
  • 減少 -XX:InitiatingHeapOccupancyPercent 提早啓動標記週期。
  • 增大預留內存 -XX:G1ReservePercent=n,默認值是 10,表明使用 10% 的堆內存爲預留內存,當 Survivor 區域沒有足夠空間容納新晉升對象時會嘗試使用預留內存。

3.5.2. 巨型對象分配

巨型對象區中的每一個 Region 中包含一個巨型對象,剩餘空間再也不利用,致使空間碎片化,當 G1 沒有合適空間分配巨型對象時,G1 會啓動串行 Full GC 來釋放空間。

能夠經過增長 -XX:G1HeapRegionSize 來增大 Region 大小,這樣一來,至關一部分的巨型對象就再也不是巨型對象了,而是採用普通的分配方式。

3.5.3. 不要設置 Young 區的大小

緣由是爲了儘可能知足目標停頓時間,邏輯上的 Young 區會進行動態調整。若是設置了大小,則會覆蓋掉而且會禁用掉對停頓時間的控制。

3.5.4. 平均響應時間設置

使用應用的平均響應時間做爲參考來設置 MaxGCPauseMillis,JVM 會盡可能去知足該條件,多是 90% 的請求或者更多的響應時間在這以內, 可是並不表明是全部的請求都能知足,平均響應時間設置太小會致使頻繁 GC。

4. 調優方法與思路

如何分析系統 JVM GC 運行情況及合理優化?

GC 優化的核心思路在於,儘量讓對象在新生代中分配和回收,儘可能避免過多對象進入老年代,致使對老年代頻繁進行垃圾回收,同時給系統足夠的內存減小新生代垃圾回收次數,進行系統分析和優化也是圍繞着這個思路展開。

4.1. 分析系統的運行情況

分析系統的運行情況:

  • 系統每秒請求數、每一個請求建立多少對象,佔用多少內存。
  • Young GC 觸發頻率、對象進入老年代的速率。
  • 老年代佔用內存、Full GC 觸發頻率、Full GC 觸發的緣由、長時間 Full GC 的緣由。

經常使用工具以下:

4.1.1. jstat

jstat 是 JVM 自帶命令行工具,可用於統計內存分配速率、GC 次數,GC 耗時。經常使用命令格式以下:

jstat -gc <pid> <統計間隔時間>  <統計次數>
複製代碼

輸出返回值表明含義以下:

例如:jstat -gc 32683 1000 10,統計 pid=32683 的進程,每秒統計 1 次,統計 10 次。

4.1.2. jmap

jmap 也是 JVM 自帶命令行工具,可用於瞭解系統運行時的對象分佈。經常使用命令格式以下:

// 命令行輸出類名、類數量數量,類佔用內存大小,
// 按照類佔用內存大小降序排列
jmap -histo <pid>

// 生成堆內存轉儲快照,在當前目錄下導出dump.hrpof的二進制文件,
// 能夠用eclipse的MAT圖形化工具分析
jmap -dump:live,format=b,file=dump.hprof <pid>
複製代碼

4.1.3. jinfo

用來查看正在運行的 Java 應用程序的擴展參數,包括 Java System 屬性和 JVM 命令行參數。命令格式以下:

jinfo <pid> 
複製代碼

4.1.4. 其餘 GC 工具

  • 監控告警系統:Zabbix、Prometheus、Open-Falcon
  • jdk 自動實時內存監控工具:VisualVM
  • 堆外內存監控:Java VisualVM 安裝 Buffer Pools 插件、google perf工具、Java NMT(Native Memory Tracking)工具
  • GC 日誌分析:GCViewer、gceasy
  • GC 參數檢查和優化:xxfox.perfma.com/

4.2. GC 優化案例

4.2.1. 數據分析平臺系統頻繁 Full GC

平臺主要對用戶在 App 中行爲進行定時分析統計,並支持報表導出,使用 CMS GC 算法。

數據分析師在使用中發現系統頁面打開常常卡頓,經過 jstat 命令發現系統每次 Young GC 後大約有 10% 的存活對象進入老年代。

原來是由於 Survivor 區空間設置太小,每次 Young GC 後存活對象在 Survivor 區域放不下,提早進入老年代。

經過調大 Survivor 區,使得 Survivor 區能夠容納 Young GC 後存活對象,對象在 Survivor 區經歷屢次 Young GC 達到年齡閾值才進入老年代。

調整以後每次 Young GC 後進入老年代的存活對象穩定運行時僅幾百 Kb,Full GC 頻率大大下降。

4.2.2. 業務對接網關 OOM

網關主要消費 Kafka 數據,進行數據處理計算而後轉發到另外的 Kafka 隊列,系統運行幾個小時候出現 OOM,重啓系統幾個小時以後又 OOM。

經過 jmap 導出堆內存,在 eclipse MAT 工具分析才找出緣由:代碼中將某個業務 Kafka 的 topic 數據進行日誌異步打印,該業務數據量較大,大量對象堆積在內存中等待被打印,致使 OOM。

4.2.3. 鑑權系統頻繁長時間 Full GC

系統對外提供各類帳號鑑權服務,使用時發現系統常常服務不可用,經過 Zabbix 的監控平臺監控發現系統頻繁發生長時間 Full GC,且觸發時老年代的堆內存一般並無佔滿,發現原來是業務代碼中調用了 System.gc()。

小結

GC 問題能夠說沒有捷徑,排查線上的性能問題自己就並不簡單,除了將本文介紹到的原理和工具融會貫通,還須要咱們不斷去積累經驗,真正作到性能最優。

篇幅所限,再也不展開介紹常見 GC 參數的使用,能夠從 GitHub 克隆:

https://github.com/caison/caison-blog-demo
複製代碼

參考

  • 《Java Performance: The Definitive Guide》 Scott Oaks
  • 《深刻理解 Java 虛擬機:JVM 高級特性與最佳實踐(第二版》 周志華
  • Java 性能調優實戰
  • Getting Started with the G1 Garbage Collector
  • GC 參考手冊-Java 版
  • 請教 G1 算法的原理——RednaxelaFX 的回答
  • Java Hotspot G1 GC 的一些關鍵技術——美團技術團隊

轉載:陳彩華(caison),Akulaku 岩心科技開發工程師,喜歡研究分佈式系統、線上問題排查、架構設計

關於公衆號

本賬號持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。

相關文章
相關標籤/搜索