JVM垃圾收集器及GC日志相关

了解JVM的垃圾收集器对于性能分析和调优很有帮助,一般系统挂死或出现性能问题,通过分析GC日志,往往可以为我们提供有用的线索。

一、前言

  • 安全点

什么是安全点?

A point during program execution at which all GC roots are known and all heap object contents are consistent. From a global point of view, all threads must block at a safepoint before the GC can run.

Safepoint是java代码中一个线程可能暂停执行的一个位置,SafePoint保存了其他位置没有的一些运行信息。在这个位置上保存了线程上下文的任何信息,包括对象或者非对象的内部指针。在接收到JVM的进入Stop The World 的信息,在safepoint上,用户线程会被挂起。如果JNI代码想要访问在安全点上的用户线程,那么JNI所在线程也会阻塞直到用户线程离开安全点。因为在安全点上jvm线程信息被很好的描述,所以特别适合做一些全局性的操作,例如代码反优化,线程快照等等。(http://peg.hengtiansoft.com/article/jvm-zhong-de-safepoint/)

 

Safepoint是如何工作的?

 在HotSpot虚拟机中,safepoint 协议是主动协作的 。每一个用户线程在安全点 上都会检测一个标志位,来决定自己是否暂停执行。 对于JIT编译后的代码,JIT会在代码特定的位置(通常来说,在方法的返回处和counted loop的结束处)上插入安全点代码。对于解释执行的代码,JVM会设置一个2字节的dispatch tables,解释器执行的时候会经常去检查这个dispatch tables,当有safepoint请求的时候,就会让线程去进行safepoint检查。

 

Safepoint的相关总结---https://blog.csdn.net/u011918260/article/details/70047159

  • 一个线程可以在SafePoint上,也可以不在SafePoint上。一个线程在SafePoint时,它的状态可以安全地其他JVM线程所操作和观测;不在SafePoint时,就不能。 

  • 在SafePoint上不代表被阻塞(比如:JNI方法就可以在SafePoint上运行),但是被阻塞一定发生在SafePoint上。 

  • 当JVM决定达到一个全局的SafePoint(也叫做Stop the World),JVM里面所有的线程都要在SafePoint上并且不能离开,直到JVM让线程允许为止。这对要求所有线程都要被良好的描述的操作(比如CG,代码反优化等等)非常有好处。

  • 一些JVM可以持有一些私有的线程到SafePoint上而不需要全局的SafePoint,比如Zing.

  • 当你写一些非安全的代码的时候,你必须假设SafePoint有可能发生在任何两个字节码之间。

  • 非安全代码的调用并不要求必须有安全点,但是他们可以包含一个或者多个安全点。

  • 所有类型的JVM有一些效率非常高的技巧和去快速的穿过SafePoint,线程并不需要真正地进入SafePoint除非虚拟机指示线程这么做。

  • 所有的JNI方法都在SafePoint上执行。在安全点,JNI代码都不能改变和观测发起调用JNI代码线程的java机器状态。任何通过JNI API改变和观测调用线程的状态必须在调用线程离开安全点之后,以及再次进入SafePoint之前的时间内发生。

 

  • 垃圾回收算法(https://blog.csdn.net/hp910315/article/details/50937045)

1.标记-清除算法

(1)标记:使用可达性分析算法将无用的对象标记出来 

(2)将第一步标记的对象进行回收清除 

不足:

(1)效率问题:标记和清除的效率都不高 

(2)空间问题:清除后会产生大量不连续的内存碎片。 

JVM垃圾收集器及GC日志相关_第1张图片

 

2.复制算法 

思想:它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。

JVM垃圾收集器及GC日志相关_第2张图片

这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。 

3.标记-整理算法 

思想:在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。

JVM垃圾收集器及GC日志相关_第3张图片

4.分代收集算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。

JVM垃圾收集器及GC日志相关_第4张图片

内存被分为下面三个区域: 

(1)新生代:Eden、from survivor space、to survivor space。 

(2)老年代 

(3)永久代:方法区 

新生代:包含有Eden、from survivor space、to survivor space三个区,绝大多数最新被创建的对象会被分配到这里,大部分对象在创建之后会变得很快不可达,对象从这个对象消失的过程称为”minor GC” 

特征:(1)GC的发生相对比较频繁 

(2)GC的发生比较迅速高效,因为新生代的空间通常比较小,并且可能包含了许多短周期对象。 

(3)经历过几次新生代的垃圾回收之后,存活下来的对象会被拷贝到老年代的堆空间中。 

老年代:从新生代存活下来的对象会被拷贝到这里,它的空间比新生代要大,所以在老年代上发生的GC要比新生代少得多。对象从老年代消失的过程称为”major GC”或者”full GC”。 

特征:(1)比新生代的堆空间要大 

(2)内存占用的增长比较慢 

(3)GC操作不是很频繁,但是耗时比新生代中的GC要长。

JVM垃圾收集器及GC日志相关_第5张图片

持久代:也被称为方法区,用来存放类常量和字符串常量,这个区域不是用来存储那些从老年代存活下来的对象。它也会发生GC操作。

内存分配与回收的几条策略 

1)、对象优先在Eden上分配 

2)、大对象可以直接进入老年代 设置标签-XX:PretenureSizeThreshold = n 

