主要矛盾和次要矛盾_次要GC,主要GC与完整GC
主要矛盾和次要矛盾
在使用Plumbr中的GC暫停檢測功能時,我被迫通過大量有關該主題的文章,書籍和演示工作。 在整個旅程中,我多次對次要,主要和完全GC事件的使用(誤用)感到困惑。 這導致了這篇博客文章,我希望我設法消除一些困惑。
該帖子希望讀者熟悉JVM中內置的通用垃圾收集原理。 將堆劃分為Eden,幸存者和終身/舊空間,世代假設和不同的GC算法不在本文范圍之內。
次要GC
從年輕空間(由Eden和Survivor空間組成)收集垃圾稱為次要GC 。 這個定義既清晰又統一。 但是,在處理次要垃圾回收事件時,您仍然應該注意一些有趣的注意事項:
因此,對于Minor GC而言,情況非常清楚- 每個Minor GC都會清洗年輕一代 。
主GC與全GC
應該注意的是,這些術語沒有正式的定義。 JVM規范和垃圾收集研究論文均未提及。 但乍看之下,在我們認為對次要GC清潔年輕空間的正確認識之上建立這些定義應該很簡單:
- 主要GC正在清理使用權空間。
- Full GC可以清理整個堆-年輕的和終身使用的空間。
不幸的是,它有點復雜和混亂。 首先,許多主要GC由次要GC觸發,因此在很多情況下不可能將兩者分開。 另一方面,許多現代垃圾收集僅對部分使用權空間進行清理,因此,再次使用術語“清理”僅部分正確。
這就引出了一個點,您不必擔心GC是被稱為Major GC還是Full GC,而應專注于確定當前的GC是停止了所有應用程序線程還是能夠與應用程序線程同時進行 。
JVM標準工具中甚至內置了這種混淆。 我的意思最好通過一個例子來解釋。 讓我們比較在運行并發標記和清除收集器( -XX:+ UseConcMarkSweepGC )的JVM上跟蹤GC的兩種不同工具的輸出
首先嘗試通過jstat輸出獲得洞察力:
my-precious: me$ jstat -gc -t 4235 1sTime S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 5.7 34048.0 34048.0 0.0 34048.0 272640.0 194699.7 1756416.0 181419.9 18304.0 17865.1 2688.0 2497.6 3 0.275 0 0.000 0.2756.7 34048.0 34048.0 34048.0 0.0 272640.0 247555.4 1756416.0 263447.9 18816.0 18123.3 2688.0 2523.1 4 0.359 0 0.000 0.3597.7 34048.0 34048.0 0.0 34048.0 272640.0 257729.3 1756416.0 345109.8 19072.0 18396.6 2688.0 2550.3 5 0.451 0 0.000 0.4518.7 34048.0 34048.0 34048.0 34048.0 272640.0 272640.0 1756416.0 444982.5 19456.0 18681.3 2816.0 2575.8 7 0.550 0 0.000 0.5509.7 34048.0 34048.0 34046.7 0.0 272640.0 16777.0 1756416.0 587906.3 20096.0 19235.1 2944.0 2631.8 8 0.720 0 0.000 0.720 10.7 34048.0 34048.0 0.0 34046.2 272640.0 80171.6 1756416.0 664913.4 20352.0 19495.9 2944.0 2657.4 9 0.810 0 0.000 0.810 11.7 34048.0 34048.0 34048.0 0.0 272640.0 129480.8 1756416.0 745100.2 20608.0 19704.5 2944.0 2678.4 10 0.896 0 0.000 0.896 12.7 34048.0 34048.0 0.0 34046.6 272640.0 164070.7 1756416.0 822073.7 20992.0 19937.1 3072.0 2702.8 11 0.978 0 0.000 0.978 13.7 34048.0 34048.0 34048.0 0.0 272640.0 211949.9 1756416.0 897364.4 21248.0 20179.6 3072.0 2728.1 12 1.087 1 0.004 1.091 14.7 34048.0 34048.0 0.0 34047.1 272640.0 245801.5 1756416.0 597362.6 21504.0 20390.6 3072.0 2750.3 13 1.183 2 0.050 1.233 15.7 34048.0 34048.0 0.0 34048.0 272640.0 21474.1 1756416.0 757347.0 22012.0 20792.0 3200.0 2791.0 15 1.336 2 0.050 1.386 16.7 34048.0 34048.0 34047.0 0.0 272640.0 48378.0 1756416.0 838594.4 22268.0 21003.5 3200.0 2813.2 16 1.433 2 0.050 1.484該片段是從JVM啟動后的前17秒中提取的。 根據此信息,我們可以得出結論,在12次次要GC運行之后,執行了兩次完整GC運行,總共運行了50毫秒 。 您將通過基于GUI的工具(例如jconsole或jvisualvm)得到相同的確認。
在點此結論之前,讓我們看一下從同一JVM啟動收集的垃圾收集日志的輸出。 顯然-XX:+ PrintGCDetails告訴了我們一個不同而更詳細的故事:
java -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC eu.plumbr.demo.GarbageProducer3.157: [GC (Allocation Failure) 3.157: [ParNew: 272640K->34048K(306688K), 0.0844702 secs] 272640K->69574K(2063104K), 0.0845560 secs] [Times: user=0.23 sys=0.03, real=0.09 secs] 4.092: [GC (Allocation Failure) 4.092: [ParNew: 306688K->34048K(306688K), 0.1013723 secs] 342214K->136584K(2063104K), 0.1014307 secs] [Times: user=0.25 sys=0.05, real=0.10 secs] ... cut for brevity ... 11.292: [GC (Allocation Failure) 11.292: [ParNew: 306686K->34048K(306688K), 0.0857219 secs] 971599K->779148K(2063104K), 0.0857875 secs] [Times: user=0.26 sys=0.04, real=0.09 secs] 12.140: [GC (Allocation Failure) 12.140: [ParNew: 306688K->34046K(306688K), 0.0821774 secs] 1051788K->856120K(2063104K), 0.0822400 secs] [Times: user=0.25 sys=0.03, real=0.08 secs] 12.989: [GC (Allocation Failure) 12.989: [ParNew: 306686K->34048K(306688K), 0.1086667 secs] 1128760K->931412K(2063104K), 0.1087416 secs] [Times: user=0.24 sys=0.04, real=0.11 secs] 13.098: [GC (CMS Initial Mark) [1 CMS-initial-mark: 897364K(1756416K)] 936667K(2063104K), 0.0041705 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 13.102: [CMS-concurrent-mark-start] 13.341: [CMS-concurrent-mark: 0.238/0.238 secs] [Times: user=0.36 sys=0.01, real=0.24 secs] 13.341: [CMS-concurrent-preclean-start] 13.350: [CMS-concurrent-preclean: 0.009/0.009 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 13.350: [CMS-concurrent-abortable-preclean-start] 13.878: [GC (Allocation Failure) 13.878: [ParNew: 306688K->34047K(306688K), 0.0960456 secs] 1204052K->1010638K(2063104K), 0.0961542 secs] [Times: user=0.29 sys=0.04, real=0.09 secs] 14.366: [CMS-concurrent-abortable-preclean: 0.917/1.016 secs] [Times: user=2.22 sys=0.07, real=1.01 secs] 14.366: [GC (CMS Final Remark) [YG occupancy: 182593 K (306688 K)]14.366: [Rescan (parallel) , 0.0291598 secs]14.395: [weak refs processing, 0.0000232 secs]14.395: [class unloading, 0.0117661 secs]14.407: [scrub symbol table, 0.0015323 secs]14.409: [scrub string table, 0.0003221 secs][1 CMS-remark: 976591K(1756416K)] 1159184K(2063104K), 0.0462010 secs] [Times: user=0.14 sys=0.00, real=0.05 secs] 14.412: [CMS-concurrent-sweep-start] 14.633: [CMS-concurrent-sweep: 0.221/0.221 secs] [Times: user=0.37 sys=0.00, real=0.22 secs] 14.633: [CMS-concurrent-reset-start] 14.636: [CMS-concurrent-reset: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]根據這些信息,我們可以看到,確實在12次次要GC運行之后,“情況有所不同”開始發生。 但是實際上,這是兩個完全GC運行,而實際上,“不同的事情”只是一個在Tenured一代中運行的GC,它由不同的階段組成:
- 初始標記階段,持續0.0041705秒或大約4ms。 此階段是一個世界停止事件,它將停止所有應用程序線程以進行初始標記。
- 同時執行Markup和Preclean階段。 這些與應用程序線程并發運行
- 最終備注階段,范圍為0.0462010秒或大約46ms。 此階段再次是世界停止事件。
- 并發執行Sweep操作。 顧名思義,此階段也可以并發執行,而無需停止應用程序線程。
因此,我們從實際的垃圾收集日志中看到的是–代替了兩次Full GC操作,實際上只執行了一次Major GC Cleaning Old空間。
如果您在等待時間之后,那么根據jstat揭示的數據做出決策將使您朝著正確的決策方向邁進。 它正確地列出了兩個總計為50ms的世界停止事件,這些事件當時影響了所有活動線程的延遲。 但是,如果您嘗試針對吞吐量進行優化,那么您可能會被誤導-僅列出世界末日的初始標記和最終標記階段,而jstat輸出則完全隱藏了正在完成的并發工作。
結論
考慮到這種情況,最好甚至避免以次要,主要或完全GC的方式進行思考。 而是監視您的應用程序的延遲或吞吐量,并將GC事件鏈接到結果。 除了這些事件之外,您還需要了解特定的GC事件是強制所有應用程序線程停止還是是并發處理的事件的一部分。
如果您喜歡這些內容–這是我們的《垃圾收集手冊》中的示例章節。 整個手冊將于2015年3月發行。
翻譯自: https://www.javacodegeeks.com/2015/03/minor-gc-vs-major-gc-vs-full-gc.html
主要矛盾和次要矛盾
總結
以上是生活随笔為你收集整理的主要矛盾和次要矛盾_次要GC,主要GC与完整GC的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux zip解压缩命令(linux
- 下一篇: grunt 插件_从Grunt测试Gru