JVM汇总--jvm调优-命令篇/实战

GC的最根本原因:垃圾收集器的工作就是清除Java创建的对象,垃圾收集器需要清理的对象数量以及要执行的GC数量均取决于已创建的对象数量。因此,为了使你的系统在GC上表现良好,首先需要减少创建对象的数量。

GC优化的两个目的:

  1. 将进入老年代的对象数量降到最低

  2. 减少Full GC的执行时间

原则:

将进入老年代的对象数量降到最低

    设置内存大小,设置新老代比例,设置对象在新代的存活周期

降低Full GC的时间

    找到最合理的老年代内存大小,

    设置 CMS-Remark之前强制进行年轻代的GC,以降低remark时扫描的内存大小,降低STW的时间

  • 如果通过减小老年代内存来减少Full GC时间,可能会引起 OutOfMemoryError或者导致Full GC的频率升高。

  • 另外,如果通过增加老年代内存来降低Full GC的频率,Full GC的时间可能因此增加。

 

影响GC性能的参数(最常用)

    设置好几个参数并不会提升GC执行的速度,反而会使它变得更慢。

    GC类型的选择

    -Xms-Xmx 设置堆内存大小    

    NewRatio:新生代和老年代的内存比,值设置得越大,则老年代空间越大,新生代空间越小。

                可能会认为把 NewRatio设为1会是最好的选择,然而事实并非如此,根据笔者的经验,当 NewRatio设为2或3时,整个GC的状态表现得更好。

                    /(-XX:NewSize 新生代大小)

    -XX:SurvivorRatio  Eden区和Survivor区的内存比

命令简介

jstat -gc  pid  2000 20 (垃圾回收堆的行为统计,每隔2000ms输出一次,一共输出20次)

  • S0C : survivor0区的总容量

  • S1C : survivor1区的总容量

  • S0U : survivor0区已使用的容量

  • S1C : survivor1区已使用的容量

  • EC : Eden区的总容量

  • EU : Eden区已使用的容量

  • OC : Old区的总容量

  • OU : Old区已使用的容量

  • PC 当前perm的容量 (KB)

  • PU perm的使用 (KB)

  • YGC : 新生代垃圾回收次数

  • YGCT : 新生代垃圾回收时间

  • FGC : 老年代垃圾回收次数

  • FGCT : 老年代垃圾回收时间

  • GCT : 垃圾回收总消耗时间

-gccapacity

同-gc,不过还会输出Java堆各区域使用到的最大、最小空间

  • NGCMN : 新生代占用的最小空间

  • NGCMX : 新生代占用的最大空间

  • OGCMN : 老年代占用的最小空间

  • OGCMX : 老年代占用的最大空间

  • OGC:当前年老代的容量 (KB)

  • OC:当前年老代的空间 (KB)

  • PGCMN : perm占用的最小空间

  • PGCMX : perm占用的最大空间

-gcutil

同-gc,不过输出的是已使用空间占总空间的百分比

-gccause

垃圾收集统计概述(同-gcutil),附加最近两次垃圾回收事件的原因

  • LGCC:最近垃圾回收的原因

  • GCC:当前垃圾回收的原因

jmap(JVM Memory Map)命令用于生成heap dump文件,如果不使用这个命令,还可以使用-XX:+HeapDumpOnOutOfMemoryError参数来让虚拟机出现OOM的时候·自动生成dump文件。 jmap不仅能生成dump文件,还阔以查询finalize执行队列、Java堆和永久代的详细信息,如当前使用率、当前使用的是哪种收集器等。

jmap -dump::live,format=b,file=<filename> pid

dump堆到文件,format指定输出格式,live指明是活着的对象,file指定文件名

jmap -heap pid

打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况,可以用此来判断内存目前的使用情况以及垃圾回收情况,可以很清楚的看到Java堆中各个区域目前的情况。

jmap -histo:live pic | more