3)、长期存活的对象将进入老年代 虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并且经过第一次Minor GC仍然存活,并且能够被Survivor空间容纳,进入移动到Survivor空间,并且设置对象年龄为1,对象在Survivor区每熬过一个Minor GC,年龄就增加1岁,当它的年龄到达一定的程度(默认为15岁),就会被移动到老年代,这个年龄阀值可以通过-XX:MaxTenuringThreshold设置。 

4)、动态对象年龄判断 虚拟机并不是永远要求对象的年龄达到MaxTenuringThreshold才移动到老年代,如果Survivor区中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或者等于该年龄的对象也被移动到老年代。 

5)、空间分配担保 在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

新生代GC的过程

(1)新对象会被分配到Eden空间中。 

(2)当Eden空间满时,就会触发minor GC过程。

(3)没有被引用的对象就会被垃圾收集 

(4)被引用的对象会被复制到S0 survivor区,并且age加1。

(5)当对象被移动到S0 survivor区,Eden空间会被清空。 

当minor GC再次发生 首先Eden区进行GC,然后S0 survivor区进行GC 

(6)最后进行GC操作之后被引用的对象变成from survivor空间。 

(7)GC后被引用的对象会被复制到to survicor空间,另外age加1。 

(8)复制完成之后,将Eden区和from survivor空间清空。 

当minor GC再次发生 首先Eden区进行GC,然后to survivor空间进行GC。 

(9)最后进行GC操作之后被引用的对象变成from survivor空间。 

(10)GC后被引用的对象会被复制到to survivor空间,另外age加1。 

(11)复制完成后将Eden区和from survivor空间清空。

 

当下一次GC发生 首先Eden区进行GC,然后from survicor空间进行GC。 重复(9)(10)(11)操作 

当存活的对象达到了一个age的阀值,最终就会复制到老年代。 

总结:(1)每次minor GC都会进行重复处理 

(2)当存活的对象达到了一个age的阀值时,他们会被复制到老年代。 

 

在新生代中,每次垃圾收集时发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集,而老年代中因为对象存活率高,没有额外的空间对它进行分配担保,就必须使用”标记-清理”或者”标记-整理”算法进行回收。

(以下二、三部分图文内容来自于:https://blog.csdn.net/tjiyu/article/details/53983650)

二、相关概念

1.HotSpot虚拟机所有收集器:

JVM垃圾收集器及GC日志相关_第6张图片

1).图中展示了7种不同分代的收集器:        

Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1; 

2).而它们所处区域,则表明其是属于新生代收集器还是老年代收集器:       

新生代收集器:Serial、ParNew、Parallel Scavenge;       

老年代收集器:Serial Old、Parallel Old、CMS;       

整堆收集器:G1; 

3).两个收集器间有连线,说明它们可以搭配使用(新生代和老年代搭配):        

Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old、G1; 

4).其中Serial Old作为CMS出现"Concurrent Mode Failure"失败的后备预案。

 

2.并发垃圾收集和并行垃圾收集的区别

