Java性能权威指南

一、Java监控工具

  • jcmd:打印Java进程中的基本类、线程和JVM信息。适合用在脚本中。以下方式执行:
    % jcmd process_id command optional_arguments。
  • jconsole:提供JVM活动的图形化视图,包括线程的使用、类的使用和GC活动。jconsole需要相当多的系统资源,在生产系统中运行可能会干扰系统。你可以设置jconsole让它在本地运行,并附加到远程系统上,这样就不会影响远程系统的性能了。
  • jmap:提供堆转储和其他JVM内存使用的信息。适合用在脚本中,堆转储需要在后期处理工具中使用。
  • jinfo:查看JVM系统属性,允许动态设置一些系统属性。适合用在脚本中。
  • jstack:转储Java进程的栈信息。适合用在脚本中。
  • jstat:提供GC和类加载活动的信息。适合用在脚本中。
  • jvisualvm:监控JVM的GUI工具,既可以分析正在运行的应用程序,也可以分析JVM堆转储文件(这是后期处理操作,不过jvisualvm也可以实时导出程序的堆转储)。

二、获取基本的VM信息

  • JVM启动后运行的时长: jcmd process_id VM.uptime
  • 系统属性: jinfo -sysprops process_id
  • JVM版本: jcmd process_id VM.version
  • JVM命令行: jcmd process_id VM.command_line
  • JVM调优标志: jcmd process_id VM.flags [-all]
  • JVM平台相关的默认值:java other_options -XX:+PrintFlagsFinal -version
  • 检查单个标志的值:jinfo -flag  xxx process_id

三、获取线程信息 

  • 显示每个线程栈的大量输出:jstack process_id或者jcmd process_id Thread.print

四、垃圾回收器

Serial垃圾回收器

Serial垃圾回收器使用单线程来处理堆。它在处理堆时会停止所有的应用程序线程(不管是Minor GC还是Full GC)。使用-XX:+UseSerialGC标志可以开启Serial垃圾回收器。

Throughput垃圾回收器

Throughput垃圾回收器使用多个线程回收新生代和老年代。Throughput垃圾回收器在Minor GC和Full GC时都会停止所有的应用程序线程。可以使用-XX:+UseParallelGC标志开启。

G1 GC垃圾回收器

G1 GC使用并发回收策略来以最小的停顿回收堆。G1 GC将堆划分为多个Region(区域或分区),但它仍然认为堆有两代。一些区域组成了新生代,回收新生代时G1 GC还是会暂停所有应用程序线程,并将所有活跃的对象移动到老年代或Survivor空间(此时使用了多个线程)。G1 GC可以通过-XX:+UseG1GC标志开启。

CMS垃圾回收器

CMS使用多个线程来执行回收操作,会在Minor GC的过程中停止所有的应用程序线程。CMS可以通过-XX:+UseConcMarkSweepGC标志开启

选择合适的GC算法

        对于大多数应用程序来说,G1 GC的算法是更好的选择。当在单CPU机器上运行CPU密集型应用程序时,即使这个CPU是超线程的,使用Serial垃圾回收器也更有意义。在同样的硬件上处理非CPU密集型任务,G1 GC是更好的选择。在多CPU机器上运行CPU密集型任务时,Throughput垃圾回收器更有意义。即使任务不是CPU密集型的,如果它产生的Full GC相对较少或者老年代一般是满的,Throughput垃圾回收器也可能是更好的选择。

五、GC优化

设置堆大小

        在调整堆的大小时,首要规则是设定堆的大小永远不要超过机器的物理内存——如果有多个JVM在运行,那么这适用于所有堆的总和。你也需要为JVM的原生内存以及其他应用程序留出一些内存空间。通常,常见的操作系统配置需要至少1 GB的空间。堆的大小由两个值控制:初始值(通过-XmsN 标志设定)和最大值(通过-XmxN标志设定)

调整分代大小

        如果新生代相对较大,Young GC的停顿时间会增加,但是新生代的回收频率会降低,晋升到老年代的对象也会更少。但同时,老年代相对会更小,它被填满的频率会更高,会执行更多的Full GC。找到平衡点是关键。-XX:NewRatio=N:设置新生代与老年代的比例。

调整元空间大小

元空间保存了类元数据(不是类对象),并且表现得像一个单独的堆,元空间的初始大小可以基于所有类都加载之后的使用量来设置,这会稍微加快启动速度,定义并丢弃大量类的应用程序,在元空间被填满且旧的类被删除时,偶尔会发生Full GC。根据初始大小(-XX:MetaspaceSize=N)动态调整的。

控制并行

线程的数量由-XX:ParallelGCThreads=N标志控制。所有GC算法的基本线程数都是基于机器的CPU数量的。线程总数如下(N表示CPU的数量):ParallelGCThreads = 8 + ((N - 8) * 5 / 8)。有时这个结果会偏大需要根据实际情况调整。

在jdk8开启GC日志

-XX:+PrintGCDetails标志将创建包含更多信息的详细日志,在结合用-XX:+PrintGCTimeStamps或-XX:+PrintGCDateStamps,这样可以确定GC操作之间的时间。

将日志记录到文件里:-Xloggc:gc.log -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation-XX:NumberOfGCLogFile=8 -XX:GCLogFileSize=8m 这个命令还设置日志滚动。

在JDK 11中开启GC日志

-Xlog:gc*:file=gc.log:time:filecount=7,filesize=8M,(gc*)设定了应该开启哪些模块的日志

动态查看GC日志

jstat -gcutil 进程Id 重复命令的毫秒数

六、堆

获取堆的直方图:jcmd 进程Id GC.class_histogram(包含活跃对象,因为该命令通常会强制执行Full GC。你可以在命令中加上-all标志来跳过Full GC,不过这样一来,直方图就会包含未被引用的(垃圾)对象了)

堆转储:jcmd process_id GC.heap_dump /path/to/heap_dump.hprof

你可能感兴趣的:(java,开发语言)