Arthas

Arthas thread查看线程信息

2021-05-19  本文已影响0人  晴天哥_王志

系列

开篇

ThreadMXBean的API


public interface ThreadMXBean extends PlatformManagedObject {
    // 返回活动线程的当前数目,包括守护线程和非守护线程。
    public int getThreadCount();

    // 返回自从 Java 虚拟机启动或峰值重置以来峰值活动线程计数。
    public int getPeakThreadCount();

    // 返回自从 Java 虚拟机启动以来创建和启动的线程总数目。 
    public long getTotalStartedThreadCount();

    // 返回活动守护线程的当前数目。
    public int getDaemonThreadCount();

    // 返回活动线程 ID。在返回的数组中包含的某些线程可能在此方法返回时已经终止。
    public long[] getAllThreadIds();

    // 返回指定 id 的不具有堆栈跟踪的线程的线程信息。
    public ThreadInfo getThreadInfo(long id);

    // 返回其 ID 在输出数组 ids 中的每个线程的线程信息,这些线程不具有堆栈跟踪
    public ThreadInfo[] getThreadInfo(long[] ids);

    // 返回指定 id 的线程的线程信息,并带有指定堆栈追踪元素数的堆栈追踪。
    public ThreadInfo getThreadInfo(long id, int maxDepth);

    // 回其 ID 在输入数组 ids 中的每个线程的线程信息,并带有指定堆栈追踪元素数的堆栈追踪。
    public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth);

    // 测试 Java 虚拟机是否支持线程争用监视。
    public boolean isThreadContentionMonitoringSupported();

    // 测试是否启用了线程争用监视。
    public boolean isThreadContentionMonitoringEnabled();

    // 启用或禁用线程争用监视。默认情况下,线程争用监视是被禁用的。
    public void setThreadContentionMonitoringEnabled(boolean enable);

    // 返回当前线程的总 CPU 时间(以毫微秒为单位)。
    public long getCurrentThreadCpuTime();

    // 返回当前线程在用户模式中执行的 CPU 时间(以毫微秒为单位)。
    public long getCurrentThreadUserTime();

    // 返回指定 ID 的线程的总 CPU 时间(以毫微秒为单位)。
    public long getThreadCpuTime(long id);

    // 返回指定 ID 的线程在用户模式中执行的 CPU 时间(以毫微秒为单位)。
    public long getThreadUserTime(long id);

    // 测试 Java 虚拟机实现是否支持任何线程的 CPU 时间测量。
    public boolean isThreadCpuTimeSupported();

    // 测试 Java 虚拟机是否支持当前线程的 CPU 时间测量。
    public boolean isCurrentThreadCpuTimeSupported();

    // 测试是否启用了线程 CPU 时间测量。
    public boolean isThreadCpuTimeEnabled();

    // 启用或禁用线程 CPU 时间测量。此默认值与平台有关。
    public void setThreadCpuTimeEnabled(boolean enable);

    // 找到处于死锁状态(等待获取对象监视器)的线程的周期。
    public long[] findMonitorDeadlockedThreads();

    // 将峰值线程计数重置为当前活动线程的数量。
    public void resetPeakThreadCount();

    // 查找因为等待获得对象监视器或可拥有同步器而处于死锁状态的线程循环。
    public long[] findDeadlockedThreads();

    // 测试 Java 虚拟机是否支持使用对象监视器的监视。
    public boolean isObjectMonitorUsageSupported();

    // 测试 Java 虚拟机是否支持使用 可拥有同步器的监视。
    public boolean isSynchronizerUsageSupported();

    // 返回每个线程的线程信息,线程 ID 位于输入数组 ids 中,带有堆栈跟踪和同步信息。
    public ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, boolean lockedSynchronizers);

    // 返回所有活动线程的线程信息,并带有堆栈跟踪和同步信息。
    public ThreadInfo[] dumpAllThreads(boolean lockedMonitors, boolean lockedSynchronizers);
}