1).并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;        

如ParNew、Parallel Scavenge、Parallel Old; 

2).并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行);       

用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上。     

如CMS、G1(也有并行)。

 

3.Minor GC和Full GC的区别

1).Minor GC:又称新生代GC,指发生在新生代的垃圾收集动作;        

因为Java对象大多是朝生夕灭,所以Minor GC非常频繁,一般回收速度也比较快; 

2).Full GC:又称Major GC或老年代GC,指发生在老年代的GC;出现Full GC经常会伴随至少一次的Minor GC(不是绝对,Parallel Sacvenge收集器就可以选择设置Major GC策略);       

Major GC速度一般比Minor GC慢10倍以上;

 

三、相关收集器

1.Serial收集器(新生代 – 串行GC)

最基本、最悠久的收集器,他是一个单线程的收集器,而单线程是指当他在进行垃圾收集的时候,必须暂停其他所有的工作线程,直到它收集结束,被称为”Stop The World”。

优点:简单而高效(与其他收集器的单线程相比),对于限于单个CPU的环境,Serial收集器没有线程交互的开销,所以能获得最高的单线程收集效率。

适用:是HotSpot在Client模式下默认的新生代收集器;运行在Client模式下的虚拟机。

设置参数:"-XX:+UseSerialGC"添加该参数来显式的使用串行垃圾收集器。

从Serial收集器->Parallel收集器->CMS(Concurrent Mark Sweep)->G1(Garbage First),用户线程停顿时间不断缩短,但仍然无法完全消除;

JVM垃圾收集器及GC日志相关_第7张图片

2.ParNew收集器(新生代 – 并行GC)

ParNew垃圾收集器是Serial收集器的多线程版本。除了使用多条线程进行垃圾收集之外,其余的和Serial所有控制参数一样。在配置为CMS GC的情况下默认的新生代收集器,

优点:在多CPU的环境下可以发挥更高而效率,并且是唯一一个可以和CMS收集器搭配工作的新生代并行GC。

适用:运行在server模式下的虚拟机首选的新生代收集器。因为除Serial外,目前只有它能与CMS收集器配合工作; 在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销。

设置参数:ParNew收集器是使用”-XX:+UseConcMarkSweepGC”选项后的默认新生代收集器;

可以用"-XX:+UseParNewGC”强制指定;

可以用"-XX:ParallelGCThreads”来限制垃圾收集的线程数,ParNew默认开启的收集线程与CPU的数量相同。

JVM垃圾收集器及GC日志相关_第8张图片

3.Parallel Scavenge收集器(新生代 – 并行回收GC)

看上去和ParNew没什么区别(新生代收集器,采用复制算法,多线程收集),Parallel Scavenge收集器的特点是它的关注点在于达到一个可控制的CPU吞吐量,吞吐量=运行代码时间/(运行代码时间+垃圾收集时间)。较高的吞吐量可以最好的利用CPU的效率,即减少垃圾收集时间,让用户代码获得更长的运行时间。

优点:被称为”吞吐量优先”收集器,有一个自适应调节参数(-XX:+UseAdaptiveSizePolicy),当这个参数打开后,无需手动指定新生代大小(-Xmn)、Eden和Survivor比例(-XX:SurvivorRatio)、晋升老年代年龄限制(-XX:PretenureSizeThreshold)等细节参数,虚拟机会动态调节这些参数来提供最适合的停顿时间或最大吞吐量。适用:本身是Server级别多CPU机器上的默认GC方式,也可以通过-XX:+UseParallelGC来指定,并且可以采用-XX:ParallelGCThread来指定线程数。

设置参数:“-XX:MaxGCPauseMillis”配置最大垃圾收集停顿时间,“-XX:GCTimeRatio”配置吞吐量大小(值大于0小于100),即垃圾收集时间占总时间的比率。计算方法为:1/(1+n),默认值为99,即允许最大1%(1/(1+99))的垃圾收集时间。

其他参数:"-XX:+UseAdptiveSizePolicy”,开启这个参数后,不用手工指定一些细节参数,如:新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等;JVM会根据当前系统运行情况收集性能监控信息,动态调整这些参数,以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomiscs)。

