GC参考手册 —— GC 调优(基础篇)
GC調(diào)優(yōu)(Tuning Garbage Collection)和其他性能調(diào)優(yōu)是同樣的原理。初學(xué)者可能會被 200 多個 GC參數(shù)弄得一頭霧水, 然后隨便調(diào)整幾個來試試結(jié)果,又或者修改幾行代碼來測試。其實(shí)只要參照下面的步驟,就能保證你的調(diào)優(yōu)方向正確:
第一步, 我們需要做的事情就是: 制定明確的GC性能指標(biāo)。對所有性能監(jiān)控和管理來說, 有三個維度是通用的:
- Latency(延遲)
- Throughput(吞吐量)
- Capacity(系統(tǒng)容量)
我們先講解基本概念,然后再演示如何使用這些指標(biāo)。如果您對 延遲、吞吐量和系統(tǒng)容量等概念很熟悉, 可以跳過這一小節(jié)。
核心概念(Core Concepts)
我們先來看一家工廠的裝配流水線。工人在流水線將現(xiàn)成的組件按順序拼接,組裝成自行車。通過實(shí)地觀測, 我們發(fā)現(xiàn)從組件進(jìn)入生產(chǎn)線,到另一端組裝成自行車需要4小時。
繼續(xù)觀察,我們還發(fā)現(xiàn),此后每分鐘就有1輛自行車完成組裝, 每天24小時,一直如此。將這個模型簡化, 并忽略維護(hù)窗口期后得出結(jié)論:?這條流水線每小時可以組裝60輛自行車。
說明: 時間窗口/窗口期,請類比車站賣票的窗口,是一段規(guī)定/限定做某件事的時間段。
通過這兩種測量方法, 就知道了生產(chǎn)線的相關(guān)性能信息:?延遲與吞吐量:
- 生產(chǎn)線的延遲:?4小時
- 生產(chǎn)線的吞吐量:?60輛/小時
請注意, 衡量延遲的時間單位根據(jù)具體需要而確定 —— 從納秒(nanosecond)到幾千年(millennia)都有可能。系統(tǒng)的吞吐量是每個單位時間內(nèi)完成的操作。操作(Operations)一般是特定系統(tǒng)相關(guān)的東西。在本例中,選擇的時間單位是小時, 操作就是對自行車的組裝。
掌握了延遲和吞吐量兩個概念之后, 讓我們對這個工廠來進(jìn)行實(shí)際的調(diào)優(yōu)。自行車的需求在一段時間內(nèi)都很穩(wěn)定, 生產(chǎn)線組裝自行車有四個小時延遲, 而吞吐量在幾個月以來都很穩(wěn)定: 60輛/小時。假設(shè)某個銷售團(tuán)隊(duì)突然業(yè)績暴漲, 對自行車的需求增加了1倍。客戶每天需要的自行車不再是 60 * 24 = 1440輛, 而是 2*1440 = 2880輛/天。老板對工廠的產(chǎn)能不滿意,想要做些調(diào)整以提升產(chǎn)能。
看起來總經(jīng)理很容易得出正確的判斷, 系統(tǒng)的延遲沒法子進(jìn)行處理 —— 他關(guān)注的是每天的自行車生產(chǎn)總量。得出這個結(jié)論以后, 假若工廠資金充足, 那么應(yīng)該立即采取措施, 改善吞吐量以增加產(chǎn)能。
我們很快會看到, 這家工廠有兩條相同的生產(chǎn)線。每條生產(chǎn)線一分鐘可以組裝一輛成品自行車。 可以想象,每天生產(chǎn)的自行車數(shù)量會增加一倍。達(dá)到 2880輛/天。要注意的是, 不需要減少自行車的裝配時間 —— 從開始到結(jié)束依然需要 4 小時。
巧合的是,這樣進(jìn)行的性能優(yōu)化,同時增加了吞吐量和產(chǎn)能。一般來說,我們會先測量當(dāng)前的系統(tǒng)性能, 再設(shè)定新目標(biāo), 只優(yōu)化系統(tǒng)的某個方面來滿足性能指標(biāo)。
在這里做了一個很重要的決定 —— 要增加吞吐量,而不是減小延遲。在增加吞吐量的同時, 也需要增加系統(tǒng)容量。比起原來的情況, 現(xiàn)在需要兩條流水線來生產(chǎn)出所需的自行車。在這種情況下, 增加系統(tǒng)的吞吐量并不是免費(fèi)的, 需要水平擴(kuò)展, 以滿足增加的吞吐量需求。
在處理性能問題時, 應(yīng)該考慮到還有另一種看似不相關(guān)的解決辦法。假如生產(chǎn)線的延遲從1分鐘降低為30秒,那么吞吐量同樣可以增長 1 倍。
或者是降低延遲, 或者是客戶非常有錢。軟件工程里有一種相似的說法 —— 每個性能問題背后,總有兩種不同的解決辦法。 可以用更多的機(jī)器, 或者是花精力來改善性能低下的代碼。
Latency(延遲)
GC的延遲指標(biāo)由一般的延遲需求決定。延遲指標(biāo)通常如下所述:
- 所有交易必須在10秒內(nèi)得到響應(yīng)
- 90%的訂單付款操作必須在3秒以內(nèi)處理完成
- 推薦商品必須在 100 ms 內(nèi)展示到用戶面前
面對這類性能指標(biāo)時, 需要確保在交易過程中, GC暫停不能占用太多時間,否則就滿足不了指標(biāo)。“不能占用太多” 的意思需要視具體情況而定, 還要考慮到其他因素, 比如外部數(shù)據(jù)源的交互時間(round-trips), 鎖競爭(lock contention), 以及其他的安全點(diǎn)等等。
假設(shè)性能需求為:?90%的交易要在?1000ms?以內(nèi)完成, 每次交易最長不能超過?10秒。 根據(jù)經(jīng)驗(yàn), 假設(shè)GC暫停時間比例不能超過10%。 也就是說, 90%的GC暫停必須在?100ms?內(nèi)結(jié)束, 也不能有超過?1000ms?的GC暫停。為簡單起見, 我們忽略在同一次交易過程中發(fā)生多次GC停頓的可能性。
有了正式的需求,下一步就是檢查暫停時間。在本節(jié)中我們通過查看GC日志, 檢查一下GC暫停的時間。相關(guān)的信息散落在不同的日志片段中, 看下面的數(shù)據(jù):
2015-06-04T13:34:16.974-0200: 2.578: [Full GC (Ergonomics) [PSYoungGen: 93677K->70109K(254976K)] [ParOldGen: 499597K->511230K(761856K)] 593275K->581339K(1016832K), [Metaspace: 2936K->2936K(1056768K)] , 0.0713174 secs] [Times: user=0.21 sys=0.02, real=0.07 secs這表示一次GC暫停, 在?2015-06-04T13:34:16?這個時刻觸發(fā). 對應(yīng)于JVM啟動之后的?2,578 ms。
此事件將應(yīng)用線程暫停了?0.0713174?秒。雖然花費(fèi)的總時間為 210 ms, 但因?yàn)槭嵌嗪薈PU機(jī)器, 所以最重要的數(shù)字是應(yīng)用線程被暫停的總時間, 這里使用的是并行GC, 所以暫停時間大約為?70ms?。 這次GC的暫停時間小于?100ms?的閾值,滿足需求。
繼續(xù)分析, 從所有GC日志中提取出暫停相關(guān)的數(shù)據(jù), 匯總之后就可以得知是否滿足需求。
Throughput(吞吐量)
吞吐量和延遲指標(biāo)有很大區(qū)別。當(dāng)然兩者都是根據(jù)一般吞吐量需求而得出的。一般吞吐量需求(Generic requirements for throughput) 類似這樣:
- 解決方案每天必須處理 100萬個訂單
- 解決方案必須支持1000個登錄用戶,同時在5-10秒內(nèi)執(zhí)行某個操作: A、B或C
- 每周對所有客戶進(jìn)行統(tǒng)計(jì), 時間不能超過6小時,時間窗口為每周日晚12點(diǎn)到次日6點(diǎn)之間。
可以看出,吞吐量需求不是針對單個操作的, 而是在給定的時間內(nèi), 系統(tǒng)必須完成多少個操作。和延遲需求類似, GC調(diào)優(yōu)也需要確定GC行為所消耗的總時間。每個系統(tǒng)能接受的時間不同, 一般來說, GC占用的總時間比不能超過?10%。
現(xiàn)在假設(shè)需求為: 每分鐘處理 1000 筆交易。同時, 每分鐘GC暫停的總時間不能超過6秒(即10%)。
有了正式的需求, 下一步就是獲取相關(guān)的信息。依然是從GC日志中提取數(shù)據(jù), 可以看到類似這樣的信息:
2015-06-04T13:34:16.974-0200: 2.578: [Full GC (Ergonomics) [PSYoungGen: 93677K->70109K(254976K)] [ParOldGen: 499597K->511230K(761856K)] 593275K->581339K(1016832K), [Metaspace: 2936K->2936K(1056768K)], 0.0713174 secs] [Times: user=0.21 sys=0.02, real=0.07 secs此時我們對 用戶耗時(user)和系統(tǒng)耗時(sys)感興趣, 而不關(guān)心實(shí)際耗時(real)。在這里, 我們關(guān)心的時間為?0.23s(user + sys = 0.21 + 0.02 s), 這段時間內(nèi), GC暫停占用了 cpu 資源。 重要的是, 系統(tǒng)運(yùn)行在多核機(jī)器上, 轉(zhuǎn)換為實(shí)際的停頓時間(stop-the-world)為?0.0713174秒, 下面的計(jì)算會用到這個數(shù)字。
提取出有用的信息后, 剩下要做的就是統(tǒng)計(jì)每分鐘內(nèi)GC暫停的總時間。看看是否滿足需求: 每分鐘內(nèi)總的暫停時間不得超過6000毫秒(6秒)。
Capacity(系統(tǒng)容量)
系統(tǒng)容量(Capacity)需求,是在達(dá)成吞吐量和延遲指標(biāo)的情況下,對硬件環(huán)境的額外約束。這類需求大多是來源于計(jì)算資源或者預(yù)算方面的原因。例如:
- 系統(tǒng)必須能部署到小于512 MB內(nèi)存的Android設(shè)備上
- 系統(tǒng)必須部署在Amazon?EC2實(shí)例上, 配置不得超過?c3.xlarge(4核8GB)。
- 每月的 Amazon EC2 賬單不得超過?$12,000
因此, 在滿足延遲和吞吐量需求的基礎(chǔ)上必須考慮系統(tǒng)容量。可以說, 假若有無限的計(jì)算資源可供揮霍, 那么任何 延遲和吞吐量指標(biāo) 都不成問題, 但現(xiàn)實(shí)情況是, 預(yù)算(budget)和其他約束限制了可用的資源。
相關(guān)示例
介紹完性能調(diào)優(yōu)的三個維度后, 我們來進(jìn)行實(shí)際的操作以達(dá)成GC性能指標(biāo)。
請看下面的代碼:
//imports skipped for brevity public class Producer implements Runnable { private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2); private Deque<byte[]> deque; private int objectSize; private int queueSize; public Producer(int objectSize, int ttl) { this.deque = new ArrayDeque<byte[]>(); this.objectSize = objectSize; this.queueSize = ttl * 1000; } @Override public void run() { for (int i = 0; i < 100; i++)轉(zhuǎn)載于:https://www.cnblogs.com/java-chen-hao/p/10650544.html
總結(jié)
以上是生活随笔為你收集整理的GC参考手册 —— GC 调优(基础篇)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sweet.js 用 Readtable
- 下一篇: JVM参数详解和调优