`
leichenlei
  • 浏览: 128673 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

jvm垃圾回收——诊断垃圾收集问题

 
阅读更多
转载请注明出处,多谢。

这个附录包含说明垃圾收集问题的日志的例子。如不指定,使用默认收集器。除非另有说明,日志依据下面生成:

-verbosegc -XX:+ PrintGCDetails

这个输出格式用于1.4.3。这个格式在以后的版本不保证不变。尤其和-XX:+ PrintGCDetail相关的的输出,几乎不保证不变,这些输出代表了JVM开发者的当前需要。

这些选项的次要收集输出:

[GC [<collector>: <starting occupancy1> -> <ending occupancy1>, <pause time1> secs] <starting occupancy3> -> <ending occupancy3>, <pause time3> secs]

其中:

<collector>                   次要收集使用的收集器的内部名字。

<starting occupancy1>       收集之前年轻代的占用。(译者:1个伊甸园+1个生还者)

<ending occupancy1> 收集之后年轻代的占用。

<pause time1>           次要gc的暂停时间的秒数。

<starting occupancy3>       收集之前整个堆占用(1个伊甸园+1个生还者+1个老年代)。

<ending occupancy3> 收集之后整个堆占用。

<pause time3>            整个堆收集的暂停时间。包括完成主GC时间。

 

在这,下面的日志片段是列出的收集信息的例子。如果陈述是关于一个量,像暂停时间、引用的数量是收集的数据集的总汇的平均值,不只来自于提供的日志片段。如果给了范围值,这个范围来自提供的日志片段。

1 年轻代太小

第一个例子的堆大小是32M,年轻代的大小是4M

[GC [DefNew: 4032K->64K(4032K), 0.0429742 secs] 9350K->7748K(32704K), 0.0431096 secs]

[GC [DefNew: 4032K->64K(4032K), 0.0403446 secs] 11716K->10121K(32704K), 0.0404867 secs]

[GC [DefNew: 4032K->64K(4032K), 0.0443969 secs] 14089K->12562K(32704K), 0.0445251 secs]

       这个输出从垃圾时间消耗的角度看,是合理的。但是注意,年轻代的占用减少(第一行,从4032k64k,共3968k),整个堆的占用减少的更少(从9350k7748k,共1602k)。这说明年轻代40%是垃圾,其余的对象在收集中生还,被提升到老年代。

1.1 收集之后增加年轻代大小,以增加可用空间

年轻代被增加到8M

[GC [DefNew: 8128K->64K(8128K), 0.0453670 secs] 13000K->7427K(32704K), 0.0454906 secs]

[GC [DefNew: 8128K->64K(8128K), 0.0388632 secs] 15491K->9663K(32704K), 0.0390013 secs]

[GC [DefNew: 8128K->64K(8128K), 0.0388610 secs] 17727K->11829K(32704K), 0.0389919 secs]

8M的年轻代,在次GC中大多数是垃圾。在第一行,年轻代从8128k64k减少了8064k,整个堆从13000k7427k减少5573k,意味着年轻代的68%是垃圾。正如所料,年轻代的大小没有改变次要GC生还的活对象的数量(大于2.4M),但是减少了次GC次数和次GC暂停时间(每行最后的时间)。

1.2 应用时间和停止时间

       为了对比4M年轻代和8M年轻代的消耗,下面是附带命令行-XX:+ PrintGCApplicationConcurrentTime-XX:+ PrintGCApplicationStoppedTime的输出,它测量了收集之间的时间和收集时间。4M年轻代情况下,应用运行时间.53.91秒,次要GC暂停时间.045.047秒。大约5%8%的次GC开销。

Application time: 0.5291524 seconds

[GC [DefNew: 3968K->64K(4032K), 0.0460948 secs] 7451K->6186K(32704K), 0.0462350 secs]

Total time for which application threads were stopped: 0.0468229 seconds

Application time: 0.5279058 seconds

[GC [DefNew: 4032K->64K(4032K), 0.0447854 secs] 10154K->8648K(32704K), 0.0449156 secs]

Total time for which application threads were stopped: 0.0453124 seconds

Application time: 0.9063706 seconds

[GC [DefNew: 4032K->64K(4032K), 0.0464574 secs] 12616K->11187K(32704K), 0.0465921 secs]

Total time for which application threads were stopped: 0.0470484 seconds

8M年轻GC

Application time: 1.3874623 seconds

[GC [DefNew: 8064K->63K(8128K), 0.0509215 secs] 11106K->5994K(32704K), 0.0510972 secs]

Total time for which application threads were stopped: 0.0517092 seconds

Application time: 1.5225065 seconds

[GC [DefNew: 8127K->63K(8128K), 0.0432982 secs] 14058K->8273K(32704K), 0.0434172 secs]

Total time for which application threads were stopped: 0.0440447 seconds

Application time: 1.4263524 seconds

[GC [DefNew: 8127K->64K(8128K), 0.0363538 secs] 16337K->10381K(32704K), 0.0364811 secs]

Total time for which application threads were stopped: 0.0369103 seconds

       应用运行1.381.52秒之间,收集暂停.036.051秒。大约2.63.6%的次GC开销。

2 年轻代太大

年轻代从4M8M,次GC的开销减少一半。如果年轻代增加到16M会怎样?

[GC [DefNew: 16000K->16000K(16192K), 0.0000574 secs][Tenured: 2973K->2704K(16384K), 0.1012650 secs] 18973K->2704K(32576K), 0.1015066 secs]

[GC [DefNew: 16000K->16000K(16192K), 0.0000518 secs][Tenured: 2704K->2535K(16384K), 0.0931034 secs] 18704K->2535K(32576K), 0.0933519 secs]

[GC [DefNew: 16000K->16000K(16192K), 0.0000498 secs][Tenured: 2535K->2319K(16384K), 0.0860925 secs] 18535K->2319K(32576K), 0.0863350 secs]

这个例子,年轻代相对老年代太大,而不允许年轻代提升被担保(年轻代大约是老年代的一半)。年轻代不能被成功收集,只有主GC发生。注意,在这,年轻代被收集了,但是只是更昂贵的主GC的一部分。

3 老年代太大或者太小?

8M的年轻代,整个堆32M,主GC暂停大约.13:

[GC [DefNew: 8128K->8128K(8128K), 0.0000558 secs][Tenured: 17746K->2309K(24576K), 0.1247669 secs] 25874K->2309K(32704K), 0.1250098 secs]

如果堆大小增加到64M,主GC暂停大于.21:

[GC [DefNew: 8128K->8128K(8128K), 0.0000369 secs][Tenured: 50059K->5338K(57344K), 0.2218912 secs] 58187K->5338K(65472K), 0.2221317 secs]

但是主GC频率下降一半。通过添加-XX:+ PrintGCTimeStamps看收集输出的时间戳。32M堆的主GC大约10-11秒一次(只显示主GC:

111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs]

122.463: [GC 122.463: [DefNew: 8128K->8128K(8128K), 0.0000560 secs]122.463: [Tenured: 18630K->2366K(24576K), 0.1322560 secs] 26758K->2366K(32704K), 0.1325284 secs]

133.896: [GC 133.897: [DefNew: 8128K->8128K(8128K), 0.0000443 secs]133.897: [Tenured: 18240K->2573K(24576K), 0.1340199 secs] 26368K->2573K(32704K), 0.1343218 secs]

144.112: [GC 144.112: [DefNew: 8128K->8128K(8128K), 0.0000544 secs]144.112: [Tenured: 16564K->2304K(24576K), 0.1246831 secs] 24692K->2304K(32704K), 0.1249602 secs]

64M堆的主GC大约30秒一次:

90.597: [GC 90.597: [DefNew: 8128K->8128K(8128K), 0.0000542 secs]90.597: [Tenured: 49841K->5141K(57344K), 0.2129882 secs] 57969K->5141K(65472K), 0.2133274 secs]

120.899: [GC 120.899: [DefNew: 8128K->8128K(8128K), 0.0000550 secs]120.899: [Tenured: 50384K->2430K(57344K), 0.2216590 secs] 58512K->2430K(65472K), 0.2219384 secs]

153.968: [GC 153.968: [DefNew: 8128K->8128K(8128K), 0.0000511 secs]153.968: [Tenured: 51164K->2309K(57344K), 0.2193906 secs] 59292K->2309K(65472K), 0.2196372 secs]

32M还是64M更好呢?吞吐量64M更好,32M的暂停时间更小。

3.1 更大的年轻代

为了追求更高吞吐量,考虑更大的堆。256M64M年轻代,相对于32M8M年轻代,次GC暂停多了10%;说明了从收集中生还的对象数大致相同。主GC多了3.8秒,说明了堆更大了。

[GC [DefNew: 64575K->959K(64576K), 0.0457646 secs] 196016K->133633K(261184K), 0.0459067 secs]

[GC [DefNew: 64575K->64575K(64576K), 0.0000573 secs][Tenured: 132673K->5437K(196608K), 0.4959855 secs] 197249K->5437K(261184K), 0.4962533 secs]

[GC [DefNew: 63616K->959K(64576K), 0.0360258 secs] 69053K->7600K(261184K), 0.0361663 secs]

4 调节之后,次GC暂停太长

4.1 尝试平行年轻代收集器

如果次GC暂停太长,尝试使用平行年轻代收集器。加上-XX:+UseParallelGC产生下面输出。在这个例子,为了简单,用-XX:- UseAdaptiveSizing关闭自适应调整大小。为了和上面对比也加了时间戳选项。

500.285: [GC 51526K->2678K(253952K), 0.0120328 secs]

506.734: [GC 51830K->2646K(253952K), 0.0117832 secs]

513.068: [GC 51798K->2742K(253952K), 0.0124632 secs]

519.566: [GC 51894K->2550K(253952K), 0.0122923 secs]

这里,平均次GC暂停大约0.15秒,减少了68%。关闭了自适应调整大小,因为加上之后,下面一组输出中会出现不寻常行为:

[GC 62752K->2992K(259328K), 0.0126936 secs]

[GC 62896K->60192K(259328K), 0.0127579 secs]

[GC 63008K->3120K(259328K), 0.0123150 secs]

[GC 63024K->60256K(259328K), 0.0120565 secs]

[GC 63072K->3024K(259328K), 0.0127215 secs]

[GC 62928K->60208K(259328K), 0.0113090 secs]

[GC 63024K->3136K(259328K), 0.0133799 secs]

[GC 63040K->60256K(259328K), 0.0135459 secs]

自适应调整生还者区大小。上面的日志展示的次要GC,生还者区的大小在来回变化,引起最小年轻代收集或者完整年轻代收集。虽然使用自适应调整的行为奇怪,但是仍然产生了高的吞吐量。

4.2 或者,使用-XX:+UseParNewGC收益

497.905: [GC 497.905: [ ParNew: 64576K->960K(64576K), 0.0255372 secs] 155310K->93003K(261184K), 0.0256767 secs]

506.305: [GC 506.305: [ParNew: 64576K->960K(64576K), 0.0276291 secs] 156619K->94267K(261184K), 0.0277958 secs]

514.565: [GC 514.565: [ParNew: 64576K->960K(64576K), 0.0261376 secs] 157883K->95711K(261184K), 0.0262927 secs]

522.838: [GC 522.838: [ParNew: 64576K->960K(64576K), 0.0316625 secs] 159327K->97331K(261184K), 0.0318099 secs]

显示了,次GC暂停大约减少44%。时间戳显示次GC执行周期。使用了-XX:+UseParallelGC之后,大约6.5秒执行一次,每次执行.015秒(次要GC开销大约0.33%)。使用-XX:+UseParNewGC之后,大约8秒一次,每次执行0.026秒(次要GC开销大约0.23%)。

5 调整之后,主GC太长

5.1 尝试并发低暂停收集器

GC暂停在256M堆、64M年轻代下大约0.489秒。如果太长,尝试添加-XX:+UseConcMarkSweepGC(同时也设置-XX:+UseParNewGC)。

[GC [ParNew: 64576K->960K(64576K), 0.0377639 secs] 140122K->78078K(261184K), 0.0379598 secs]

[GC [ParNew: 64576K->960K(64576K), 0.0329313 secs] 141694K->79533K(261184K), 0.0331324 secs]

[GC [ParNew: 64576K->960K(64576K), 0.0413880 secs] 143149K->81128K(261184K), 0.0416101 secs]

[GC [1 CMS-initial-mark: 80168K(196608K)] 81144K(261184K), 0.0059036 secs]

[CMS-concurrent-mark: 0.129/0.129 secs]

[CMS-concurrent-preclean: 0.007/0.007 secs]

[GC[ Rescan (non-parallel) [ grey object rescan, 0.0020879 secs][root rescan, 0.0144199 secs], 0.0166258 secs][weak refs processing, 0.0000411 secs] [1 CMS-remark:80168K(196608K)] 82493K(261184K), 0.0168943 secs]

[CMS-concurrent-sweep: 1.208/1.208 secs]

[CMS-concurrent-reset: 0.036/0.036 secs]

[GC [ParNew: 64576K->960K(64576K), 0.0311520 secs] 66308K->4171K(261184K), 0.0313513 secs]

[GC [ParNew: 64576K->960K(64576K), 0.0348341 secs] 67787K->5695K(261184K), 0.0350776 secs]

[GC [ParNew: 64576K->960K(64576K), 0.0359806 secs] 69311K->7154K(261184K), 0.0362064 secs]

用于例子的应用,次GC执行间相对并发收集执行时间隔时间短。这个不是典型情况。更经常的情况,并发收集之间会有次要收集。注意,并发阶段可能比较长(例如,CMS-concurrent-sweep 1.2秒)但是在并发阶段,应用并没有暂停,所以应用不会出现暂停。相反,虽然并发阶段应用没有暂停,但应用并没有使用全部处理器(其中一个被垃圾收集线程使用)。暂停来自于CMS-initial-markCMS-remark pause。在这个输出片段,较大的那些是017秒。CMS-remark pause的平均运行时间(最长的暂停)是0.019秒。主GC的最大暂停(和默认比较)从0.489秒减少到0.019秒(减少了96%)。也要注意,次GC暂停增加到0.035秒,大于只使用-XX:+UseParNewGC0.026秒。这暗示了并发收集和主GC有关。

5.2 尝试增量地暂停收集器

GC时间还可以通过增量低暂停收集器来减少。使用命令-Xincgc

[GC [DefNew: 64575K->959K(64576K), 0.0616204 secs][Train: 5537K->5474K(196608K), 0.0108803 secs] 68664K->6434K(261184K), 0.0730248 secs]

[GC [DefNew: 64575K->959K(64576K), 0.0559078 secs][Train: 6813K->6749K(196608K), 0.0115077 secs] 70050K->7709K(261184K), 0.0680289 secs]

[GC [DefNew: 64575K->959K(64576K), 0.0607615 secs][Train: 8178K->8114K(196608K), 0.0112705 secs] 71325K->9074K(261184K), 0.0727902 secs]

在执行每个次GC时,部分老年代被收集,没有单独的主GC暂停。次GC暂停时间增加到0.078秒。如果老年代增量收集,在老年代满之前不能完成,触发主GC,在日志中用MSC标示。

[GC [DefNew: 64575K->64575K(64576K), 0.0005311 secs][Train MSC: 68226K->135K(196608K), 0.4347857 secs] 132802K->2565K(261184K), 0.4397831 secs]

 

 

草稿版本:2003-2-20

分享到:
评论

相关推荐

    实战Java虚拟机——JVM故障诊断与性能优化 pdf

    《实战Java虚拟机——JVM故障诊断与性能优化》内容简介:随着越来越多的第三方语言(Groovy、Scala、JRuby等)在Java虚拟机上运行,Java也俨然成为一个充满活力的生态圈。本书将通过200余示例详细介绍Java虚拟机中的...

    实战Java虚拟机——JVM故障诊断与性能优化

    了解JVM内部机制,包括类加载机制、内存模型、垃圾收集等,是提高程序性能和解决运行时问题的关键。书中的内容涵盖了以下几个核心知识点: 1. **JVM结构**:JVM分为多个组成部分,包括类装载器、运行时数据区、执行...

    实战JAVA虚拟机 JVM故障诊断与性能优化

    《实战JAVA虚拟机 JVM故障诊断与性能优化》这本书主要涵盖了Java开发者在实际工作中可能遇到的JVM相关问题,包括但不限于故障排查、性能调优、内存管理、垃圾收集机制等内容。以下将详细介绍这些知识点: 1. **Java...

    实战JAVA虚拟机++JVM故障诊断与性能优化.pdf

    第4~5章介绍了垃圾回收的算法和各种垃圾回收器。第6章介绍了Java虚拟机的性能监控和故障诊断工具。第7章详细介绍了对Java堆的分析方法和案例。第8章介绍了Java虚拟机对多线程,尤其是对锁的支持。第9~10章介绍了Java...

    Java虚拟机-jvm故障诊断与性能优化-源码

    通过学习这些知识点,并结合《实战Java虚拟机——JVM故障诊断与性能优化》的源码,开发者能够更深入地理解JVM的工作机制,从而更好地优化Java应用的性能,处理各种运行时问题。实践中,不断尝试、调整和学习,才能...

    Java中内存泄露及垃圾回收机制宣贯.pdf

    Java的垃圾回收机制是由虚拟机(JVM)的一部分——垃圾回收器(Garbage Collector)来执行的。与C++等语言不同,Java程序员不需要显式地释放内存,这大大减少了内存泄漏的可能性。 垃圾回收器通过一系列算法来判断...

    JVM性能调优-JVM内存整理及GC回收.pdf_java_jvm_

    《JVM性能调优——JVM内存整理及GC回收》是针对Java开发人员的重要主题,尤其是在大型企业级应用中,确保JVM(Java虚拟机)的高效运行是至关重要的。本资料深入探讨了如何通过调整JVM内存设置和优化垃圾回收机制来...

    JVM基础系列

    学习JVM内存模型可以帮助开发者理解内存分配策略,从而更好地诊断和解决问题。 - **垃圾回收机制**:理解不同垃圾回收器的特点及其选择依据,可以显著提高线上环境下的系统稳定性。通过调整垃圾回收参数,可以优化GC...

    深入JVM内核-原理、诊断与优化ppt.zip

    《深入JVM内核——原理、诊断与优化》是一份深度探讨Java虚拟机核心机制、问题诊断和性能优化的专业资料。这份资料涵盖了JVM的各个方面,对于Java开发者来说,理解和掌握这些知识至关重要。 首先,我们要了解JVM...

    深入JVM内核—原理、诊断与优化视频教程-4.GC算法与种类

    本教程——“深入JVM内核—原理、诊断与优化视频教程”着重讲解了JVM的内部机制,特别是关于垃圾收集(Garbage Collection, GC)的算法和种类,这对于理解和提升Java应用性能至关重要。 一、JVM内存模型 首先,...

    Qcon2011杭州-莫枢(RednaxelaFX)演讲-JVM@Taobao

    总结来说,莫枢在Qcon2011杭州的演讲揭示了淘宝在JVM优化上的深度实践,包括但不限于内存管理、类加载、垃圾回收、性能监控等方面。这些经验对于任何使用Java技术栈的大型企业来说都具有极高的参考价值,提醒我们...

    ImagesForJVM-JVM笔记图片

    【描述】"ImagesForJVM——JVM笔记图片" 暗示这些图片可能是教学或学习笔记的一部分,旨在通过视觉化的方式解释JVM的关键概念,如内存模型、类加载机制、垃圾收集以及性能优化等方面。 【标签】"java" 明确了这些...

    jvm调优

    《JVM调优详解——基于源码与工具的探索》 在Java开发中,JVM(Java Virtual Machine)调优是提升系统性能的关键环节。它涉及到内存管理、线程调度、垃圾回收等多个方面,理解并掌握JVM调优技巧,能有效避免性能...

    jvm内存管理,pdf

    通过以上内容可以看出,深入理解JVM内存管理和垃圾收集机制对于提高应用程序的性能至关重要。尽管现代JVM已经非常智能,能够自动管理大部分内存相关的工作,但在面对复杂的应用场景时,仍然需要开发人员具备一定的...

    java虚拟机源码-JVMbookSource:实战Java虚拟机———JVM故障诊断与性能优化(第2版)源码.rar

    《实战Java虚拟机——JVM故障诊断与性能优化(第2版)》是Java开发者深入理解JVM工作原理、诊断问题以及进行性能调优的重要参考资料。该书籍的源码提供了丰富的示例和实践案例,帮助读者更好地掌握Java虚拟机的内部...

    JAVA高级知识,JVM篇

    ### JAVA高级知识——JVM篇 #### 一、JVM内存模型 JVM(Java Virtual Machine,Java虚拟机)作为Java程序的运行环境,其内存管理机制是理解JVM的关键。JVM内存主要分为以下几个部分: 1. **程序计数器(Program ...

Global site tag (gtag.js) - Google Analytics