arthas icon indicating copy to clipboard operation
arthas copied to clipboard

新增threadpool指令,用来监控线程池核心线程数、最大线程数、当前繁忙线程数、当前队列堆积数,同时输出调用栈来帮助定位线程池

Open huangjIT opened this issue 3 years ago • 8 comments

压测时,经常需要关注线程池的情况,所以增加这个命令来展示线程池关键信息。 输出参数如下: stackInfo:由于线程池没有名字,所以侧面通过execute方法调用栈信息来判断是那个业务线程池 corePoolSize:配置的核心线程数 maximumPoolSize:配置的最大线程数 activeThreadCount:当前繁忙线程数 currentSizeOfWorkQueue:当前队列堆积数

tomcat线程池: image

dubbo线程池: 20210711185513

支持指令: threadpool -n :输出指定个数的线程池信息,优先按繁忙线程数排序,其次按队列堆积数排序,最后按最大线程数排序,默认输出全部线程池信息,如:threadpool -n 5 表示指定输出前5个 threadpool -sd : 输出指定深度的调用栈信息,当输出的栈信息无法判断是哪个业务线程池时,可以调大这个参数,负数表示输出全部调用栈信息,该调用栈指java.util.concurrent.ThreadPoolExecutor的execute方法调用栈,已经过滤了arthas增强类的调用栈信息,默认是2行,如:threadpool -sd 5 指定输出5行调用栈信息 threadpool -d:指令执行总时长(ms),记录指定毫秒数内触发过execute方法的线程池的信息,默认1000毫秒,在指定时间内会按照指定的频率(由threadpool -i指定,默认200ms)采集线程池的【当前繁忙线程数】和【队列堆积数】数据,最后输出平均值,threadpool -d 3000 表示采样总时长3000毫秒 threadpool -i:采样频率(ms),默认200ms,按指定频率采集线程池的【当前繁忙线程数】和【队列堆积数】数据,最后输出平均值,如:threadpool -i 200 采集频率是200ms

实现原理:增强java.util.concurrent.ThreadPoolExecutor类的execute方法,在有任务提交时,将线程池对象放入concurrentHashMap中(会先后两次通过无锁的get来判断是否已经加入到map中,只有不存在时才通过有锁的put加入map,减少性能影响),通过timer定时任务根据指定频率采集线程池信息,最后输出平均值

关注点:由于增强的是jvm自带的系统类,所以开启了GlobalOptions.isUnsafe标识,在命令结束后会关闭该标识

huangjIT avatar Jul 11 '21 11:07 huangjIT

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


huangjIT seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

CLAassistant avatar Jul 11 '21 11:07 CLAassistant

这个可以给出具体的demo不?我测试了下,好像没获取到用户的栈。

hengyunabc avatar Jul 13 '21 03:07 hengyunabc

这个可以给出具体的demo不?我测试了下,好像没获取到用户的栈。

package com.hj;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author HJ
 * @date 2021-05-26
 **/
public class ThreadPoolTest {

    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3,3,5L, TimeUnit.MINUTES, new LinkedBlockingQueue<>());
        for(int i=0;i<1000000;i++){
            // 模拟调用,这地方需要暂停下,如果不暂停,等启动arthas时,所有任务已经提交了
            // 那么在threadpool监控期间,execute方法就不会再被触发,导致无法采集到线程池信息
            Thread.sleep(10);
            executor.submit(new MyRunnable(i));
        }
    }

    public static class MyRunnable implements Runnable{
        private int num;

        public MyRunnable(int num) {
            this.num = num;
        }

        @Override
        public void run() {
            System.out.println(num);
            try {
                // 模拟业务耗时,设置3个线程跑的话,10毫秒提交一个任务,RT>30会出现堆积
                Thread.sleep(35L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

huangjIT avatar Jul 13 '21 04:07 huangjIT

新增了按照指定采样频率来采集线程池数据,最后输出平均值的逻辑,这样能最大程度反应采样期间线程池的情况,新增了threadpool -d 命令来指定 采样总时长,threadpool -i 来指定采样频率,会在指令执行期间按指定频率采集线程池的繁忙线程数和队列堆积数,最后输出平均值

huangjIT avatar Jul 13 '21 15:07 huangjIT

老哥 这个功能merge到master了吗?

shangguansb avatar Nov 17 '21 12:11 shangguansb

用vmtool --action getInstances -className java.util.concurrent.ThreadPoolExecutor也能拿到线程池信息,如果目的是采集希望能够对统计做得更直观一点,比如采集前与采集后的对比

aresLD avatar Dec 09 '21 02:12 aresLD

一年多了还没合到master吗

banana-boy484 avatar Dec 29 '22 07:12 banana-boy484

ping

JackyYangPassion avatar Aug 23 '23 09:08 JackyYangPassion