打印堆的对象统计,包括对象数、内存大小等等 (因为在dump:live前会进行full gc,如果带上live则只统计活对象,因此不加live的堆大小要大于加live堆的大小 ) class name简写如下:

  1. B  byte

  2. C  char

  3. D  double

  4. F  float

  5. I  int

  6. J  long

  7. Z  boolean

  8. [  数组,如[I表示int[]

  9. [L+类名 其他对象

jhat(JVM Heap Analysis Tool)命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看。在此要注意,一般不会直接在服务器上进行分析,因为jhat是一个耗时并且耗费硬件资源的过程,一般把服务器生成的dump文件复制到本地或其他机器上进行分析。

jstack用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。

jinfo(JVM Configuration info)这个命令作用是实时查看和调整虚拟机运行参数。 之前的jps -v口令只能查看到显示指定的参数,如果想要查看未被显示指定的参数的值就要使用jinfo口令

获取 Java GC日志方法:

jstat-gc 

GC参数

JVM的GC日志的主要参数包括如下几个:

  • -XX:+PrintGC 输出GC日志

  • -XX:+PrintGCDetails 输出GC的详细日志

  • -XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)

  • -XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2017-09-04T21:53:59.234+0800)

  • -XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息

  • -Xloggc:../logs/gc.log 日志文件的输出路径

在生产环境中,根据需要配置相应的参数来监控JVM运行情况。

案例:HTTP服务响应间歇性卡顿,Full GC时间长的原因。

1.排除应用程序的内存使用问题,内存泄漏

    jmap -histo:live PID 

    这个命令把程序中当前的对象按照个数和占用的空间排序以后打印出来。这里没有发现使用异常的对象。

2.排除Cache内容过多的问题

    如果Cache内容过多也会导致JVM老年代容易被用满导致频繁GC,因此调出GC日志进行查看,发现每次GC以后内存使用一般是从NG降低到MG左右,没有明显的持续增涨,因此常驻内存的Cache不是导致GC长时间卡顿的根本原因。对于GC LOG的查看有多种方式,使用VisualVM比较直观

3.调整GC时间点,观察GC变化

如果GC需要处理的内存量比较大,执行的时间也就比较长,STW (Stop the World)时间也就更长。按照这个思路调整CMS启动的时间点,希望提早GC,也就是让GC变得更加频繁但是期望每次执行的时间较少。添加了下面这两个参数:

  1. -XX:+UseCMSInitiatingOccupancyOnly

  2. -XX:CMSInitiatingOccupancyFraction=50

意思是说在Old区使用了50%的时候触发GC。实验后发现GC的频率有所增加,但是每次GC造成的陈功率降低现象并没有减弱,因此弃用这两个参数。用-XX+UseCMSInitiatingOccupancyOnly标志来命令JVM不基于运行时收集的数据来启动CMS垃圾收集周期。而是,当该标志被开启时,JVM通过CMSInitiatingOccupancyFraction的指定来触发。

4.调整对象在年轻代内存中驻留的时间

    如果能够降低老年代GC的频率也可以达到降低GC影响的目的,因此尝试让对象在年轻代内存中进行更长时间的驻留,提升这些对象在年轻代GC时候被销毁的概率。使用参数 -XX:MaxTenuringThreshold=30调整以后收效不明显。

5.CMS-Remark之前强制进行年轻代的GC

    通过GC日志和成功率下降的时间点进行比对发现并不是每一次老年代GC都会导致成功率的下降,但是从中发现了一个规律:CMS-Remark过程在4s左右造成了成功率的下降,GC并没有对成功率造成明显的影响时候,CMS-Remark只有0.18s左右,而时间短的时候处理CMS-Remark的内存量少,即处理的越多就越慢。

    因此添加下面两个参数强制在remark阶段和FULL GC阶段之前先在进行一次年轻代的GC,减少CMS-Remark的内存量。

  1. XX:+ScavengeBeforeFullGC

  2. -XX:+CMSScavengeBeforeRemark

备注:

1、蓝色部分的含义:remark标记需要清理对象的容量。

2、FULL GC阶段之前先在进行一次年轻代的GC的意义是:Yong区对象引用了Old区的对象,如果在Old区进行清理之前不进行Yong区清理,就会导致Old区被Yong区引用的对象无法释放。

调优以后效果很明显。

结论

1、在CMS-remark阶段需要对堆中所有的内存对象进行处理,如果在这个阶段之前强制执行一次年轻代的GC会大量减少remark需要处理的内存数量,进而降低JVM卡顿对成功率的影响。
2、对于Java HTTP服务,JVM的卡顿时间应该小于HTTP客户端的调用超时时间,否则JVM卡顿会对成功率造成影响。

https://mp.weixin.qq.com/s/sFnMxEwJiYRjwTiBIjfcZg

猜你喜欢

转载自my.oschina.net/u/3705388/blog/1794553