源码解析

public class ThreadCommand extends AnnotatedCommand {
    private static Set<String> states = null;
    private static ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

    public void process(CommandProcess process) {
        ExitStatus exitStatus;
        if (id > 0) {
            // 获取指定的id的线程
            exitStatus = processThread(process);
        } else if (topNBusy != null) {
            // 获取topN的busy的线程
            exitStatus = processTopBusyThreads(process);
        } else if (findMostBlockingThread) {
            // 获取阻塞的线程
            exitStatus = processBlockingThread(process);
        } else {
            // 获取全部的线程
            exitStatus = processAllThreads(process);
        }
        CommandUtils.end(process, exitStatus);
    }
}
  private ExitStatus processThread(CommandProcess process) {
        // 获取线程的指定信息
        ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(new long[]{id}, lockedMonitors, lockedSynchronizers);
        if (threadInfos == null || threadInfos.length < 1 || threadInfos[0] == null) {
            return ExitStatus.failure(1, "thread do not exist! id: " + id);
        }

        process.appendResult(new ThreadModel(threadInfos[0]));
        return ExitStatus.success();
    }
    public static BlockingLockInfo findMostBlockingLock() {
        // 通过threadMXBean.dumpAllThreads返回所有活动线程的线程信息
        ThreadInfo[] infos = threadMXBean.dumpAllThreads(threadMXBean.isObjectMonitorUsageSupported(),
                threadMXBean.isSynchronizerUsageSupported());

        // a map of <LockInfo.getIdentityHashCode, number of thread blocking on this>
        Map<Integer, Integer> blockCountPerLock = new HashMap<Integer, Integer>();
        // a map of <LockInfo.getIdentityHashCode, the thread info that holding this lock
        Map<Integer, ThreadInfo> ownerThreadPerLock = new HashMap<Integer, ThreadInfo>();

        for (ThreadInfo info: infos) {
            if (info == null) {
                continue;
            }

            LockInfo lockInfo = info.getLockInfo();
            if (lockInfo != null) {
                // the current thread is blocked waiting on some condition
                if (blockCountPerLock.get(lockInfo.getIdentityHashCode()) == null) {
                    blockCountPerLock.put(lockInfo.getIdentityHashCode(), 0);
                }
                int blockedCount = blockCountPerLock.get(lockInfo.getIdentityHashCode());
                blockCountPerLock.put(lockInfo.getIdentityHashCode(), blockedCount + 1);
            }

            for (MonitorInfo monitorInfo: info.getLockedMonitors()) {
                // the object monitor currently held by this thread
                if (ownerThreadPerLock.get(monitorInfo.getIdentityHashCode()) == null) {
                    ownerThreadPerLock.put(monitorInfo.getIdentityHashCode(), info);
                }
            }

            for (LockInfo lockedSync: info.getLockedSynchronizers()) {
                // the ownable synchronizer currently held by this thread
                if (ownerThreadPerLock.get(lockedSync.getIdentityHashCode()) == null) {
                    ownerThreadPerLock.put(lockedSync.getIdentityHashCode(), info);
                }
            }
        }

        // find the thread that is holding the lock that blocking the largest number of threads.
        int mostBlockingLock = 0; // System.identityHashCode(null) == 0
        int maxBlockingCount = 0;
        for (Map.Entry<Integer, Integer> entry: blockCountPerLock.entrySet()) {
            if (entry.getValue() > maxBlockingCount && ownerThreadPerLock.get(entry.getKey()) != null) {
                // the lock is explicitly held by anther thread.
                maxBlockingCount = entry.getValue();
                mostBlockingLock = entry.getKey();
            }
        }

        if (mostBlockingLock == 0) {
            // nothing found
            return EMPTY_INFO;
        }

        BlockingLockInfo blockingLockInfo = new BlockingLockInfo();
        blockingLockInfo.setThreadInfo(ownerThreadPerLock.get(mostBlockingLock));
        blockingLockInfo.setLockIdentityHashCode(mostBlockingLock);
        blockingLockInfo.setBlockingThreadCount(blockCountPerLock.get(mostBlockingLock));
        return blockingLockInfo;
    }
    public static ThreadGroup getRoot() {
        ThreadGroup group = Thread.currentThread().getThreadGroup();
        ThreadGroup parent;
        while ((parent = group.getParent()) != null) {
            group = parent;
        }
        return group;
    }


    public static List<ThreadVO> getThreads() {
        ThreadGroup root = getRoot();
        Thread[] threads = new Thread[root.activeCount()];
        while (root.enumerate(threads, true) == threads.length) {
            threads = new Thread[threads.length * 2];
        }
        List<ThreadVO> list = new ArrayList<ThreadVO>(threads.length);
        for (Thread thread : threads) {
            if (thread != null) {
                ThreadVO threadVO = createThreadVO(thread);
                list.add(threadVO);
            }
        }
        return list;
    }
    private ExitStatus processTopBusyThreads(CommandProcess process) {
        ThreadSampler threadSampler = new ThreadSampler();
        // 进行第一次采样
        threadSampler.sample(ThreadUtil.getThreads());
        // 等待一段时间的
        threadSampler.pause(sampleInterval);
        // 进行第二次采样
        List<ThreadVO> threadStats = threadSampler.sample(ThreadUtil.getThreads());

        int limit = Math.min(threadStats.size(), topNBusy);
        List<ThreadVO> topNThreads = threadStats.subList(0, limit);
        List<Long> tids = new ArrayList<Long>(topNThreads.size());
        for (ThreadVO thread : topNThreads) {
            if (thread.getId() > 0) {
                tids.add(thread.getId());
            }
        }
        // 获取线程信息
        ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(ArrayUtils.toPrimitive(tids.toArray(new Long[0])), lockedMonitors, lockedSynchronizers);
        if (tids.size()> 0 && threadInfos == null) {
            return ExitStatus.failure(1, "get top busy threads failed");
        }

        //threadInfo with cpuUsage
        List<BusyThreadInfo> busyThreadInfos = new ArrayList<BusyThreadInfo>(topNThreads.size());
        for (ThreadVO thread : topNThreads) {
            ThreadInfo threadInfo = findThreadInfoById(threadInfos, thread.getId());
            BusyThreadInfo busyThread = new BusyThreadInfo(thread, threadInfo);
            busyThreadInfos.add(busyThread);
        }
        process.appendResult(new ThreadModel(busyThreadInfos));
        return ExitStatus.success();
    }
