【个人技术经验及开发技巧分享】 【个人技术经验及开发技巧分享】
首页
  • 操作系统初识
  • JAVA基础
  • JVM
  • 开发框架
  • Redis
  • Zookeeper
  • 消息中间件
  • 持久化
  • 算法
  • 网络
  • 系统架构
  • 并发编程
  • 框架
  • 开发杂货
  • 线上排查
  • 技巧备忘
  • 部署指南
  • 版本管理
  • 工作流程
  • 发版流程
  • 友情链接
  • 网站备忘
  • 在线工具
  • 学习
  • 各种云
  • 应用下载

Louis

首页
  • 操作系统初识
  • JAVA基础
  • JVM
  • 开发框架
  • Redis
  • Zookeeper
  • 消息中间件
  • 持久化
  • 算法
  • 网络
  • 系统架构
  • 并发编程
  • 框架
  • 开发杂货
  • 线上排查
  • 技巧备忘
  • 部署指南
  • 版本管理
  • 工作流程
  • 发版流程
  • 友情链接
  • 网站备忘
  • 在线工具
  • 学习
  • 各种云
  • 应用下载
  • 开发杂货

  • 线上排查

    • Jprofiler内存排查
    • CPU飙高排查
      • 1 前言
      • 2 Demo
        • 2.1 代码示例
        • 2.2 IDEA构建jar包
        • 2.3 程序运行
      • 3 排查
        • 3.1 查看cpu情况
        • 3.2 查看进程信息
        • 3.3 将线程ID转为16进制
        • 3.4 线程状态说明
      • 4 使用阿尔萨斯排查
        • 4.1 下载并运行arthas
        • 4.2 找出最忙的线程
    • FULLGC频繁排查
    • 阿尔萨斯(Arthas)
    • 常见开发问题记录
  • 技巧备忘

  • 部署指南

  • 技术应用
  • 线上排查
luoxiaofeng
2022-04-30
目录

CPU飙高排查

# 1 前言

线上应用CPU飙高,使用几个简单命令快速定位问题代码。

# 2 Demo

# 2.1 代码示例

Java代码示例(CPU、内存飙高)
public class TestMemCpu {

    //是否跑消耗内存代码的标记,默认否,即跑消耗Cpu的代码
    private static boolean memConsume = false;

    // 消耗 Cpu 命令
    // java -XX:InitialHeapSize=64m -XX:MaxHeapSize=64m -XX:NewSize=32m -XX:MaxNewSize=32m
    // 消耗内存命令
    // java -XX:InitialHeapSize=4096m -XX:MaxHeapSize=4096m -XX:NewSize=3072m -XX:MaxNewSize=3072m -XX:+UseConcMarkSweepGC -XX:PretenureSizeThreshold=500m -cp "." ResouceManageService -m 1 &