这是一种值得推荐的方式: (1)、只需设置好内存数据大小(如"-Xmx"设置最大堆);  (2)、然后使用"-XX:MaxGCPauseMillis"或"-XX:GCTimeRatio"给JVM设置一个优化目标; (3)、那些具体细节参数的调节就由JVM自适应完成;

 这也是Parallel Scavenge收集器与ParNew收集器一个重要区别。

 

4.Serial Old收集器(老年代 – 串行GC)

Serial Old是Serial收集器的老年代版本,同样是一个单线程收集器,使用”标记-整理”算法。

适用:Client模式下虚拟机使用;在Server模式有两大用途:在JDK 1.5及以前的版本中与Parallel Scavenge收集器搭配使用;另一种用途作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

JVM垃圾收集器及GC日志相关_第9张图片

 

5.Parallel Old收集器(老年代 – 并行GC)

Parallel Old是Parallel Scavenge收集器的老年代版本,为了配合Parallel Scavenge的面向吞吐量的特性而开发的对应组合。JDK1.6中开始提供。采用“标记-整理”算法。

特点:多线程收集。

适用:在注重吞吐量以及CPU资源敏感的场合,可以优先考虑Parallel Scavenge加Parallel Old收集器。

设置参数:"-XX:+UseParallelOldGC”来指定使用Parallel Old收集器。

Parallel Old收集器工作过程如下图:

JVM垃圾收集器及GC日志相关_第10张图片

6.CMS收集器(老年代 – 并发GC)

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。基于”标记-清除”算法,整个过程分为4个步骤:初始标记(CMS initial mark)---需要STW、并发标记(CMS concurrent mark)、重新标记(CMS remark)---需要STW、并发清除(CMS concurrent sweep)。

其中初始标记、重新标记仍然是”Stop The World”,初始标记仅仅是标记一下GC Roots能直接关联的对象,并发标记进行GC Roots Tracing的过程,重新标记为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那部分对象,这个阶段停顿时间比初始标记阶段稍长一些,但比并发标记时间短。

优点:并发收集、低停顿。是HotSpot在JDK1.5推出的第一款真正意义上的并发收集器。第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

适用:重视服务的响应速度、系统停顿时间和用户体验的互联网网站或者B/S系统。

设置参数:"-XX:+UseConcMarkSweepGC":指定使用CMS收集器; 

JVM垃圾收集器及GC日志相关_第11张图片

CMS收集器3个明显的缺点:

1)对CPU资源非常敏感

2)无法处理浮动垃圾,可能出现"Concurrent Mode Failure”失败 

3)产生大量内存碎片

总体来看,与Parallel Old垃圾收集器相比,CMS减少了执行老年代垃圾收集时应用暂停的时间;但却增加了新生代垃圾收集时应用暂停的时间、降低了吞吐量而且需要占用更大的堆空间。

7.G1收集器

G1(Garbage First)收集器是当前收集器技术最前沿成果,与之前的CMS相比有两个显著改进:基于”标记-整理”算法实现收集器和精确控制停顿。能够在基本不牺牲吞吐量的前提下完成低停顿的内存回收。

特点:1)并行与并发:能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(或CPU核心)来缩短"Stop The World”停顿的时间;可以通过并发的方式让垃圾收集与Java程序同时进行; 

2)分代收集:收集范围包括新生代和老年代,能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;可以采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。3)空间整合:从整体看,G1是基于标记-整理算法,从局部(两个Region间)看,是基于复制算法, 这两种算法都不会产生内存碎片,收集后能提供规整的可用内存,有利于长时间运行。

4)可预测的停顿:低停顿的同时实现高吞吐量,G1除了追求低停顿处,还能建立可预测的停顿时间模型;可以明确指定M毫秒时间片内,垃圾收集消耗的时间不超过N毫秒;

应用场景:面向服务端应用,针对具有大内存、多处理器的机器;最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案;如:在堆大小约6GB或更大时,可预测的暂停时间可以低于0.5秒;用来替换掉JDK1.5中的CMS收集器;       