public class ThreadSampler {

    private static ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    private static HotspotThreadMBean hotspotThreadMBean;
    private static boolean hotspotThreadMBeanEnable = true;

    private Map<ThreadVO, Long> lastCpuTimes = new HashMap<ThreadVO, Long>();

    private long lastSampleTimeNanos;
    private boolean includeInternalThreads = true;


    public List<ThreadVO> sample(Collection<ThreadVO> originThreads) {

        List<ThreadVO> threads = new ArrayList<ThreadVO>(originThreads);

        // Sample CPU
        if (lastCpuTimes.isEmpty()) {
            lastSampleTimeNanos = System.nanoTime();
            for (ThreadVO thread : threads) {
                if (thread.getId() > 0) {
                    long cpu = threadMXBean.getThreadCpuTime(thread.getId());
                    lastCpuTimes.put(thread, cpu);
                    thread.setTime(cpu / 1000000);
                }
            }

            // add internal threads
            Map<String, Long> internalThreadCpuTimes = getInternalThreadCpuTimes();
            if (internalThreadCpuTimes != null) {
                for (Map.Entry<String, Long> entry : internalThreadCpuTimes.entrySet()) {
                    String key = entry.getKey();
                    ThreadVO thread = createThreadVO(key);
                    thread.setTime(entry.getValue() / 1000000);
                    threads.add(thread);
                    lastCpuTimes.put(thread, entry.getValue());
                }
            }

            //sort by time
            Collections.sort(threads, new Comparator<ThreadVO>() {
                @Override
                public int compare(ThreadVO o1, ThreadVO o2) {
                    long l1 = o1.getTime();
                    long l2 = o2.getTime();
                    if (l1 < l2) {
                        return 1;
                    } else if (l1 > l2) {
                        return -1;
                    } else {
                        return 0;
                    }
                }
            });
            return threads;
        }

        // Resample
        long newSampleTimeNanos = System.nanoTime();
        Map<ThreadVO, Long> newCpuTimes = new HashMap<ThreadVO, Long>(threads.size());
        for (ThreadVO thread : threads) {
            if (thread.getId() > 0) {
                long cpu = threadMXBean.getThreadCpuTime(thread.getId());
                newCpuTimes.put(thread, cpu);
            }
        }
        // internal threads
        Map<String, Long> newInternalThreadCpuTimes = getInternalThreadCpuTimes();
        if (newInternalThreadCpuTimes != null) {
            for (Map.Entry<String, Long> entry : newInternalThreadCpuTimes.entrySet()) {
                ThreadVO threadVO = createThreadVO(entry.getKey());
                threads.add(threadVO);
                newCpuTimes.put(threadVO, entry.getValue());
            }
        }

        // Compute delta time
        final Map<ThreadVO, Long> deltas = new HashMap<ThreadVO, Long>(threads.size());
        for (ThreadVO thread : newCpuTimes.keySet()) {
            Long t = lastCpuTimes.get(thread);
            if (t == null) {
                t = 0L;
            }
            long time1 = t;
            long time2 = newCpuTimes.get(thread);
            if (time1 == -1) {
                time1 = time2;
            } else if (time2 == -1) {
                time2 = time1;
            }
            long delta = time2 - time1;
            deltas.put(thread, delta);
        }

        long sampleIntervalNanos = newSampleTimeNanos - lastSampleTimeNanos;

        // Compute cpu usage
        final HashMap<ThreadVO, Double> cpuUsages = new HashMap<ThreadVO, Double>(threads.size());
        for (ThreadVO thread : threads) {
            double cpu = sampleIntervalNanos == 0 ? 0 : (deltas.get(thread) * 10000 / sampleIntervalNanos / 100.0);
            cpuUsages.put(thread, cpu);
        }

        // Sort by CPU time : should be a rendering hint...
        Collections.sort(threads, new Comparator<ThreadVO>() {
            public int compare(ThreadVO o1, ThreadVO o2) {
                long l1 = deltas.get(o1);
                long l2 = deltas.get(o2);
                if (l1 < l2) {
                    return 1;
                } else if (l1 > l2) {
                    return -1;
                } else {
                    return 0;
                }
            }
        });

        for (ThreadVO thread : threads) {
            //nanos to mills
            long timeMills = newCpuTimes.get(thread) / 1000000;
            long deltaTime = deltas.get(thread) / 1000000;
            double cpu = cpuUsages.get(thread);

            thread.setCpu(cpu);
            thread.setTime(timeMills);
            thread.setDeltaTime(deltaTime);
        }
        lastCpuTimes = newCpuTimes;
        lastSampleTimeNanos = newSampleTimeNanos;

        return threads;
    }

    private Map<String, Long> getInternalThreadCpuTimes() {
        if (hotspotThreadMBeanEnable && includeInternalThreads) {
            try {
                if (hotspotThreadMBean == null) {
                    hotspotThreadMBean = ManagementFactoryHelper.getHotspotThreadMBean();
                }
                return hotspotThreadMBean.getInternalThreadCpuTimes();
            } catch (Throwable e) {
                //ignore ex
                hotspotThreadMBeanEnable = false;
            }
        }
        return null;
    }
}
上一篇下一篇

猜你喜欢

热点阅读