文章目录
- 前言
- 一、内存使用过高导致CPU满载
- 案例代码
- 分析思路
- 二、出现了类似死循环导致cpu负载
- 案例代码
- 分析思路
- 三、死锁
- 案例代码
- 分析思路
前言
首先线上接口变慢,原因可能有很多,有可能是网络,有可能是慢 SQL,有可能是服务本身出现了问题,还有可能是机器达到了性能瓶颈。而机器性能瓶颈也又可以分为磁盘 IO 瓶颈、CPU 性能瓶颈、网卡瓶颈等等。
线上接口过慢,排除网络的原因之外无非有以下三点:
- 内存使用过高,频繁gc导致cpu占满
- 内存使用不高,出现了类似死循环场景
- 死锁
一般在遇到问题的时候先使用top -c 命令查看cpu是否占满,然后再使用free -m查看内存使用率,初步
判断是上面问题的哪一种,然后再针对这一种问题深入排查。下面来模拟一下以上几种情况:
一、内存使用过高导致CPU满载
案例代码
public class FullGc {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
for (int i = 0; i < 10000000; i++) {
String str = "";
for (int j = 0; j < 1000; j++) {
str += UUID.randomUUID().toString();
}
list.add(str);
System.out.println(str);
}
}
}
分析思路
-
查看负载高的cpu进程id: top -c
这时发现进程id为70719的cpu负载较高
-
查看该进程id的线程id: top -Hp 70719
这时发现线程id为70720的线程负载较高
-
将十进制的线程id转换为16进制小写的:printf “%x\n” 69476
发现16进制的线程id为11440
-
查看该线程的运行的情况:jstack 70719|grep 11440 -A60
发现是FullGc这个类的第14行有问题
-
通过jamp工具导出内存日志,分析堆的情况:jmap -dump:format=b,file=dump.dat 70719
-
使用MAT工具分析堆的情况
发现了以上大量的字符串
二、出现了类似死循环导致cpu负载
案例代码
public class Cpu {
public static void main(String[] args) {
while (true) {
}
}
}
分析思路
基本上跟内存使用率过高的前几步差不多的,依然是以下几步:
- top命令查看当前CPU消耗过高的进程,得到进程id
- 根据进程id得到占比较高的线程id
- 将线程id转换为十六进制
- 根据进程编号用jstack命令查看线程id,可以得到线程的具体堆栈信息
从图中可以看出Cpu这个类的第三行有问题
三、死锁
案例代码
public class DeadLock {
private static Object left = new Object();
private static Object right = new Object();
private static class Thread1 implements Runnable {
@Override
public void run() {
synchronized (left) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
synchronized (right) {
}
}
}
}
private static class Thread2 implements Runnable {
@Override
public void run() {
synchronized (right) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
synchronized (left) {
}
}
}
}
public static void main(String[] args) {
new Thread(new Thread1()).start();
new Thread(new Thread2()).start();
}
}
分析思路
这个就简单了
- 先使用jps查看进程id,如图:
- 找出死锁的线程:jstack [进程id]
jvm会自动搜索出死锁的堆栈信息