在下面的情况时,使用G1可能比CMS好:1).超过50%的Java堆被活动数据占用;2).对象分配频率或年代提升频率变化很大;3).GC停顿时间过长(长于0.5至1秒)。

设置参数:"-XX:+UseG1GC"指定使用G1收集器;       

"-XX:InitiatingHeapOccupancyPercent"当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45;       "-XX:MaxGCPauseMillis"为G1设置暂停时间目标,默认值为200毫秒;"-XX:G1HeapRegionSize"设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region。

JVM垃圾收集器及GC日志相关_第12张图片

 

四、补充GC日志相关内容

(内容主要来自于:https://www.cnblogs.com/mikevictor07/p/5024645.html?utm_source=tuicool&utm_medium=referral)

监控GC在于判断JVM是否在良好高效地工作并且是否需要投入性能调优(主要包括应用程序优化与JVM参数优化),关注的数据大概有:

    1. Minor GC(新生代GC)频率、持续时间以及回收内存量。

    2. Major GC(老年代GC)频率、持续时间、回收内存量以及 STW 耗时。

    3. Heap 对象分配(导出.hprof文件分析,通常比较大)

加入-XX:+PrintGCDetails 参数则可以打印详细GC信息至控制台。参数-verbose:gc也是可以,但不够详细。

通过加入 -XX:+PrintGCDateStamps则可以记录GC发生的详细时间。

通过加入 -Xloggc:/home/XX/gc/app_gc.log 可以把GC输出至文件,这对长时间服务器GC监控很有帮助。以下列出一些参数大致打印的信息如下:

1)-verbose:gc: [GC 72104K->9650K(317952K), 0.0130635 secs]

2)-XX:+PrintGCDetails: [GC [PSYoungGen: 142826K->10751K(274944K)] 162800K->54759K(450048K), 0.0609952 secs] [Times: user=0.13 sys=0.02, real=0.06 secs]

3)-XX:+PrintGCDetails 加上-XX:+PrintGCDateStamps 参数则打印如下:

2015-12-06T12:32:02.890+0800: [GC [PSYoungGen: 142833K->10728K(142848K)] 166113K->59145K(317952K), 0.0792023 secs] [Times: user=0.22 sys=0.00, real=0.08 secs]

可以看出,如果是想监控详细信息与GC发生时间,加上-XX:+PrintGCDateStamps -XX:+PrintGCDetails 参数会是一个比较好的选择。

 

首先来说明一段在各个GC中通用的字段含义说明:

1)142826K->10751K(274944K) 分别代表回收前、回收后以及总内存大小。

2)Times: user=0.46 sys=0.05, real=0.07 secs:  user代表GC 需要的各个CPU总时间(各个CPU时间相加),sys代表回收器自身的行为所占用CPU时间,real则代表本次GC所耗费的真正耗时(在多核CPU中并行回收,它通常小于user) 。

 

1. Serial GC (-XX:+UseSerialGC)日志

 下面是一段的Serial GC日志含义依次分解:

[GC[DefNew: 78656K->8704K(78656K), 0.0487492 secs] 135584K->80553K(253440K), 0.0488309 secs] [Times: user=0.05 sys=0.00, real=0.05 secs] 

[Full GC[Tenured: 62546K->60809K(174784K), 0.1600120 secs] 85931K->60809K(253440K), [Perm : 38404K->38404K(65536K)], 0.1600814 secs] [Times: user=0.16 sys=0.00, real=0.16 secs]

---------------------------

1)其中的DefNew代表单线程回收yong generation。

2)紧跟后面的 78656K->8704K(78656K) 中的 78656K 代表young generation 回收前大小,8704K 代表回收后大小,括号中的78656K 代表young generation总大小(包含2个survivor)。

3)135584K->80553K(253440K) 则代表整个Heap(young+old)的变化与总量,含义参照前面所述(Perm也一样)。

4)Full GC 即代表 major GC, Tenured: 62546K->60809K(174784K)则表示 old generation变化及总量

 

2.Parallel GC 通过加入参数 -XX:+UseParallelGC 来指定

[GC [PSYoungGen: 88524K->10728K(274944K)] 133505K->61187K(450048K), 0.0374438 secs] [Times: user=0.08 sys=0.00, real=0.04 secs]

