Skip to content

Commit

Permalink
#97 整理options项,同时讲Thread的计算拆分出去
Browse files Browse the repository at this point in the history
  • Loading branch information
calvin1978 committed Sep 6, 2018
1 parent a54c6cf commit 671b4d4
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 223 deletions.
2 changes: 1 addition & 1 deletion vjmxcli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<groupId>com.vip.vjtools</groupId>
<artifactId>vjmxcli</artifactId>
<name>vjmxcli</name>
<version>黑店老板娘,你们买的那个生发液,在公司里也有防脱洗发水哦,你自己有咩有试过</version>
<version>1.0.6-SNAPSHOT</version>
<description>jmx command line client</description>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@ public void handleCommand(String command) throws Exception {
}

private void printStacktrace(String command) throws IOException {
if (app.view.collectingData) {
tty.println(" Please wait for top threads displayed");
return;
}

app.preventFlush();
String pidStr;
if (command.length() == 1) {
Expand Down Expand Up @@ -134,7 +129,7 @@ private void changeDisplayMode() {
tty.println(" Nothing be changed");
} else {
if (app.view.mode.isCpuMode != detailMode.isCpuMode) {
app.view.cleanupThreadsHistory();
app.view.switchCpuAndMemory();
app.view.mode = detailMode;
tty.println(" Display mode changed to " + app.view.mode + " for next flush");
app.interruptSleep();
Expand Down
212 changes: 212 additions & 0 deletions vjtop/src/main/java/com/vip/vjtools/vjtop/TopThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
package com.vip.vjtools.vjtop;

import java.io.IOException;
import java.lang.management.ThreadInfo;

import com.vip.vjtools.vjtop.VMDetailView.DetailMode;
import com.vip.vjtools.vjtop.util.LongObjectHashMap;
import com.vip.vjtools.vjtop.util.LongObjectMap;
import com.vip.vjtools.vjtop.util.Utils;

public class TopThread {
private VMInfo vmInfo;

private long[] topTidArray;

private LongObjectMap<Long> lastThreadCpuTotalTimes = new LongObjectHashMap<Long>();
private LongObjectMap<Long> lastThreadSysCpuTotalTimes = new LongObjectHashMap<Long>();
private LongObjectMap<Long> lastThreadMemoryTotalBytes = new LongObjectHashMap<Long>();

public TopThread(VMInfo vmInfo) {
this.vmInfo = vmInfo;
}

public TopCpuResult topCpuThreads(DetailMode mode, int threadLimit) throws IOException {

long tids[] = vmInfo.getThreadMXBean().getAllThreadIds();

TopCpuResult result = new TopCpuResult();

int mapSize = tids.length * 2;
result.threadCpuTotalTimes = new LongObjectHashMap<Long>(mapSize);
result.threadCpuDeltaTimes = new LongObjectHashMap<>(mapSize);
result.threadSysCpuTotalTimes = new LongObjectHashMap<>(mapSize);
result.threadSysCpuDeltaTimes = new LongObjectHashMap<>(mapSize);

// 批量获取CPU times,性能大幅提高。
// 两次获取之间有间隔,在低流量下可能造成负数
long[] threadCpuTotalTimeArray = vmInfo.getThreadMXBean().getThreadCpuTime(tids);
long[] threadUserCpuTotalTimeArray = vmInfo.getThreadMXBean().getThreadUserTime(tids);

// 过滤CPU占用太少的线程,每秒0.05%CPU (0.5ms cpu time)
long minDeltaCpuTime = (vmInfo.upTimeMills.delta * Utils.NANOS_TO_MILLS / 2000);

// 计算本次CPU Time
// 此算法第一次不会显示任何数据,保证每次显示都只显示区间内数据
for (int i = 0; i < tids.length; i++) {
long tid = tids[i];
Long threadCpuTotalTime = threadCpuTotalTimeArray[i];
result.threadCpuTotalTimes.put(tid, threadCpuTotalTime);

Long lastTime = lastThreadCpuTotalTimes.get(tid);
if (lastTime != null) {
Long deltaThreadCpuTime = threadCpuTotalTime - lastTime;
if (deltaThreadCpuTime >= minDeltaCpuTime) {
result.threadCpuDeltaTimes.put(tid, deltaThreadCpuTime);
result.deltaAllThreadCpu += deltaThreadCpuTime;
}
}
}

// 计算本次SYSCPU Time
for (int i = 0; i < tids.length; i++) {
long tid = tids[i];
// 因为totalTime 与 userTime 的获取时间有先后,实际sys接近0时,后取的userTime可能比前一时刻的totalTime高,计算出来的sysTime可为负数
Long threadSysCpuTotalTime = Math.max(0, threadCpuTotalTimeArray[i] - threadUserCpuTotalTimeArray[i]);
result.threadSysCpuTotalTimes.put(tid, threadSysCpuTotalTime);

Long lastTime = lastThreadSysCpuTotalTimes.get(tid);
if (lastTime != null) {
Long deltaThreadSysCpuTime = Math.max(0, threadSysCpuTotalTime - lastTime);
if (deltaThreadSysCpuTime >= minDeltaCpuTime) {
result.threadSysCpuDeltaTimes.put(tid, deltaThreadSysCpuTime);
result.deltaAllThreadSysCpu += deltaThreadSysCpuTime;
}
}
}

if (lastThreadCpuTotalTimes.isEmpty()) {
lastThreadCpuTotalTimes = result.threadCpuTotalTimes;
lastThreadSysCpuTotalTimes = result.threadSysCpuTotalTimes;
result.ready = false;
return result;
}

// 按不同类型排序,过滤
if (mode == DetailMode.cpu) {
topTidArray = Utils.sortAndFilterThreadIdsByValue(result.threadCpuDeltaTimes, threadLimit);
result.noteableThreads = result.threadCpuDeltaTimes.size();
} else if (mode == DetailMode.syscpu) {
topTidArray = Utils.sortAndFilterThreadIdsByValue(result.threadSysCpuDeltaTimes, threadLimit);
result.noteableThreads = result.threadSysCpuDeltaTimes.size();
} else if (mode == DetailMode.totalcpu) {
topTidArray = Utils.sortAndFilterThreadIdsByValue(result.threadCpuTotalTimes, threadLimit);
result.noteableThreads = result.threadCpuTotalTimes.size();
} else if (mode == DetailMode.totalsyscpu) {
topTidArray = Utils.sortAndFilterThreadIdsByValue(result.threadSysCpuTotalTimes, threadLimit);
result.noteableThreads = result.threadSysCpuTotalTimes.size();
} else {
throw new RuntimeException("unkown mode:" + mode);
}

if (result.noteableThreads == 0) {
System.out.printf("%n -Every thread use cpu lower than 0.05%%-%n");
}

// 获得threadInfo
result.threadInfos = vmInfo.getThreadMXBean().getThreadInfo(topTidArray);

lastThreadCpuTotalTimes = result.threadCpuTotalTimes;
lastThreadSysCpuTotalTimes = result.threadSysCpuTotalTimes;
return result;
}


public TopMemoryResult topMemoryThreads(DetailMode mode, int threadLimit) throws IOException {
long tids[] = vmInfo.getThreadMXBean().getAllThreadIds();
TopMemoryResult result = new TopMemoryResult();

int mapSize = tids.length * 2;
result.threadMemoryTotalBytesMap = new LongObjectHashMap<Long>(mapSize);
result.threadMemoryDeltaBytesMap = new LongObjectHashMap<Long>(mapSize);

// 批量获取内存分配
long[] threadMemoryTotalBytesArray = vmInfo.getThreadMXBean().getThreadAllocatedBytes(tids);

// 过滤太少的线程,每秒小于1k
long minDeltaMemory = vmInfo.upTimeMills.delta * 1024 / 1000;

// 此算法第一次不会显示任何数据,保证每次显示都只显示区间内数据
for (int i = 0; i < tids.length; i++) {
long tid = tids[i];
Long threadMemoryTotalBytes = threadMemoryTotalBytesArray[i];
result.threadMemoryTotalBytesMap.put(tid, threadMemoryTotalBytes);
result.totalAllThreadBytes += threadMemoryTotalBytes;

Long threadMemoryDeltaBytes = 0L;
Long lastBytes = lastThreadMemoryTotalBytes.get(tid);

if (lastBytes != null) {
threadMemoryDeltaBytes = threadMemoryTotalBytes - lastBytes;
if (threadMemoryDeltaBytes >= minDeltaMemory) {
result.threadMemoryDeltaBytesMap.put(tid, threadMemoryDeltaBytes);
result.deltaAllThreadBytes += threadMemoryDeltaBytes;
}
}
}

if (lastThreadMemoryTotalBytes.isEmpty()) {
lastThreadMemoryTotalBytes = result.threadMemoryTotalBytesMap;
result.ready = false;
return result;
}

// 线程排序
long[] topTidArray;
if (mode == DetailMode.memory) {
topTidArray = Utils.sortAndFilterThreadIdsByValue(result.threadMemoryDeltaBytesMap, threadLimit);
result.noteableThreads = result.threadMemoryDeltaBytesMap.size();
} else {
topTidArray = Utils.sortAndFilterThreadIdsByValue(result.threadMemoryTotalBytesMap, threadLimit);
result.noteableThreads = result.threadMemoryTotalBytesMap.size();
}

if (result.noteableThreads == 0) {
System.out.printf("%n -Every thread allocate memory slower than 1k/s-%n");
}

result.threadInfos = vmInfo.getThreadMXBean().getThreadInfo(topTidArray);

lastThreadMemoryTotalBytes = result.threadMemoryTotalBytesMap;

return result;
}

public ThreadInfo[] getTopThreadInfo() throws IOException {
return vmInfo.getThreadMXBean().getThreadInfo(topTidArray, 20);
}

public void cleanupThreadsHistory() {
this.lastThreadCpuTotalTimes.clear();
this.lastThreadSysCpuTotalTimes.clear();
this.lastThreadMemoryTotalBytes.clear();
}

public static class TopCpuResult {
public ThreadInfo[] threadInfos;
public long noteableThreads = 0;

public long deltaAllThreadCpu = 0;
public long deltaAllThreadSysCpu = 0;

public LongObjectMap<Long> threadCpuTotalTimes;
public LongObjectMap<Long> threadCpuDeltaTimes;
public LongObjectMap<Long> threadSysCpuTotalTimes;
public LongObjectMap<Long> threadSysCpuDeltaTimes;

public boolean ready = true;
}

public static class TopMemoryResult {
public ThreadInfo[] threadInfos;
public long noteableThreads = 0;

public long deltaAllThreadBytes = 0;
public long totalAllThreadBytes = 0;

public LongObjectMap<Long> threadMemoryTotalBytesMap;
public LongObjectMap<Long> threadMemoryDeltaBytesMap;

public boolean ready = true;
}
}
47 changes: 25 additions & 22 deletions vjtop/src/main/java/com/vip/vjtools/vjtop/VJTop.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.io.PrintStream;
import java.util.Arrays;

import com.vip.vjtools.vjtop.VMDetailView.DetailMode;
import com.vip.vjtools.vjtop.VMInfo.VMInfoState;
import com.vip.vjtools.vjtop.util.Formats;
import com.vip.vjtools.vjtop.util.Utils;
Expand Down Expand Up @@ -50,13 +51,26 @@ private static OptionParser createOptionParser() {
"JMX url like 127.0.0.1:7001 when VM attach is not work").withRequiredArg().ofType(String.class);

// detail mode
parser.accepts("cpu",
"default mode in detail view, display thread cpu usage and sort by thread delta cpu time ");
parser.accepts("totalcpu", "display thread cpu usage and sort by total cpu time");
parser.accepts("syscpu", "display thread cpu usage and sort by delta syscpu time");
parser.accepts("totalsyscpu", "display thread cpu usage and sort by total syscpu time");
parser.accepts("memory", "display thread memory allocated and sort by delta");
parser.accepts("totalmemory", "display thread memory allocated and sort by total");
parser.acceptsAll(Arrays.asList(new String[] { "m", "mode" }),
"number of thread display mode: \n"
+ " 1.cpu(default): display thread cpu usage and sort by its delta cpu time\n"
+ " 2.syscpu: display thread cpu usage and sort by delta syscpu time\n"
+ " 3.total cpu: display thread cpu usage and sort by total cpu time\n"
+ " 4.total syscpu: display thread cpu usage and sort by total syscpu time\n"
+ " 5.memory: display thread memory allocated and sort by delta\n"
+ " 6.total memory: display thread memory allocated and sort by total")
.withRequiredArg().ofType(Integer.class);

parser.acceptsAll(Arrays.asList(new String[] { "o", "output" }),
"output format: \n" + " console(default): console with warning and flush ascii code\n"
+ " cleanconsole: console without warning and flush ascii code\n"
+ " text: plain text like /proc/status for 3rd tools\n")
.withRequiredArg().ofType(String.class);

parser.acceptsAll(Arrays.asList(new String[] { "c", "content" }),
"output format: \n"
+ " all(default): jvm info and theads info\n jvm: only jvm info\n thread: only thread info\n")
.withRequiredArg().ofType(String.class);

return parser;
}
Expand Down Expand Up @@ -153,11 +167,7 @@ private void run(VMDetailView view) throws Exception {
int iterations = 0;
while (!view.shouldExit()) {
waitForInput();

Formats.clearTerminal();

view.printView();

if (view.shouldExit()) {
break;
}
Expand All @@ -173,7 +183,7 @@ private void run(VMDetailView view) throws Exception {

iterations++;
sleepStartTime = System.currentTimeMillis();
Utils.sleep(sleepSeconds * 1000);
Utils.sleep(sleepSeconds * 1000L);
}
System.out.println("");
System.out.flush();
Expand All @@ -188,16 +198,9 @@ private void run(VMDetailView view) throws Exception {

private static VMDetailView.DetailMode parseDisplayMode(OptionSet optionSet) {
VMDetailView.DetailMode displayMode = VMDetailView.DetailMode.cpu;
if (optionSet.has("memory")) {
displayMode = VMDetailView.DetailMode.memory;
} else if (optionSet.has("totalmemory")) {
displayMode = VMDetailView.DetailMode.totalmemory;
} else if (optionSet.has("totalcpu")) {
displayMode = VMDetailView.DetailMode.totalcpu;
} else if (optionSet.has("syscpu")) {
displayMode = VMDetailView.DetailMode.syscpu;
} else if (optionSet.has("totalsyscpu")) {
displayMode = VMDetailView.DetailMode.totalsyscpu;
if (optionSet.hasArgument("mode")) {
Integer mode = (Integer) optionSet.valueOf("mode");
displayMode = DetailMode.parse(mode.toString());
}
return displayMode;
}
Expand Down
Loading

0 comments on commit 671b4d4

Please sign in to comment.