    public static void main(String[] args) {
        final ResouceManageService service = new ResouceManageService();
        int num = 1;
        for (int i = 0; i < args.length; i++) {
            //指定-m表明跑消耗内存,指定-c或不指定为消耗Cpu,
            if ("-c".equals(args[i])) {

            } else if ("-m".equals(args[i])) {
                memConsume = true;
                num = Integer.parseInt(args[i + 1]);
                i++;
            }
        }

        int finalNum = num;
        new Thread(()->{
            if(memConsume){
                service.memConsume(finalNum);
            }else {
                service.cpuConsume();
            }
        }, "thread-louis-100").start();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ResouceManageService {

    //只内存消耗调用这个方法
    @SuppressWarnings("unchecked")
    public void memConsume(int num){

        //执行一个for循环进行新生代内存的申请,共消耗num数量GB
        for(int i=0;i<num * 10;i++){

            @SuppressWarnings("rawtypes")
            Vector v = new Vector();
            byte b1[] = new byte[104857600]; //100M
            v.add(b1);

        }

        //内存消耗申请完后,执行死循环休眠,让JVM一直占用申请到的内存,达到一直占用num数量GB的效果
        while(true){
            try {
                Thread.sleep(3600000l);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //cpu消耗方法
    public void cpuConsume(){
        // 角度的分割
        final double SPLIT = 0.01;
        // 2PI分割的次数,也就是2/0.01个,正好是一周
        final int COUNT = (int) (2 / SPLIT);
        final double PI = Math.PI;
        // 时间间隔
        final int INTERVAL = 200;
        long[] busySpan = new long[COUNT];
        long[] idleSpan = new long[COUNT];
        int half = INTERVAL / 2;
        double radian = 0.0;
        for (int i = 0; i < COUNT; i++) {
            busySpan[i] = (long) (half + (Math.sin(PI * radian) * half));
            idleSpan[i] = INTERVAL - busySpan[i];
            radian += SPLIT;
        }
        long startTime = 0;
        int j = 0;
        while (true) {
            j = j % COUNT;
            startTime = System.currentTimeMillis();
            while (System.currentTimeMillis() - startTime < busySpan[j]);
            try {
                //这里的if控制可以注解掉,让Thread.sleep(idleSpan[j])一直执行。
                //我这里加了if控制是因为希望Cpu一直保存在70%以上工作的效果(小于70不sleep),If注解掉将以正弦曲线的趋势使用Cpu
                if(idleSpan[j]<70){
                    Thread.sleep(idleSpan[j]);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            j++;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

# 2.2 IDEA构建jar包



image-20220712162216963 image-20220712162337955 image-20220712162509953 image-20220712162548167

# 2.3 程序运行

java -XX:InitialHeapSize=64m -XX:MaxHeapSize=64m -XX:NewSize=32m -XX:MaxNewSize=32m -jar mydemo.jar
1

# 3 排查

# 3.1 查看cpu情况

top
1
top

可以看到有个 PID 为 30064 的进程占用CPU过高。

# 3.2 查看进程信息

ps -mp 30064 -o THREAD,tid,time

# 属性介绍:tid代码线程ID,time代表这个线程的已运行时间,30064为对应pid的值。
1
2
3
ps

可以看到 30076 的线程cpu占用率最高。

# 3.3 将线程ID转为16进制

提示

因为jvm运行信息都是16进制,所以,这里需要将10进制转为16进制。

printf "%x\n" 30076

757c
1
2
3
jstack 30064 | grep 757c -A30
1
jstack

定位到问题可能出现在线程 thread-louis-100 。

提示

代码中创建线程时加上命名,可帮助快速定位问题。

# 3.4 线程状态说明

线程的Thread状态 java.lang.Thread.State 内容说明:

一个Thread对象可以有多个状态,在 java.lang.Thread.State 中,总共定义六种状态:

1、NEW

线程刚刚被创建,也就是已经 new 过了,但是还没有调用 start() 方法, jstack 命令不会列出处于此状态的线程信息。

2、RUNNABLE

RUNNABLE这个名字很具有欺骗性,很容易让人误以为处于这个状态的线程正在运行。事实上,这个状态只是表示,线程是可运行的。我们已经无数次提到过,一个单核CPU在同一时刻,只能运行一个线程。

3、BLOCKED

线程处于阻塞状态,正在等待一个monitor lock。通常情况下,是因为本线程与其他线程公用了一个锁。其他在线程正在使用这个锁进入某个synchronized同步方法块或者方法,而本线程进入这个同步代码块也需要这个锁,最终导致本线程处于阻塞状态。

4、WAITING

等待状态,调用以下方法可能会导致一个线程处于等待状态:

例如:对于wait()方法,一个线程处于等待状态,通常是在等待其他线程完成某个操作。

本线程调用某个对象的wait()方法,其他线程处于完成之后,调用同一个对象的notify或者notifyAll()方法。

Object.wait()方法只能够在同步代码块中调用。调用了wait()方法后,会释放锁。

5、TIMED_WAITING

线程等待指定的时间,对于以下方法的调用,可能会导致线程处于这个状态:

Thread.sleep #java.lang.Thread.State: TIMED_WAITING (sleeping)
 
Object.wait 指定超时时间 #java.lang.Thread.State: TIMED_WAITING (on object monitor)
 
Thread.join with timeout
 
LockSupport.parkNanos #java.lang.Thread.State: TIMED_WAITING (parking)
 
LockSupport.parkUntil #java.lang.Thread.State: TIMED_WAITING (parking)
1
2
3
4
5
6
7
8
9

6、TERMINATED

线程终止。

# 4 使用阿尔萨斯排查

# 4.1 下载并运行arthas

curl -O https://arthas.aliyun.com/arthas-boot.jar
1
java -jar arthas-boot.jar
1

image-20220712173418388

# 4.2 找出最忙的线程

thread -n 3
1

image-20220712173548678

image-20220712173659291

#Arthas
Jprofiler内存排查
FULLGC频繁排查

← Jprofiler内存排查 FULLGC频繁排查→

最近更新
01
SpringBoot
10-21
02
Spring
10-20
03
Sentinel
10-14
更多文章>
Copyright © 2022-2023 Louis | 粤ICP备2022060093号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式