[Full GC [PSYoungGen: 10728K->0K(274944K)] [ParOldGen: 50459K->50210K(175104K)] 61187K->50210K(450048K) [PSPermGen: 38656K->38643K(77312K)], 0.3787131 secs] [Times: user=0.98 sys=0.02, real=0.38 secs]

--------------------------

1). PSYoungGen 代表并行回收 young generation

2). ParOldGen 代表并行回收 old generation.

3). PSPermGen 代表并行回收 Permanent generation. 其他的参数与前面解释的类似。

 

3.CMS GC

    CMS GC相对来说比较复杂,通过使用 -XX:+UseConcMarkSweepGC 参数在指定,但是一般情况需要更多的其他参数来保证它能比较好地达到我们的低延时目的,下面就部分常用参数做介绍:

-XX:+CMSIncrementalMode     采用增量式的标记方式,减少标记时应用停顿时间

-XX:+CMSParallelRemarkEnabled     启用并行标记

-XX:CMSInitiatingOccupancyFraction=70     Old generation消耗比例达到多少时进行回收,通常配置60-80之间

-XX:CMSFullGCsBeforeCompaction=1     多少次Full GC 后压缩old generation一次

-XX:+UseCMSInitiatingOccupancyOnly  

-XX:+ScavengeBeforeFullGC             Old generation GC前对young generation GC一次,默认开启。

-XX:+CMSScavengeBeforeRemark     CMS remark之前进行一次young generation GC

下面使用一段LOG 作为 CMS GC行为:

--------------------------------

[GC [ParNew: 5033216K->629120K(5662336K), 19.9680140 secs] 5033216K->2091720K(11953792K), 19.9682480 secs] [Times: user=119.82 sys=14.07, real=19.97 secs]

[GC [1 CMS-initial-mark: 1462600K(6291456K)] 4404491K(11953792K), 3.6824630 secs] [Times: user=3.67 sys=0.01, real=3.69 secs] 

[CMS-concurrent-mark-start]

[[ParNew: 5662336K->629120K(5662336K), 6.8885140 secs] 7124936K->4366353K(11953792K), 6.8886670 secs] [Times: user=136.94 sys=0.92, real=6.89 secs] 

[CMS-concurrent-mark: 2.053/75.039 secs] [Times: user=192.12 sys=3.19, real=75.04 secs] 

[CMS-concurrent-preclean-start]

[CMS-concurrent-preclean: 6.159/6.274 secs] [Times: user=7.53 sys=0.30, real=6.28 secs] 

[CMS-concurrent-abortable-preclean-start]

[CMS-concurrent-abortable-preclean: 10.680/10.681 secs] [Times: user=12.77 sys=0.43, real=10.68 secs] 

[GC[YG occupancy: 3043648 K (5662336 K)] [GC [ParNew: 3043648K->629120K(5662336K), 1.5345480 secs] 6780882K->4985422K(11953792K), 1.5346490 secs] [Times: user=30.72 sys=0.27, real=1.53 secs] 

[Rescan (parallel) , 0.1900960 secs] [weak refs processing, 0.0000460 secs] [scrub string table, 0.0008680 secs] [1 CMS-remark: 4356302K(6291456K)] 4985422K(11953792K), 1.7259240 secs] [Times: user=34.88 sys=0.27, real=1.72 secs] 

[CMS-concurrent-sweep-start]

[CMS-concurrent-sweep: 3.379/3.380 secs] [Times: user=4.05 sys=0.13, real=3.38 secs]

[CMS-concurrent-reset-start]

[CMS-concurrent-reset: 0.030/0.030 secs] [Times: user=0.02 sys=0.01, real=0.03 secs]

 

GC 分析主要点在于:

1. Minor & Major GC 情况

2. Heap 对象分配

注: jstack 命令可以查询当前应用线程状态,可用于判断是否存在死锁、线程等待原因等问题。

GC查看工具:GCViewer 

    该工具用于统计加上参数如-XX:+PrintGCDetails -Xloggc:/home/xx.log 形成的LOG文件,从服务器上拿到文件后启动GCViewer打开

你可能感兴趣的:(Java相关)