JVM性能调优
- 1. 前置启动程序
- 1.1 Jmap
- 1.1.1 Jmap查询内存信息
- 1.1.2 Jmap查询堆信息
- 1.1.3 jmap查询堆内存dump
- 1.2 Jstack
- 1.3 远程连接jvisualvm
本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。
课程内容:
1、Jmap,Jstack,Jinfo命令详解
2、Jvisualvm调优工具实战
3、JVM内存或CPU飙高如何快速定位
4、Jstat命令预估JⅣVM运行情况
5、系统频繁Full GC导致系统卡顿实战调优
6、内存泄露到底是怎么回事
1. 前置启动程序
事先启动一个web应用程序,用jps查看其进程id,接着用各种jdk自带命令优化应用。
因此为了后续使用,我们直接从创建一个web项目开始,该部分知识是为了复习之前所学内容。
-
创建一个springboot项目
①. 首先打开idea,找到new module
②. 点击next,填入下列信息
在这里插入图片描述
③. 点击下一步继续选择
④. 继续下一步,检查信息
⑤. 完成(如果包加载的特别慢,记得配置一下maven)
-
写一个简单的小例子
①写一段代码
package com.ding.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String getById(){
System.out.println("springboot is running.......");
return "springboot is running........";
}
}
② 运行启动类
③ 访问成功
注意事项:记得把启动类与文件放的目录关系
将该项目打包成jar项目
我们现在启动该jar包。
找到文件的位置
java -jar jvm03-0.0.1-SNAPSHOT.jar
然后这个界面就可以访问了
1.1 Jmap
1.1.1 Jmap查询内存信息
用jps查看其进程id
此命令可以用来查看内存信息,示例个数以及占用内存大小。
jmap -histo 21480(进程号)
其实一般这样就可以了,但是这样不好看,所以我们可以把他写到一个文件里面。
jmap -histo 21480 > ./log.txt
这样就打印到D盘去了
- num:序号
- instances:实例数量
- bytes:占用空间大小
- class name:类名称,[C is a char[],[S is a short[],[I is a int[],[B is a byte[],[[I is a int[][]
1.1.2 Jmap查询堆信息
jmap -heap 21480
1.1.3 jmap查询堆内存dump
jmap -dump:format=b,file=eureka.hprof 21480
导出的信息:
这个信息的作用是等一会可以导到可视化工具里面的。例如jvisualvm。
进入之后是这个界面
在这个里面它可以装入文件,就是刚才我们所生成的文件。
我们就可以看到我们当时保存快照时候的堆信息情况
也可以设置内存溢出自动导出dump文件(内存很大的时候,可能会导不出来)
- -XX:+HeapDumpOnOutOfMemoryError
- -XX:HeapDumpPath=./(路径)
示例代码:(这个代码是我又写的,不是那个jar里面的)
package ding;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class OOMTest {
public static List<Object> list = new ArrayList<>();
public static void main(String[] args) {
//ArrayList<Object> list = new ArrayList<>();
int i = 0;
int j = 0;
while (true){
list.add(new User(i++, UUID.randomUUID().toString()));
new User(j--, UUID.randomUUID().toString());
}
}
}
这个程序我们知道肯定会溢出,但是我们假如这两个参数,在快溢出的时候会导出一个快照文件。
现在来验证一下,首先配置一下,把内存设置的小一点,方便他溢出
-Xms10M -Xmx10M -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\jvm.dump
运行之前D盘是没有那个jvm.dump文件的,运行之后就会产生。
现在我们把这个文件导入到那个jvisualvm工具中。
导入之后就可以看到什么类比较多,看主要是谁占的内存比较多
1.2 Jstack
使用jstack加进程id查找死锁,见如下示例
package ding;
public class DeadLockTest {
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock1){
System.out.println("thread1 begin");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2){
System.out.println("thread1 end");
}
}
}).start();
new Thread(()->{
synchronized (lock2){
System.out.println("thread2 begin");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1){
System.out.println("thread2 end");
}
}
}).start();
System.out.println("main thread end");
}
}
在运行的时候能看到程序一直在运行,但是不动,发生了死锁。
这个时候就可以使用jstack命令去看到底是怎么回事。
查询一下进程号
这个时候就可以看到详细信息
-
“Thread-1” 线程名
-
prio=5 优先级=5
-
tid=Ox000000001fa9e000 线程id
-
nid=Ox2d64 线程对应的本地线程标识
-
nidjava.lang.Thread.State: BLOCKED 线程状态
并且在这个详细信息里面会提示发现死锁
也可以使用jvisualvm也可以直接检测到死锁。我们打开这个工具他会自动识别这个进程。
立马能显示检测到死锁(我这个中间我把这个进程杀死了,重新启动的,进程id变化了一下哈,但是不影响实验)
点击上图右上角的线程dump可以看到详细的信息。就是和我们的那个刚才黑窗口里面的信息是一样的。
1.3 远程连接jvisualvm
启动普通的jar程序JMX端口配置:
java ‐Dcom.sun.management.jmxremote.port=8888 ‐Djava.rmi.server.hostname=192.168.50.60‐Dcom.sun.management.jmxremote.ssl=false ‐Dcom.sun.management.jmxremote.authenticate=false ‐jar microservice‐eureka‐server.jar
PS:
- -Dcom.sun.management.jmxremote.port 为远程机器的JMX端口
- -Djava.rmi.server.hostname 为远程机器IP
tomcat的JMX配置:在catalina.sh文件里的最后一个JAVA_OPTS的赋值语句下一行增加如下配置行
JAVA_OPTS=“$JAVA_OPTS ‐Dcom.sun.management.jmxremote.port=8888 ‐Djava.rmi.server.hostname=192.168.50.60 ‐Dcom.sun.management.jmxremote.ssl=false ‐Dcom.sun.management.jmxremote.authenticate=false”
连接时确认下端口是否通畅,可以临时关闭防火墙
systemctl stop firewalld #临时关闭防火墙