diff --git a/vjtop/pom.xml b/vjtop/pom.xml index bf3a7dfb..8fa550e2 100644 --- a/vjtop/pom.xml +++ b/vjtop/pom.xml @@ -25,13 +25,13 @@ jopt-simple 4.9 - + junit junit @@ -145,6 +145,31 @@ + + + + jdk9 + + [1.9,) + + + + + default-jdk + + (,1.8] + + + + com.sun + tools + ${java.version} + system + ${toolsjar} + + + + diff --git a/vjtop/src/main/assembly/vjtop.sh b/vjtop/src/main/assembly/vjtop.sh index 2a5602f0..6741b955 100644 --- a/vjtop/src/main/assembly/vjtop.sh +++ b/vjtop/src/main/assembly/vjtop.sh @@ -9,16 +9,61 @@ if [ ! -d "$JAVA_HOME" ] ; then exit 1 fi -TOOLSJAR="$JAVA_HOME/lib/tools.jar" -if [ ! -f "$TOOLSJAR" ] ; then - echo "$TOOLSJAR doesn't exist" >&2 - exit 1 +# returns the JDK version. +# 8 for 1.8.0_nn, 9 for 9-ea etc, and "no_java" for undetected +GET_JDK_VERSION() { + local result + local java_cmd + if [[ -n $(type -p java) ]] + then + java_cmd=java + elif [[ (-n "$JAVA_HOME") && (-x "$JAVA_HOME/bin/java") ]] + then + java_cmd="$JAVA_HOME/bin/java" + fi + local IFS=$'\n' + # remove \r for Cygwin + local lines=$("$java_cmd" -Xms32M -Xmx32M -version 2>&1 | tr '\r' '\n') + if [[ -z $java_cmd ]] + then + result=no_java + else + for line in $lines; do + if [[ (-z $result) && ($line = *"version \""*) ]] + then + local ver=$(echo $line | sed -e 's/.*version "\(.*\)"\(.*\)/\1/; 1q') + # on macOS, sed doesn't support '?' + if [[ $ver = "1."* ]] + then + result=$(echo $ver | sed -e 's/1\.\([0-9]*\)\(.*\)/\1/; 1q') + else + result=$(echo $ver | sed -e 's/\([0-9]*\)\(.*\)/\1/; 1q') + fi + fi + done + fi + echo "$result" +} + +JDK_VERSION=$(GET_JDK_VERSION) +echo "JDK_VERSION : $JDK_VERSION" + +# jdk 8 and before +if [[ $JDK_VERSION -le 8 ]]; then + TOOLSJAR="$JAVA_HOME/lib/tools.jar" + if [ ! -f "$TOOLSJAR" ] ; then + echo "$TOOLSJAR doesn't exist" >&2 + exit 1 + fi + JAVA_OPTS="-Xms256m -Xmx256m -XX:NewRatio=1 -Xss256k -XX:+UseSerialGC -XX:CICompilerCount=2 -Xverify:none -XX:AutoBoxCacheMax=20000" +else + # jdk 9 or later + JAVA_OPTS="-Xms256m -Xmx256m -XX:NewRatio=1 -Xss256k -XX:+UseSerialGC -XX:CICompilerCount=2 -XX:AutoBoxCacheMax=20000" fi -DIR=$( cd $(dirname $0) ; pwd -P ) -JAVA_OPTS="-Xms256m -Xmx256m -XX:NewRatio=1 -Xss256k -XX:+UseSerialGC -XX:CICompilerCount=2 -Xverify:none -XX:AutoBoxCacheMax=20000" +DIR=$( cd $(dirname $0) ; pwd -P ) "$JAVA_HOME"/bin/java $JAVA_OPTS -cp "$DIR/vjtop.jar:$TOOLSJAR" com.vip.vjtools.vjtop.VJTop "$@" exit $? diff --git a/vjtop/src/main/java/com/vip/vjtools/vjtop/VMDetailView.java b/vjtop/src/main/java/com/vip/vjtools/vjtop/VMDetailView.java index 2a53ee4a..a65d5114 100755 --- a/vjtop/src/main/java/com/vip/vjtools/vjtop/VMDetailView.java +++ b/vjtop/src/main/java/com/vip/vjtools/vjtop/VMDetailView.java @@ -157,12 +157,16 @@ private void printJvmInfoAsConsole() { Formats.toColor(vmInfo.classLoaded.current, warning.loadClass), vmInfo.classUnLoaded, Formats.toColor(vmInfo.classLoaded.delta, warning.newClass)); - System.out.printf(" HEAP: %s eden, %s sur, %s old%n", Formats.formatUsage(vmInfo.eden), - Formats.formatUsage(vmInfo.sur), Formats.formatUsageWithColor(vmInfo.old, warning.old)); + if (vmInfo.ygcStrategy.equals("ZGC")) { + System.out.printf(" HEAP: %s%n", Formats.formatUsage(vmInfo.eden)); + } else { + System.out.printf(" HEAP: %s eden, %s sur, %s old%n", Formats.formatUsage(vmInfo.eden), + Formats.formatUsage(vmInfo.sur), Formats.formatUsageWithColor(vmInfo.old, warning.old)); + } System.out.printf(" NON-HEAP: %s %s, %s codeCache", Formats.formatUsageWithColor(vmInfo.perm, warning.perm), vmInfo.permGenName, Formats.formatUsageWithColor(vmInfo.codeCache, warning.codeCache)); - if (vmInfo.jvmMajorVersion >= 8) { + if (vmInfo.jvmMajorVersion >= 8 && !vmInfo.ygcStrategy.equals("ZGC")) { System.out.printf(", %s ccs", Formats.formatUsage(vmInfo.ccs)); } System.out.println(""); @@ -172,13 +176,21 @@ private void printJvmInfoAsConsole() { Formats.toMB(vmInfo.direct.max), Formats.toMB(vmInfo.map.used), Formats.toMB(vmInfo.map.committed), vmInfo.map.max, Formats.toMB(vmInfo.threadStackSize * vmInfo.threadActive)); + // gc strategy + System.out.printf(" GC-STRATEGY: %s / %s%n", vmInfo.ygcStrategy, vmInfo.fullgcStrategy); + // gc count long ygcCount = vmInfo.ygcCount.delta; long ygcTime = vmInfo.ygcTimeMills.delta; long avgYgcTime = ygcCount == 0 ? 0 : ygcTime / ygcCount; long fgcCount = vmInfo.fullgcCount.delta; - System.out.printf(" GC: %s/%sms/%sms ygc, %s/%dms fgc", Formats.toColor(ygcCount, warning.ygcCount), - Formats.toColor(ygcTime, warning.ygcTime), Formats.toColor(avgYgcTime, warning.ygcAvgTime), - Formats.toColor(fgcCount, warning.fullgcCount), vmInfo.fullgcTimeMills.delta); + if (vmInfo.ygcStrategy.equals("ZGC")) { + System.out.printf(" GC: %s/%sms/%sms zgc", Formats.toColor(ygcCount, warning.ygcCount), + Formats.toColor(ygcTime, warning.ygcTime), Formats.toColor(avgYgcTime, warning.ygcAvgTime)); + } else { + System.out.printf(" GC: %s/%sms/%sms ygc, %s/%dms fgc", Formats.toColor(ygcCount, warning.ygcCount), + Formats.toColor(ygcTime, warning.ygcTime), Formats.toColor(avgYgcTime, warning.ygcAvgTime), + Formats.toColor(fgcCount, warning.fullgcCount), vmInfo.fullgcTimeMills.delta); + } if (vmInfo.perfDataSupport) { System.out.printf(" | SAFE-POINT: %s count, %sms time, %dms syncTime", @@ -536,6 +548,7 @@ public static ThreadInfoMode parseInt(String mode) { public enum OutputFormat { console(true), cleanConsole(false), text(false); + OutputFormat(boolean ansi) { this.ansi = ansi; } diff --git a/vjtop/src/main/java/com/vip/vjtools/vjtop/VMInfo.java b/vjtop/src/main/java/com/vip/vjtools/vjtop/VMInfo.java index 2fab39dd..0c36ba71 100755 --- a/vjtop/src/main/java/com/vip/vjtools/vjtop/VMInfo.java +++ b/vjtop/src/main/java/com/vip/vjtools/vjtop/VMInfo.java @@ -7,9 +7,11 @@ import java.util.Locale; import java.util.Map; +import com.sun.management.GarbageCollectorMXBean; import com.vip.vjtools.vjtop.data.PerfData; import com.vip.vjtools.vjtop.data.ProcFileData; import com.vip.vjtools.vjtop.data.jmx.JmxClient; +import com.vip.vjtools.vjtop.data.jmx.JmxGarbageCollectorManager; import com.vip.vjtools.vjtop.data.jmx.JmxMemoryPoolManager; import com.vip.vjtools.vjtop.util.Formats; import com.vip.vjtools.vjtop.util.Utils; @@ -65,8 +67,10 @@ public class VMInfo { public double cpuLoad = 0.0; public double singleCoreCpuLoad = 0.0; + public String ygcStrategy = ""; public Rate ygcCount = new Rate(); public Rate ygcTimeMills = new Rate(); + public String fullgcStrategy = ""; public Rate fullgcCount = new Rate(); public Rate fullgcTimeMills = new Rate(); public String currentGcCause = ""; @@ -179,9 +183,8 @@ private void init() throws IOException { Map taregetVMSystemProperties = jmxClient.getRuntimeMXBean().getSystemProperties(); osUser = taregetVMSystemProperties.get("user.name"); jvmVersion = taregetVMSystemProperties.get("java.version"); - jvmMajorVersion = getJavaMajorVersion(jvmVersion); + jvmMajorVersion = Utils.getJavaMajorVersion(taregetVMSystemProperties.get("java.specification.version")); permGenName = jvmMajorVersion >= 8 ? "metaspace" : "perm"; - threadStackSize = 1024 * Long.parseLong(jmxClient.getHotSpotDiagnosticMXBean().getVMOption("ThreadStackSize").getValue()); maxDirectMemorySize = Long @@ -337,9 +340,21 @@ private void updateMemoryPool() { try { JmxMemoryPoolManager memoryPoolManager = jmxClient.getMemoryPoolManager(); - eden = new Usage(memoryPoolManager.getEdenMemoryPool().getUsage()); - old = new Usage(memoryPoolManager.getOldMemoryPool().getUsage()); - warningRule.updateOld(old.max); + + MemoryPoolMXBean edenMXBean = memoryPoolManager.getEdenMemoryPool(); + if (edenMXBean != null) { + eden = new Usage(edenMXBean.getUsage()); + } else { + eden = new Usage(); + } + + MemoryPoolMXBean oldMXBean = memoryPoolManager.getOldMemoryPool(); + if (oldMXBean != null) { + old = new Usage(oldMXBean.getUsage()); + warningRule.updateOld(old.max); + } else { + old = new Usage(); + } MemoryPoolMXBean survivorMemoryPool = memoryPoolManager.getSurvivorMemoryPool(); if (survivorMemoryPool != null) { @@ -348,8 +363,13 @@ private void updateMemoryPool() { sur = new Usage(); } - perm = new Usage(memoryPoolManager.getPermMemoryPool().getUsage()); - warningRule.updatePerm(perm.max); + MemoryPoolMXBean permMXBean = memoryPoolManager.getPermMemoryPool(); + if (permMXBean != null) { + perm = new Usage(permMXBean.getUsage()); + warningRule.updatePerm(perm.max); + } else { + perm = new Usage(); + } if (jvmMajorVersion >= 8) { MemoryPoolMXBean compressedClassSpaceMemoryPool = memoryPoolManager.getCompressedClassSpaceMemoryPool(); @@ -360,7 +380,12 @@ private void updateMemoryPool() { } } - codeCache = new Usage(memoryPoolManager.getCodeCacheMemoryPool().getUsage()); + MemoryPoolMXBean memoryPoolMXBean = memoryPoolManager.getCodeCacheMemoryPool(); + if (memoryPoolMXBean != null) { + codeCache = new Usage(memoryPoolMXBean.getUsage()); + } else { + codeCache = new Usage(); + } direct = new Usage(jmxClient.getBufferPoolManager().getDirectBufferPoolUsed(), jmxClient.getBufferPoolManager().getDirectBufferPoolCapacity(), maxDirectMemorySize); @@ -385,13 +410,18 @@ private void updateGC() { } } else if (isJmxStateOk()) { try { - ygcCount.update(jmxClient.getGarbageCollectorManager().getYoungCollector().getCollectionCount()); - ygcTimeMills.update(jmxClient.getGarbageCollectorManager().getYoungCollector().getCollectionTime()); - - if (jmxClient.getGarbageCollectorManager().getFullCollector() != null) { - fullgcCount.update(jmxClient.getGarbageCollectorManager().getFullCollector().getCollectionCount()); - fullgcTimeMills - .update(jmxClient.getGarbageCollectorManager().getFullCollector().getCollectionTime()); + JmxGarbageCollectorManager gcManager = jmxClient.getGarbageCollectorManager(); + + GarbageCollectorMXBean ygcMXBean = gcManager.getYoungCollector(); + ygcStrategy = gcManager.getYgcStrategy(); + ygcCount.update(ygcMXBean.getCollectionCount()); + ygcTimeMills.update(ygcMXBean.getCollectionTime()); + + GarbageCollectorMXBean fullgcMXBean = gcManager.getFullCollector(); + if (fullgcMXBean != null) { + fullgcStrategy = gcManager.getFgcStrategy(); + fullgcCount.update(fullgcMXBean.getCollectionCount()); + fullgcTimeMills.update(fullgcMXBean.getCollectionTime()); } } catch (Exception e) { handleJmxFetchDataError(e); @@ -480,17 +510,6 @@ public boolean isJmxStateOk() { return state != VMInfoState.ATTACHED_UPDATE_ERROR && state != VMInfoState.DETACHED; } - private static int getJavaMajorVersion(String jvmVersion) { - if (jvmVersion.startsWith("1.8")) { - return 8; - } else if (jvmVersion.startsWith("1.7")) { - return 7; - } else if (jvmVersion.startsWith("1.6")) { - return 6; - } else { - return 0; - } - } public enum VMInfoState { INIT, ERROR_DURING_ATTACH, ATTACHED, ATTACHED_UPDATE_ERROR, DETACHED diff --git a/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxClient.java b/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxClient.java index 138c52a1..eaa7793b 100755 --- a/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxClient.java +++ b/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxClient.java @@ -55,6 +55,7 @@ import com.sun.management.OperatingSystemMXBean; import com.sun.management.ThreadMXBean; import com.sun.tools.attach.VirtualMachine; +import com.vip.vjtools.vjtop.util.Utils; @SuppressWarnings("restriction") public class JmxClient { @@ -94,7 +95,7 @@ public void connect(String pid, String jmxHostAndPort) throws Exception { JMXServiceURL jmxUrl = new JMXServiceURL( "service:jmx:rmi://" + jmxHostAndPort + "/jndi/rmi://" + jmxHostAndPort + "/jmxrmi"); Map credentials = new HashMap(1); - String[] creds = new String[] { null, null }; + String[] creds = new String[]{null, null}; credentials.put(JMXConnector.CREDENTIALS, creds); this.jmxc = JMXConnectorFactory.connect(jmxUrl, credentials); @@ -235,31 +236,40 @@ public String attachToGetConnectorAddress() throws Exception { // 3. 未启动,尝试启动 String home = vm.getSystemProperties().getProperty("java.home"); + int version = Utils.getJavaMajorVersion(vm.getSystemProperties().getProperty("java.specification.version")); + + if (version <= 8) { + // Normally in ${java.home}/jre/lib/management-agent.jar but might + // be in ${java.home}/lib in build environments. + String agentPath = home + File.separator + "jre" + File.separator + "lib" + File.separator + + "management-agent.jar"; + File f = new File(agentPath); + if (!f.exists()) { + agentPath = home + File.separator + "lib" + File.separator + "management-agent.jar"; + f = new File(agentPath); + if (!f.exists()) { + throw new IOException("Management agent not found"); + } + } + agentPath = f.getCanonicalPath(); + vm.loadAgent(agentPath, "com.sun.management.jmxremote"); - // Normally in ${java.home}/jre/lib/management-agent.jar but might - // be in ${java.home}/lib in build environments. - String agentPath = home + File.separator + "jre" + File.separator + "lib" + File.separator - + "management-agent.jar"; - File f = new File(agentPath); - if (!f.exists()) { - agentPath = home + File.separator + "lib" + File.separator + "management-agent.jar"; - f = new File(agentPath); - if (!f.exists()) { - throw new IOException("Management agent not found"); + // 4. 再次获取connector address + agentProps = vm.getAgentProperties(); + address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP); + + if (address == null) { + throw new IOException("Fails to find connector address"); } + } else { + // for jdk9 or later + vm.startLocalManagementAgent(); } - agentPath = f.getCanonicalPath(); - vm.loadAgent(agentPath, "com.sun.management.jmxremote"); - - // 4. 再次获取connector address agentProps = vm.getAgentProperties(); address = (String) agentProps.get(LOCAL_CONNECTOR_ADDRESS_PROP); - if (address == null) { - throw new IOException("Fails to find connector address"); - } return address; } finally { @@ -301,7 +311,7 @@ private Snapshot() { public static SnapshotMBeanServerConnection newSnapshot(MBeanServerConnection mbsc) { final InvocationHandler ih = new SnapshotInvocationHandler(mbsc); return (SnapshotMBeanServerConnection) Proxy.newProxyInstance(Snapshot.class.getClassLoader(), - new Class[] { SnapshotMBeanServerConnection.class }, ih); + new Class[]{SnapshotMBeanServerConnection.class}, ih); } } diff --git a/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxGarbageCollectorManager.java b/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxGarbageCollectorManager.java index 82481b11..813675fb 100644 --- a/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxGarbageCollectorManager.java +++ b/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxGarbageCollectorManager.java @@ -12,6 +12,8 @@ public class JmxGarbageCollectorManager { private GarbageCollectorMXBean ygcMXBean = null; private GarbageCollectorMXBean fgcMXBean = null; + private String ygcStrategy = null; + private String fgcStrategy = null; public static String getByGcName(String gcName, String defaultName) { @@ -25,16 +27,21 @@ public JmxGarbageCollectorManager(MBeanServerConnection connection) throws IOExc List gcMXBeans = ManagementFactory.getPlatformMXBeans(connection, GarbageCollectorMXBean.class); + for (GarbageCollectorMXBean gcMXBean : gcMXBeans) { String gcName = gcMXBean.getName(); if ("Copy".equals(gcName) || "PS Scavenge".equals(gcName) || "ParNew".equals(gcName) - || "G1 Young Generation".equals(gcName)) { + || "G1 Young Generation".equals(gcName) || "ZGC".equals(gcName)) { ygcMXBean = gcMXBean; + ygcStrategy = gcName; } else if ("MarkSweepCompact".equals(gcName) || "PS MarkSweep".equals(gcName) || "ConcurrentMarkSweep".equals(gcName) || "G1 Old Generation".equals(gcName)) { fgcMXBean = gcMXBean; + fgcStrategy = gcName; } else { + // default ygcMXBean = gcMXBean; + ygcStrategy = gcName; } } } @@ -46,4 +53,13 @@ public synchronized GarbageCollectorMXBean getYoungCollector() { public synchronized GarbageCollectorMXBean getFullCollector() { return fgcMXBean; } + + public String getYgcStrategy() { + return ygcStrategy; + } + + public String getFgcStrategy() { + return fgcStrategy; + } + } diff --git a/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxMemoryPoolManager.java b/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxMemoryPoolManager.java index 09567b14..a137fb9d 100644 --- a/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxMemoryPoolManager.java +++ b/vjtop/src/main/java/com/vip/vjtools/vjtop/data/jmx/JmxMemoryPoolManager.java @@ -15,7 +15,10 @@ public class JmxMemoryPoolManager { public static final String PERM = "perm"; public static final String METASPACE = "metaspace";// JDK8永久代名称 public static final String CODE_CACHE = "code cache"; - public static final String COMPRESSED_CLASS_SPACE = "compressed class space"; + public static final String CODEHEAP = "codeheap"; + public static final String CODECACHE = "codecache"; + public static final String ZHEAP = "zheap"; // for zgc + public static final String COMPRESSED_CLASS_SPACE = "compressed class space"; // zgc not support until jdk15 private MemoryPoolMXBean survivorMemoryPool = null; private MemoryPoolMXBean edenMemoryPool = null; @@ -32,13 +35,14 @@ public JmxMemoryPoolManager(MBeanServerConnection connection) throws IOException String lowerCaseName = name.toLowerCase(); if (lowerCaseName.contains(SURVIVOR)) { survivorMemoryPool = memoryPool; - } else if (lowerCaseName.contains(EDEN)) { + } else if (lowerCaseName.contains(EDEN) || lowerCaseName.contains(ZHEAP)) { edenMemoryPool = memoryPool; } else if (lowerCaseName.contains(OLD) || lowerCaseName.contains(TENURED)) { oldMemoryPool = memoryPool; } else if (lowerCaseName.contains(PERM) || lowerCaseName.contains(METASPACE)) { permMemoryPool = memoryPool; - } else if (lowerCaseName.contains(CODE_CACHE)) { + } else if (lowerCaseName.contains(CODE_CACHE) || lowerCaseName.contains(CODEHEAP) + || lowerCaseName.contains(CODECACHE)) { codeCacheMemoryPool = memoryPool; } else if (lowerCaseName.contains(COMPRESSED_CLASS_SPACE)) { compressedClassSpaceMemoryPool = memoryPool; diff --git a/vjtop/src/main/java/com/vip/vjtools/vjtop/util/Utils.java b/vjtop/src/main/java/com/vip/vjtools/vjtop/util/Utils.java index ebd10709..3a89df2f 100644 --- a/vjtop/src/main/java/com/vip/vjtools/vjtop/util/Utils.java +++ b/vjtop/src/main/java/com/vip/vjtools/vjtop/util/Utils.java @@ -70,4 +70,20 @@ public static void sleep(long mills) { } catch (InterruptedException e) { } } + + public static int getJavaMajorVersion(String javaSpecificationVersion) { + if (javaSpecificationVersion.startsWith("1.8")) { + return 8; + } else if (javaSpecificationVersion.startsWith("1.7")) { + return 7; + } else if (javaSpecificationVersion.startsWith("1.6")) { + return 6; + } else { + try { + return Integer.parseInt(javaSpecificationVersion); + } catch (NumberFormatException e) { + return 0; + } + } + } }