文章目录
1:概述
性能诊断是软件工程师在日常工作中需要经常面对和解决的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。
Java作为最流行的编程语言之一,其应用性能诊断一直受到业界广泛关注。可能造成Java应用出现性能问题的因素非常多,例如线程控制、磁盘读写、数据库访问、网络I/0、垃圾收集等。想要定位这些问题,一款优秀的性能诊断工具必不可少。
体会1:使用数据说明问题,使用知识分析问题,使用工具处理问题。
体会2:无监控、不调优!
2:jps:查看正在运行的Java进程
2.1 基本情况
jps(Java Process Status):
显示指定系统内所有的HotSpot虛拟机进程(查看虛拟机进程信息),用于查询正在运行的虚拟机进程。
说明:对于本地虚拟机进程来说,进程的本地虚拟机ID与操作系统的进程ID是一致的,是唯一的。(它是使用频率最高的JDK命令行工具,因为其他的JDK工具大多需要输入他查询到的LVMID来确定要监控的是哪一个虚拟机进程,LVMID对于本地虚拟机而言就是操作系统进程ID)
2.2 jps命令格式
jps命令格式
jps [ options ] [ hostid ]
options参数
-q :仅仅显示LVMID (local virtual machine id),即本地虚拟机唯一id。 不显示主类的名称等
-l:输出应用程序主类的全类名或如果进程执行的是jar包,则输出jar完整路径
-m:输出虚拟机进程启动时传递给主类main()的参数
参数是值:idea中点击Run有一个edit configuration里面有一个Program argument
-v:列出虚拟机进程启动时的JVM参数。 比如: -Xms20m - Xmx50m是启动程序指定的jvm参数。
说明:以上参数可以综合使用。
注意:如果某Java进程关闭了默认开启的UsePerfData参数( 即使用参数-XX:-UsePerfData),那么jps命令 (以及下面介绍的jstat)将无法探知该Java进程。
hostid参数
RMI注册表中注册的主机名。
如果想要远程监控主机上的java程序,需要安装jstatd
对于具有更严格的安全实践的网络场所而言,可能使用一个自定义的策略文件来显示对特定的可信主机或网络的访问,尽管这种技术容易受到IP地址欺诈攻击。
如果安全问题无法使用一个定制的策略文件来处理,那么最安全的操作是不运行jstatd服务器,而是在本地使用jstat和jps工具。
实例演示
C:\JavaJDK1.8\Java\jdk1.8.0_281\bin>jps
3088
34752 JpsDemo
40192 Launcher
41316 Jps
C:\JavaJDK1.8\Java\jdk1.8.0_281\bin>jps -m -> 1.txt
1.txt
3088
42500 Launcher D:/IDEA/IntelliJ IDEA 2017.3.2/lib/jps-builders-6.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/snappy-in-java-0.5.1.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/asm-all.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/aether-dependency-resolver.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/maven-aether-provider-3.3.9-all.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/jdom.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/guava-21.0.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/jps-builders.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/httpclient-4.5.2.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/oromatcher.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/log4j.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/forms_rt.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/httpcore-4.4.5.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/netty-all-4.1.13.Final.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/jna-platform.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/commons-codec-1.9.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/jna.jar;D:/IDEA/IntelliJ IDEA 2017.3.2/lib/jps-model.jar;D:/IDEA/IntelliJ IDEA 2017.3.
33144 JpsDemo hello //idea中的Program argument写了hello
41644 Jps -m -
3:jstat:查看JVM统计信息
3.1 基本情况
jstat(JVM Statistics Monitoring Tool): 用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。常用于检测垃圾回收问题以及内存泄漏问题。
3.2 jstat命令格式
jstat [option -t vmid [interval[s|ms] [count] ]
option参数
选项option可以由以下值构成。
类装载相关的:
-class:显示ClassLoader的相关信息:类的装载、卸载数量、总空间、类装载所消耗的时间等
垃圾回收相关的:
-gc:显示与GC相关的堆信息。包括Eden区、两个Survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息。
-gccapacity:显示内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大、最小空间。
-gcutil:显示内容与-gc基本相同,但输出主要关注己使用空间占总空间的百分比。
-gccause:与-gcutil功能一样,但是会额外输出导致最后一次或当前正在发生的GC产生的原因。
-gcnew:显示新生代GC状况
-gcnewcapacity:显示内容与-gcnew基本相同,输出主要关注使用到的最大、最小空间
-geold:显示老年代GC状况
JIT相关的
-compiler:显示JIT编译器编译过的方法,耗时等信息
-printcompilation:输出已经被JIT编译的方法
参数interval和count代表查询间隔和次数,如果省略这两个参数,说明只查询一次。假设需要每250ms查询一次进程2764垃圾收集情况,一共查询20次,命令如下
参数-t表示可以在输出信息前加上一个Timestamp列,显示程序的运行时间,单位:秒
重要:我们可以比较Java进程的启动时间以及总 GC 时间(GCT列),或者两次测量的间隔时间以及总GC时间的增量,来得出 GC时间占运行时间的比例。如果该比例超过20%,则说明目前堆的压力较大;如果该比例超过90%,则说明堆里几乎没有可用空间,随时都可能抛出 OOM异常。
jstat -gc 2764 250 20
实例演示
/**
* @author 郑霖俊
* @create 2021-03-30 9:55
* -Xms60m -Xmx60m -XX:SurvivorRatio=8
*/
public class JstatGCTest {
public static void main(String[] args) {
ArrayList<byte[]> list=new ArrayList<>();
for (int i=0;i<1000;i++){
byte[] arr=new byte[1024*100];
list.add(arr);
try {
Thread.sleep(100);
}catch (InterruptedException e){
}
}
}
}
4:jinfo:实时查看和修改JVM配置参数
4.1 基本情况
jinfo(Configuration Info for Java)
查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数。
在很多情况下,Java应用程序不会指定所有的Java虚拟机参数。而此时,开发人员可能不知道某一个具体的Java虚拟机参数的默认值。在这种情况下,可能需要通过查找文档获取某个参数的默认值。这个查找过程可能是非常艰难的。但有了jinfo工具,开发人员可以很方便地找到Java虚拟机参数的当前值。
4.2 jinfo命令格式
jinfo命令格式
jinfo [option] pid
jinfo -sysprops PID:可以查看由System.getProperties)取得的参数。
jinfo -flags PID:查看曾经赋过值的一些参数
jinfo -flag 具体参数 PID:查看某个java进程的具体参数的值
针对boolean类型:jinfo -flag [+I-]具体参数 PID
针对非boolean类型:jinfo -flag 具体参数=具体参数值 PID
jinfo不仅可以查看运行时某一个Java虚拟机参数的实际取值,甚至可以在运行时修改部分参数,并使之立即生效。但是,并非所有参数都支持动态修改。参数只有被标记为manageable的flag可以被实时修改其实,这个修改能力是极其有限的。
可以查看被标记为manageable的参数
java -XX: +PrintFlagsFinal -version| grep manageable
实例演示
boolean类型
非boolean类型
扩展:
java -XX:+ PrintFlagslnitial 查看所有JVM参数启动的初始值
java -XX:+ PrintFlagsFinal 查看所有JVM参数的最终值
java -XX: + PrintCommandLineFlags 查看那些已经被用户或者JVM设置过的详细的XX参数的名称和值-C
5:jmap:导出内存映像文件&内存使用情况
5.1 基本情况
jmap(JVM Memory Map): 作用一方面是获取dump文件( 堆转储快照文件,二进制文件),它还可以获取目标Java进程的内存相关信息,包括Java堆各区域的使用情况、堆中对象的统计信息、类加载信息等。
开发人员可以在控制台中输入命令“jmap -help” 查阅jmap工具的具体使用方式和一些标准选项配
5.2 jmap命令格式
jmap命令格式
jmap [option] vmid
其中option包括
重要参数:-dump -heap -histo
5.3 jmap的三种使用
使用1:导出内存映像文件
手动方式
jmap -dump:format= b,file= < filename.hprof> <pid>
jmap -dump:live,format= b,file= < filename.hprof> <pid> ẞ
自动的方式
-XX:+ HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath= <filename.hprof>
一般来说, 使用jmap指 令生成dump文件的操作算得上是最常用的jmap命令之一, 将堆中所有存活对象导出至一个文件之中。
Heap Dump 又叫做堆存储文件,指一个Java进程在某个时间点的内存快照。Heap Dump在 触发内存快照的时候会保存此刻的信息如下:
说明:1.通常在写Heap Dump文件前会触发一次Full GC,所以heap dump文件里保存的都是FullGC后留下的对象信息。
使用实例
手动方式
自动方式
当程序发生OOM退出系统时,一些瞬时信息都随着程序的终止而消失,而重现OOM问题往往比较困难或者耗时。此时若能在OOM时,自动导出dump文件就显得非常迫切。
这里介绍一种比较常用的取得堆快照文件的方法,即使用:
-XX: +HeapDumpOnOutOfMemoryError:在程序发生OOM时,导出应用程序的当前堆快照。
-XX: HeapDumpPath:可以指定堆快照的保存位置。
比如:
- Xmx100m -XX:+HeapDumpOnOutOfMemoryError - XX:HeapDumpPath=D: \m. hprof
在idea中的vm options中进行配置
使用2:显示堆内存相关信息
jmap -heap pid
jmap -histo
由于jmap将访问堆中的所有对象,为了保证在此过程中不被应用线程干扰,jmap需要借助安全点机制,让所有线程停留在不改变堆中数据的状态。也就是说,由jmap导出的堆快照必定是安全点位置的。这可能导致基于该堆快照的分析结果存在偏差。
举个例子,假设在编译生成的机器码中,某些对象的生命周期在两个安全点之间,那么:live选项将无法探知到这些对象。
另外,如果某个线程长时间无法跑到安全点,jmap将一 直等 下去。
与前面讲的jstat则不同,垃圾回收器会主动将jstat所需要的摘要数据保存至固定位置之中而jstat只需直接读取即可。
使用3:其它作用
jmap -permstat pid 查看系统的ClassL oader信息
jmap -finalizerinfo 查看堆积在finalizer队列中的对象
6:jhat: JDK自带堆分析工具
jhat(JVM Heap Analysis Tool):
Sun JDK提供的jhat命令与jmap命令搭配使用,用于分析jmap生成的heap dump文件(堆转储快照)。jhat内置了一个微型的HTTP/HTML服务器,生成dump 文件的分析结果后,用户可以在浏览器中查看分析结果(分析虚拟机转储快照信息)。
使用了jhat命令,就启动了一个http服务,端口是7000,即http://localhost : 7000/,就可以在浏览器里分析。
说明: jhat 命令在JDK9、JDK10中已经被删除,官方建议用VisualVM代替。
7: jstack:打印JVM中线程快照
jstack(JVMStackTrace):用于生成虚拟机指定进程当前时刻的线程快照(虚拟机堆栈跟综)。线程快照就是当前虚拟机内指定进程的每–条线程正在执行的方法堆栈的集合。
生成线程快照的作用:可用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。这些都是导致线程长时间停顿的常见原因。当线程出现停顿时,就可以用jstack显示各个线程调用的堆栈情况。
在thread dump中, 要留意下面几种状态
死锁,Deadlock (重点关注)
等待资源,Waiting on condition (重点关注)
等待获取监视器,Waiting on monitor entry (重点关注)
阻塞,Blocked (重点关注)
执行中,Runnable
暂停,Suspended
线程死锁演示
public class ThreadDeadLock {
public static void main(String[] args) {
StringBuilder s1=new StringBuilder();
StringBuilder s2=new StringBuilder();
new Thread(){
@Override
public void run(){
synchronized (s1){
s1.append("a");
s2.append("1");
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(){
@Override
public void run(){
synchronized (s2){
s1.append("c");
s2.append("3");
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
}
}