系列
開(kāi)篇
- Arthas提供thread命令獲取當(dāng)前thread的信息套啤,包括查詢(xún)指定最忙的前N個(gè)線程并打印堆棧,找出當(dāng)前阻塞其他線程的線程,顯示所有匹配的線程等棵磷。
- 線程相關(guān)信息的獲取是通過(guò)ThreadMXBean來(lái)進(jìn)行獲取太防。
ThreadMXBean的API
- ThreadMXBean是Java 虛擬機(jī)線程系統(tǒng)的管理接口谈撒。Java 虛擬機(jī)具有此接口的實(shí)現(xiàn)類(lèi)的單一實(shí)例蚕捉。
- 實(shí)現(xiàn)此接口的實(shí)例是一個(gè) MXBean律胀,可以通過(guò)調(diào)用ManagementFactory的getThreadMXBean() 方法或從平臺(tái) MBeanServer 方法獲得它贝乎。
public interface ThreadMXBean extends PlatformManagedObject {
// 返回活動(dòng)線程的當(dāng)前數(shù)目情连,包括守護(hù)線程和非守護(hù)線程。
public int getThreadCount();
// 返回自從 Java 虛擬機(jī)啟動(dòng)或峰值重置以來(lái)峰值活動(dòng)線程計(jì)數(shù)览效。
public int getPeakThreadCount();
// 返回自從 Java 虛擬機(jī)啟動(dòng)以來(lái)創(chuàng)建和啟動(dòng)的線程總數(shù)目却舀。
public long getTotalStartedThreadCount();
// 返回活動(dòng)守護(hù)線程的當(dāng)前數(shù)目虫几。
public int getDaemonThreadCount();
// 返回活動(dòng)線程 ID。在返回的數(shù)組中包含的某些線程可能在此方法返回時(shí)已經(jīng)終止挽拔。
public long[] getAllThreadIds();
// 返回指定 id 的不具有堆棧跟蹤的線程的線程信息辆脸。
public ThreadInfo getThreadInfo(long id);
// 返回其 ID 在輸出數(shù)組 ids 中的每個(gè)線程的線程信息,這些線程不具有堆棧跟蹤
public ThreadInfo[] getThreadInfo(long[] ids);
// 返回指定 id 的線程的線程信息螃诅,并帶有指定堆棧追蹤元素?cái)?shù)的堆棧追蹤啡氢。
public ThreadInfo getThreadInfo(long id, int maxDepth);
// 回其 ID 在輸入數(shù)組 ids 中的每個(gè)線程的線程信息,并帶有指定堆棧追蹤元素?cái)?shù)的堆棧追蹤术裸。
public ThreadInfo[] getThreadInfo(long[] ids, int maxDepth);
// 測(cè)試 Java 虛擬機(jī)是否支持線程爭(zhēng)用監(jiān)視倘是。
public boolean isThreadContentionMonitoringSupported();
// 測(cè)試是否啟用了線程爭(zhēng)用監(jiān)視。
public boolean isThreadContentionMonitoringEnabled();
// 啟用或禁用線程爭(zhēng)用監(jiān)視袭艺。默認(rèn)情況下搀崭,線程爭(zhēng)用監(jiān)視是被禁用的。
public void setThreadContentionMonitoringEnabled(boolean enable);
// 返回當(dāng)前線程的總 CPU 時(shí)間(以毫微秒為單位)猾编。
public long getCurrentThreadCpuTime();
// 返回當(dāng)前線程在用戶(hù)模式中執(zhí)行的 CPU 時(shí)間(以毫微秒為單位)瘤睹。
public long getCurrentThreadUserTime();
// 返回指定 ID 的線程的總 CPU 時(shí)間(以毫微秒為單位)。
public long getThreadCpuTime(long id);
// 返回指定 ID 的線程在用戶(hù)模式中執(zhí)行的 CPU 時(shí)間(以毫微秒為單位)袍镀。
public long getThreadUserTime(long id);
// 測(cè)試 Java 虛擬機(jī)實(shí)現(xiàn)是否支持任何線程的 CPU 時(shí)間測(cè)量默蚌。
public boolean isThreadCpuTimeSupported();
// 測(cè)試 Java 虛擬機(jī)是否支持當(dāng)前線程的 CPU 時(shí)間測(cè)量。
public boolean isCurrentThreadCpuTimeSupported();
// 測(cè)試是否啟用了線程 CPU 時(shí)間測(cè)量苇羡。
public boolean isThreadCpuTimeEnabled();
// 啟用或禁用線程 CPU 時(shí)間測(cè)量绸吸。此默認(rèn)值與平臺(tái)有關(guān)。
public void setThreadCpuTimeEnabled(boolean enable);
// 找到處于死鎖狀態(tài)(等待獲取對(duì)象監(jiān)視器)的線程的周期设江。
public long[] findMonitorDeadlockedThreads();
// 將峰值線程計(jì)數(shù)重置為當(dāng)前活動(dòng)線程的數(shù)量锦茁。
public void resetPeakThreadCount();
// 查找因?yàn)榈却@得對(duì)象監(jiān)視器或可擁有同步器而處于死鎖狀態(tài)的線程循環(huán)。
public long[] findDeadlockedThreads();
// 測(cè)試 Java 虛擬機(jī)是否支持使用對(duì)象監(jiān)視器的監(jiān)視叉存。
public boolean isObjectMonitorUsageSupported();
// 測(cè)試 Java 虛擬機(jī)是否支持使用 可擁有同步器的監(jiān)視码俩。
public boolean isSynchronizerUsageSupported();
// 返回每個(gè)線程的線程信息,線程 ID 位于輸入數(shù)組 ids 中歼捏,帶有堆棧跟蹤和同步信息稿存。
public ThreadInfo[] getThreadInfo(long[] ids, boolean lockedMonitors, boolean lockedSynchronizers);
// 返回所有活動(dòng)線程的線程信息,并帶有堆棧跟蹤和同步信息瞳秽。
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);
}
}
- 支持獲取指定線程信息瓣履、topN的busy線程、阻塞線程练俐、全部線程信息等袖迎。
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();
}
- 通過(guò)threadMXBean的getThreadInfo獲取指定線程id的線程信息。
public static BlockingLockInfo findMostBlockingLock() {
// 通過(guò)threadMXBean.dumpAllThreads返回所有活動(dòng)線程的線程信息
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;
}
- 找出當(dāng)前阻塞其他線程的線程,通過(guò)線程的LockInfo信息來(lái)統(tǒng)計(jì)燕锥。
- 通過(guò)threadMXBean.dumpAllThreads返回所有活動(dòng)線程的線程信息辜贵。
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;
}
- 通過(guò)當(dāng)前線程所屬的線程group來(lái)找到線程group的根。
- 通過(guò)ThreadGroup的enumerate來(lái)獲取當(dāng)前所有的線程归形。
private ExitStatus processTopBusyThreads(CommandProcess process) {
ThreadSampler threadSampler = new ThreadSampler();
// 進(jìn)行第一次采樣
threadSampler.sample(ThreadUtil.getThreads());
// 等待一段時(shí)間的
threadSampler.pause(sampleInterval);
// 進(jìn)行第二次采樣
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();
}
- 通過(guò)兩次采樣來(lái)計(jì)算cpu消耗最高的線程池托慨。
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;
}
}
- 通過(guò)threadMXBean.getThreadCpuTime獲取cpu消耗進(jìn)行據(jù)算。