Java虚拟机-JVM故障诊断与性能优化

一. 常用虚拟机参数

1.1 掌握跟踪调试参数

1.1.1. 跟踪垃圾回收-读懂虚拟机日志

GC 参数

-XX:+PrintGC //jdk 8 之前
-Xlog:gc     //jdk 9 10 使用
  • 需要得到详细的信息 : -XX:PrintGCDetails --jdk 8 -Xlog:gc* --jdk 9 10
  • 需要得到全面的堆信息 : -XX:PrintHeapAtGC
  • 需要分析GC时间: -XX:+PrintGCTimeStamps 在jdk 9 10中已经可以默认使用 -XXlog:gc来打印出gc时间
  • 由于GC会导致应用程序的停顿,因此还需要特别关注应用程序的执行时间和停顿时间。使用参数 -XX:PrintGCAppilcationConcurrentTime 可以打印应用程序执行的时间,另外使用参数 *-XX:PrintGCApplicationStoppedTime * 可以打印程序由于GC而产生的停顿时间。
  • 如果想要追踪系统内的软引用,弱引用,虚引用和Finallize 队列 可以使用 **-XXPPrintReferenceGC **(但是考虑到兼容性,在JDK 9删除了该参数)
    默认情况下,GC 的日志会在控制台进行输出,但是,这不便于后续分析定位问题,所以,虚拟机允许将GC日志以文件的形式进行输出,可以使用 -Xloggc来进行指定。比如使用参数-Xloggc:log/gc.log 即可将GC 日志记录在当前目录文件下的log /gc.log 中。

1.1.2 . 类加载、卸载跟踪

类加载/卸载的跟踪参数

可以使用-verbose:class 跟踪类的加载和卸载过程

  • 下面介绍单独使用参数进行跟踪
# jdk 1.8
-XX:TraceClassLoading   //类加载
-XX:TraceClassUnloading   //类卸载
# jdk 1.9 10
-Xlog:class+load=info
-Xlog:class+unload=info

1.1.3. 查看系统参数

  • 参数 -XX:+PrintCVMOptions 可以在程序运行的时候打印虚拟机接收到的命令行显式参数
  • 参数 -XX:+PrintCommandLineFlags 可以打印传递给虚拟机的显式和隐式参数
  • 使用 -XX:+PirntFlagsFinal 会打印所有的系统参数。

1.2 让性能飞起来:堆的配置参数

1.2.1 最大堆和初始堆大小的设置

当java 进程启动的时候,虚拟机首先会分配一块初始的堆空间,可以使用参数-Xms 来指定初始大小,一般来说,虚拟机会尽可能在初始堆的空间上进行运行但是如果初始堆空间用完则会对初始分配空间进行扩展,其上限为最大堆空间,最大堆空间可以使用-Xmx 来进行指定。

值得注意的时,在实际的工作工程中,可以直接将初始堆-Xms与最大堆-Xmx 设置为相等,这样的好处在于可以减少程序运行时进行垃圾回收的次数,从而提升程序的性能

1.2.2 新生代的配置

参数-Xmn 可用来指定新生代的大小。但是设置一个较大的新生代则会减少老年代的大小,这个对系统性能的影响很大,新生代的大小一般设置为整个堆空间的三分之一到四分之一。

参数-XX:SurvivorRatio 用来设置新生代中eden区和from/to 的比例

-XX:SurvivorRatio= 2 //设置eden区域和from 的比例为2/1

除了使用参数 -Xmn来指定新生代的绝对大小,还可以使用参数-XX:NewRatio 来设置新生代和老年代的比例。

-XX:NewRatio=新生代/老年代

1.2.3 堆溢出的处理

在Java 程序中,堆空间不足,则可能导致内存溢出的错误,简称OOM(out of Memory) ,一旦发生该错误,系统就会被迫退出,引起业务中断。

-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump

假如上述参数配置下,当前堆的信息保存在D:/a.dump 文件下,使用MAT进行分析。

除了发生OOM可以导出堆信息,虚拟机还允许发生错误的时候执行一个脚本文件,该文件可以用在崩溃的程序自救报警通知。比如完整的线程转存文件。

给出一个发生OOM是导出线程转存的例子,可准备转存(printStack.bat)文件如下

D:/tools/jdk1.8_23/jstack -F %1 > D:a.txt //在程序异常退出的时候,系统会将线程转存讯息保存在D盘下的a.txt 文件下

1.3 非堆的参数配置

1.3.1 方法区的配置

jdk 1.6 jdk 1.7

方法区主要存放类的元信息

-XX:PermSize  //初始永久的大小
-XX:MaxPermSize //最大永久区的大小

JDK 1.8 +

永久区被彻底移除,但是使用新的元数据区存放类的元数据(元空间) 但是依然可以使用-XX:MaxMetaspaceSize 来指定元空间的最大可用。

1.3.2 栈配置

-Xss //使用该参数来指定栈的大小

1.3.3 直接内存的配置

直接内存是Java 内存中十分重要的组成部分,在NIO 广泛使用,直接内存跳过了java堆,使Java程序可直接访问原生堆空间,因此在一定程度上提高了内存的访问速度。

-XX:MaxDirectMemorySize   //不进行此项设置的话会默认设置直接内存的大小为最大堆空间大小

1.4 虚拟机的工作模式

虚拟机的工作模式分为 -server -client两种运行模式,在默认情况下虚拟机会根据系统环境自动选择运行模式,可以使用-version 来进行查看当前模式。

java -version
// 但是使用-server参数后就可以得到
java -server -version

与Client 模式相比,Server 模式的启动比较慢,因为Server 模式会尝试收集系统信息,使用更复杂的优化算法进行优化,因此在系统完全进入稳定的运行期时,Server 的执行速度会远远快于Client 模式。所以对于后台的长期运行的程序来说,使用-server 启动参数对系统的整体性能都有较大的帮助,单数对于用户界面程序来说追求启动速度,Client 模式也是不错的选择。