垃圾收集:提高吞吐量
這篇文章的靈感來自于在內存管理術語中的“ Pig in the Python ”定義。 顯然,該術語用于解釋GC反復促進大對象世代相傳的情況。 據推測,這樣做的效果類似于Python吞下整個獵物,只是在消化過程中被固定住了。
在接下來的24小時里,我簡直無法理解令人窒息的python的圖片。 就像精神科醫生所說的那樣,消除恐懼的最好方法就是談論它們。 所以我們開始。 但是,除了Python,其余的故事都是關于垃圾收集優化的。 我承諾。
垃圾收集暫停是眾所周知的性能瓶頸。 現代JVM確實帶有高級垃圾收集器,但是據我所知,為特定應用程序找到最佳配置仍然很困難。 為了有機會手動解決該問題,需要了解垃圾收集算法的確切機制。 這篇文章可能會在這方面為您提供幫助,因為我將使用一個示例來演示JVM配置中的微小更改如何影響應用程序的吞吐量。
例
我們用來演示GC對吞吐量影響的應用程序很簡單。 它僅包含兩個線程:
- PigEater –模擬一種情況,Python不斷吃掉另一頭豬。 該代碼通過在java.util.List中添加32MB字節并在每次嘗試后休眠100ms來實現此目的。
- PigDigester –模擬異步摘要過程。 該代碼僅通過使該豬列表無效來實現消化。 由于這是一個相當累的過程,因此在每次參考清潔后,該線程將休眠2000ms。
兩個線程都會在while循環中運行,繼續吃和消化,直到蛇吃飽為止。 大約有5,000頭豬被吃掉。
package eu.plumbr.demo;public class PigInThePython {static volatile List pigs = new ArrayList();static volatile int pigsEaten = 0;static final int ENOUGH_PIGS = 5000;public static void main(String[] args) throws InterruptedException {new PigEater().start();new PigDigester().start();}static class PigEater extends Thread {@Overridepublic void run() {while (true) {pigs.add(new byte[32 * 1024 * 1024]); //32MB per pigif (pigsEaten > ENOUGH_PIGS) return;takeANap(100);}}}static class PigDigester extends Thread {@Overridepublic void run() {long start = System.currentTimeMillis();while (true) {takeANap(2000);pigsEaten+=pigs.size();pigs = new ArrayList();if (pigsEaten > ENOUGH_PIGS) {System.out.format("Digested %d pigs in %d ms.%n",pigsEaten, System.currentTimeMillis()-start);return;}}}}static void takeANap(int ms) {try {Thread.sleep(ms);} catch (Exception e) {e.printStackTrace();}} }現在讓我們將該系統的吞吐量定義為“每秒消化的豬的數量”。 考慮到每100毫秒后將豬塞入python,我們看到該系統的理論最大吞吐量因此可以達到10頭/秒。
配置GC示例
讓我們來看一下使用兩種不同配置的系統行為。 在所有情況下,該應用程序都是使用具有8G物理內存的雙核Mac(OS X 10.9.3)運行的。
第一種配置:
- 4G堆( -Xms4g –Xmx4g )
- 使用CMS清理舊的(-XX:+ UseConcMarkSweepGC ),并并行清理年輕的-XX:+ UseParNewGC )
- 已將堆的12,5%(- Xmn512m )分配給年輕一代,從而進一步將Eden和Survivor空間的大小限制為相同大小。
第二種配置有些不同:
- 2G堆( -Xms2g –Xmx2g )
- 使用并行GC在年輕一代和終身一代中進行垃圾收集( -XX:+ UseParallelGC )
- 已將堆的75%分配給年輕一代( -Xmn1536m )
現在該下賭注了,哪種配置在吞吐量方面表現更好(每秒吃掉的豬,還記得嗎?)。 你們那些花錢買第一種配置的人,我一定會讓您失望的。 結果完全相反:
- 第一種配置(大堆,較大的舊空間,CMS GC)每秒可以吃掉8.2頭豬
- 第二種配置(較小的堆2倍,較大的幼小空間,并行GC)每秒可吞噬9.2頭豬
現在,讓我對結果進行透視。 分配的資源減少了2倍(在內存方面),我們的吞吐量提高了12% 。 這與常識相反,可能需要進一步澄清實際發生的情況。
解釋GC結果
您所面對的原因并不是太復雜,而當您更仔細地觀察測試運行期間GC的操作時,答案就直盯著您。 為此,您可以使用自己選擇的工具,我在jstat的幫助下進行了深入研究,類似于以下內容:
jstat -gc -t -h20 PID 1秒
查看數據,我注意到第一個配置經歷了1,129個垃圾回收周期(YGCT + FGCT),總共花費了63.723秒:
Timestamp S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT 594.0 174720.0 174720.0 163844.1 0.0 174848.0 131074.1 3670016.0 2621693.5 21248.0 2580.9 1006 63.182 116 0.236 63.419 595.0 174720.0 174720.0 163842.1 0.0 174848.0 65538.0 3670016.0 3047677.9 21248.0 2580.9 1008 63.310 117 0.236 63.546 596.1 174720.0 174720.0 98308.0 163842.1 174848.0 163844.2 3670016.0 491772.9 21248.0 2580.9 1010 63.354 118 0.240 63.595 597.0 174720.0 174720.0 0.0 163840.1 174848.0 131074.1 3670016.0 688380.1 21248.0 2580.9 1011 63.482 118 0.240 63.723第二種配置總共暫停了168次(YGCT + FGCT),僅11.409秒。
Timestamp S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT 539.3 164352.0 164352.0 0.0 0.0 1211904.0 98306.0 524288.0 164352.2 21504.0 2579.2 27 2.969 141 8.441 11.409 540.3 164352.0 164352.0 0.0 0.0 1211904.0 425986.2 524288.0 164352.2 21504.0 2579.2 27 2.969 141 8.441 11.409 541.4 164352.0 164352.0 0.0 0.0 1211904.0 720900.4 524288.0 164352.2 21504.0 2579.2 27 2.969 141 8.441 11.409 542.3 164352.0 164352.0 0.0 0.0 1211904.0 1015812.6 524288.0 164352.2 21504.0 2579.2 27 2.969 141 8.441 11.409考慮到在這兩種情況下需要進行的工作在以下方面是等效的:–在看不到長壽的物體的情況下,GC在此吃豬活動中的職責只是盡可能快地擺脫一切。 使用第一種配置時,GC僅被迫運行約6.7倍,導致總暫停時間延長約5.6倍。
因此,這個故事實現了兩個目的。 首先,最重要的是,我希望我能從頭上看到一條令人窒息的Python的照片。 另一個更重要的收獲是–調整GC充其量是一項棘手的工作,需要深刻理解幾個基本概念。 即使使用本博客文章中使用的真正瑣碎的應用程序,您將要面對的結果也會對吞吐量和容量規劃產生重大影響。 在實際應用中,差異甚至更加驚人。 因此,選擇就是您的選擇,您既可以掌握概念,也可以專注于日常工作,并讓Plumbr根據您的需求找到合適的GC配置 。
翻譯自: https://www.javacodegeeks.com/2014/09/garbage-collection-increasing-the-throughput.html
總結
以上是生活随笔為你收集整理的垃圾收集:提高吞吐量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安庆物价局新房备案价商品房公示(安庆物价
- 下一篇: (linux的下载命令)