常见命令
#按照进程名字查找
ps -aux | grep java
#强制杀死进程
kill -9 pid
#按照端口号进行查找
netstat -anp |grep 8080
#查看两个机器是否联通
telnet ip 端口
#查看开放的端口
cat /etc/ssh/sshd_config
#跨服务器拷贝
scp -r xxx.jar root@ssh ip:/usr -》root指的是ip所在的登录用户
scp -P 22222 -r xxx.jar root@ssh ip:/usr -》指定端口号默认22
scp -r xxx.jar root@ip:/usr -》省略写ssh
#无日志输出
nohup java -jar xxx.jar > /dev/null 2> /dev/null &
#有日志输出
nohup java -jar xxx.jar > ./output.log &
#指定配置文件输出
nohup java -jar xxx.jar --spring.profiles.active=test2> ./output.log &
#查看日志
tail -f ./output.log
#复杂搜索
grep
定位
grep '搜索内容' 路径开头* ->比如grep 'aa' ./*xxx.log 或者xxx*或者xxx.log
搜索内容复杂的可以用右边替换 \\[projectName\\][[:space:]]is System* ->英文中括号加反斜杠,空格用[[:space:]]替换,搜索内容不要加引号
前后
grep -C 100 '搜索内容' xxx* 搜索前后100行
grep -A 10 -B 1 '搜索内容' xxx* 搜索前一行后10行
tail
tail -10f xxx.log 指定整个文件
1、死锁
#性能排查
https://blog.csdn.net/weixin_45549188/article/details/129629486
#arthas教程
https://arthas.aliyun.com/doc/quick-start.html
/**
//54-死锁
*/
// 创建两个共享资源
private static final String resource1 = new String();
private static final Object resource2 = new Object();
@GetMapping("lock")
public void lock() {
/**
* 两个线程 thread1 thread2
* thread1对resource1上锁,然后尝试获取resource2的锁;
* thread2对resource2上锁,然后尝试获取resource1的锁;
* 2个线程会相互等待,出现死锁
*/
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Locked resource 1");
try {
Thread.sleep(100); // 假设这里有其他操作,需要一些时间
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Thread 1: Attempting to lock resource 2");
//尝试索取resource2的锁
synchronized (resource2) {
System.out.println("Thread 1: Locked resource 2");
}
}
}, "thread1");
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Locked resource 2");
try {
Thread.sleep(100); // 假设这里有其他操作,需要一些时间
} catch (Exception e) {
e.printStackTrace();
}
//尝试索取resource1的锁
System.out.println("Thread 2: Attempting to lock resource 1");
synchronized (resource1) {
System.out.println("Thread 2: Locked resource 1");
}
}
}, "thread2");
// 启动线程
thread1.start();
thread2.start();
}
1-1、【推荐】排查步骤(命令)方法1
ps -aux | grep java
#杀死进程继续下一个测试
kill -9 pid
#以守护进程启动
nohup java -jar cpu-oom-test-1.0-SNAPSHOT.jar > ./output.log &
#如果出现这个工具没有的话
-bash: pstack: command not found
sudo yum install -y gdb
#直接输入面程序查看死锁
top
jstack pid
最后面就会出现发生死锁的位置了(默认敲完jstack命令就会滑到最后所以很容易就看到了)
1-2、使用arthas->方法2
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
#输入你的java程序,回车
#直接输入面程序查看死锁
thread -b
2、内存泄漏
/**
//55-内存泄露
*/
public static List<byte[]> oomList = new ArrayList<>();
@GetMapping("oom")
public String oom() {
while (true) {
//不断向列表中添加大对象, 10MB
oomList.add(new byte[1024 * 1024 * 10]);
}
}
//55-内存泄露
ps -aux | grep java
#杀死进程继续下一个测试
kill -9 pid
#以守护进程启动
nohup java -jar cpu-oom-test-1.0-SNAPSHOT.jar > ./output.log &
top
#可以查看内存占用情况
jmap -heap pid
#使用arthas查看占用情况
dashboard
2-1【推荐】使用程序运行日志直接查看
这时候其实output.log里面也能看见是哪一行泄漏的
grep -A 10 -B 1 'OutOfMemory' output.log
2-2程序在运行之前就指定dump输出(特点文件小,信息精准)
运行直接指定(设置内存比较小方便测试)
nohup java -jar -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heap-dump.hprof -Xmx10m xxx.jar > ./output.log &
#将上面的dump文件考出来
#用visualvm工具进行分析,没有的进行下载
https://visualvm.github.io/index.html
用工具加载之后,首页就有泄漏的红色标记
或者summary换成thread 视图选择文本直接搜memory就可以看到了
2-3 在运行的时候手动导出堆信息(特点文件大,没有直接溢出信息)【不推荐】
#在运行导出日志文件【直接导出的文件大,而且查不见泄漏的进程,日志可以】
jmap -dump:format=b,file=./dump.hprof pid
#将上面的dump文件考出来
https://eclipse.dev/mat/
#必须使用Eclipse MAT工具进行分析才行
(工具下载不下来换镜像,jdk必须17以上)
#Spring推荐的Liberica Open JDK下载地址-贝尔实验室版本
https://bell-sw.com/pages/downloads/#jdk-8-lts
往下滑会提示有提示,只能大致推断出是那个类,不能具体到哪一行
Thread Stack信息里面显示也是空空如也
如果结合visualvm分析的话,只能说也很难推断出是哪一行吧,不推荐
3、cpu飙升
/**
//56-cpu飙升
//常规排查和arthas-57
*/
@GetMapping("cpu")
public void test1() {
System.out.println("程序已启动");
while (true) {
;
}
}
3-1常规方法(方法1)
ps -aux | grep java
#杀死进程继续下一个测试
kill -9 pid
#以守护进程启动
nohup java -jar cpu-oom-test-1.0-SNAPSHOT.jar > ./output.log &
#使用top命令找到cpu飙升进程id
top
#根据进程id找到导致cpu飙升的线程
ps H -eo pid,tid,%cpu | grep 进程id
#将线程id转换为16进制
printf '0x%x\n' 线程id
#根据线程定位问题代码
jstack 进程id | grep 16进制线程id -A 20
简化【推荐,省事】
创建一个脚本直接执行,把上面几个步骤合起来
#!/bin/bash
# 获取用户输入的进程 ID
read -p "请输入要分析的Java进程ID: " pid
# 查找该进程下CPU使用率最高的线程(按CPU使用率降序排序取第一条)
top_thread=$(ps H -eo pid,tid,%cpu | grep $pid | sort -k3 -nr | head -n 1 | awk '{print $2}')
# 将线程ID转换为十六进制
hex_thread=$(printf '0x%x\n' $top_thread)
# 使用jstack查找对应线程的栈信息并输出
jstack $pid | grep $hex_thread -A 20
3-2使用arthas(方法2)【推荐快】
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
#输入你的java程序,回车
thread
#找到占用最高cpu的线程id
thread 线程id
4、arthas排查接口响应慢,代码不生效,方法执行异常
/**
//58-60-接口响应慢-arthas
*/
@GetMapping("slow")
public void slow() throws InterruptedException {
log.info("start");
this.m1(100, "t1");
this.m1(200, "t2");
this.m1(1000, "t3");
log.info("end");
}
private void m1(int minus, String name) throws InterruptedException {
//休眠100毫秒
TimeUnit.MILLISECONDS.sleep(minus);
log.info(name);
}
ps -aux | grep java
#杀死进程继续下一个测试
kill -9 pid
#以守护进程启动
nohup java -jar cpu-oom-test-1.0-SNAPSHOT.jar > ./output.log &
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
#输入你的java程序,回车
4-1排查接口响应慢
#接口响应慢排查
【会显示总共执行时间是多少毫秒,每个步骤是多少毫秒】
trace com.csw.cpuoom.controller.CpuOomController slow -n 5 --skipJDKMethod false
然后再掉一下接口观察输出
4-2、代码未生效
#检查代码未生效
【会反编译类获取线上的代码,能看到线上代码对不对】
jad com.csw.cpuoom.controller.CpuOomControllerjadd
4-3方法执行异常(执行结果)
/**
62-查看方法执行异常-atthas
*/
@GetMapping("err")
public void err() throws InterruptedException {
System.out.println("程序已启动");
for (int i = 1; i < 1000000; i++) {
add(i, i + 1);
TimeUnit.SECONDS.sleep(1);
}
}
public static int add(int a, int b) {
return a + b;
}
#检查方法执行异常(执行结果)
【相当于说能够看到方法的实时请求和返回值】【先敲命令后掉接口】
watch com.csw.cpuoom.controller.CpuOomController add "{params,returnObj}" -x 2 -n 999