JVM性能优化入门指南

前言

入门JVM垃圾回收机制后,接下来可以学习性能调优了。主要有两部分内容:

  • JDK工具的使用。

  • 调优策略。

兵器谱

jps

列出正在运行的虚拟机进程,用法如下:

jps [-option] [hostid]
选项 作用
q 只输出LVMID,省略主类的名称
m 输出main method的参数
l 输出完全的包名,应用主类名,jar的完全路径名
v 输出jvm参数

jstat

监视虚拟机运行状态信息,使用方式:

JVM性能优化入门指南

选项 作用
gc 输出每个堆区域的当前可用空间以及已用空间,GC执行的总次数,GC操作累计所花费的时间。
gccapactiy 输出每个堆区域的最小空间限制(ms)/最大空间限制(mx),当前大小,每个区域之上执行GC的次数。(不输出当前已用空间以及GC执行时间)。
gccause 输出-gcutil提供的信息以及最后一次执行GC的发生原因和当前所执行的GC的发生原因。
gcnew 输出新生代空间的GC性能数据。
gcnewcapacity 输出新生代空间的大小的统计数据。
gcold 输出老年代空间的GC性能数据。
gcoldcapacity 输出老年代空间的大小的统计数据。
gcpermcapacity 输出持久带空间的大小的统计数据。
gcutil 输出每个堆区域使用占比,以及GC执行的总次数和GC操作所花费的事件。

比如:

jstat -gc 28389 1s

每隔1秒输出一次JVM运行信息:

JVM性能优化入门指南
JVM性能优化入门指南
JVM性能优化入门指南
JVM性能优化入门指南
JVM性能优化入门指南
JVM性能优化入门指南

jmap

生成堆存储快照,使用方式:

JVM性能优化入门指南
JVM性能优化入门指南

jstack

生成虚拟机当前时刻的线程快照,帮助定位线程出现长时间停顿的原因,用法:

jstack <pid>

Monitor

Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者Class的锁。每一个对象都有,也仅有一个 monitor。下面这个图,描述了线程和 Monitor之间关系,以及线程的状态转换图:

JVM性能优化入门指南

进入区(Entrt Set):表示线程通过synchronized要求获取对象的锁,但并未得到。

拥有者(The Owner):表示线程成功竞争到对象锁。

等待区(Wait Set):表示线程通过对象的wait方法,释放对象的锁,并在等待区等待被唤醒。

线程状态

  • NEW,未启动的。不会出现在Dump中。

  • RUNNABLE,在虚拟机内执行的。

  • BLOCKED,等待获得监视器锁。

  • WATING,无限期等待另一个线程执行特定操作。

  • TIMED_WATING,有时限的等待另一个线程的特定操作。

  • TERMINATED,已退出的。

举个例子:

JVM性能优化入门指南
JVM性能优化入门指南
线程状态:JVM性能优化入门指南
t1没有抢到锁,所以显示BLOCKED。t2抢到了锁,但是处于睡眠中,所以显示TIMED_WAITING,有限等待某个条件来唤醒。
把睡眠的代码去掉,线程状态变成了:JVM性能优化入门指南
t1显示RUNNABLE,说明正在运行,这里需要额外说明一下,如果这个线程正在查询数据库,但是数据库发生死锁,虽然线程显示在运行,实际上并没有工作,对于IO型的线程别只用线程状态来判断工作是否正常。
把MyTask的代码小改一下,线程拿到锁之后执行wait,释放锁,进入等待区。JVM性能优化入门指南
线程状态如下:JVM性能优化入门指南

两个线程都显示WAITING,这次是无限期的,需要重新获得锁,所以后面跟了on object monitor

再来个死锁的例子:

JVM性能优化入门指南
JVM性能优化入门指南
JVM性能优化入门指南
线程状态:JVM性能优化入门指南

这个有点像哲学家就餐问题,每个线程都持有对方需要的锁,那就运行不下去了。

调优策略

两个基本原则:

  • 将转移到老年代的对象数量降到最少。

  • 减少Full GC的执行时间。目标是Minor GC时间在100ms以内,Full GC时间在1s以内。

主要调优参数:

设定堆内存大小,这是最基本的。

  1. -Xms:启动JVM时的堆内存空间。

  2. -Xmx:堆内存最大限制。

设定新生代大小。

新生代不宜太小,否则会有大量对象涌入老年代。

  1. -XX:NewRatio:新生代和老年代的占比。

  2. -XX:NewSize:新生代空间。

  3. -XX:SurvivorRatio:伊甸园空间和幸存者空间的占比。

  4. -XX:MaxTenuringThreshold:对象进入老年代的年龄阈值。

设定垃圾回收器

年轻代:-XX:+UseParNewGC。

老年代:-XX:+UseConcMarkSweepGC。

CMS可以将STW时间降到最低,但是不对内存进行压缩,有可能出现“并行模式失败”。比如老年代空间还有300MB空间,但是一些10MB的对象无法被顺序的存储。这时候会触发压缩处理,但是CMS GC模式下的压缩处理时间要比Parallel GC长很多。

G1采用”标记-整理“算法,解决了内存碎片问题,建立了可预测的停顿时间类型,能让使用者指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。