diff options
author | duke <none@none> | 2007-12-01 00:00:00 +0000 |
---|---|---|
committer | duke <none@none> | 2007-12-01 00:00:00 +0000 |
commit | 6e45e10b03bafdc125c46a4864ba802c24d6bc78 (patch) | |
tree | 182810ab2fece13f57a928d026f93e9ede0827f9 /src/share/demo/management | |
download | jdk8u_jdk-6e45e10b03bafdc125c46a4864ba802c24d6bc78.tar.gz |
Initial load
Diffstat (limited to 'src/share/demo/management')
-rw-r--r-- | src/share/demo/management/FullThreadDump/Deadlock.java | 204 | ||||
-rw-r--r-- | src/share/demo/management/FullThreadDump/FullThreadDump.java | 113 | ||||
-rw-r--r-- | src/share/demo/management/FullThreadDump/README.txt | 52 | ||||
-rw-r--r-- | src/share/demo/management/FullThreadDump/ThreadMonitor.java | 256 | ||||
-rw-r--r-- | src/share/demo/management/JTop/JTop.java | 364 | ||||
-rw-r--r-- | src/share/demo/management/JTop/JTopPlugin.java | 119 | ||||
-rw-r--r-- | src/share/demo/management/JTop/META-INF/services/com.sun.tools.jconsole.JConsolePlugin | 1 | ||||
-rw-r--r-- | src/share/demo/management/JTop/README.txt | 61 | ||||
-rw-r--r-- | src/share/demo/management/MemoryMonitor/MemoryMonitor.java | 463 | ||||
-rw-r--r-- | src/share/demo/management/MemoryMonitor/README.txt | 48 | ||||
-rw-r--r-- | src/share/demo/management/VerboseGC/PrintGCStat.java | 146 | ||||
-rw-r--r-- | src/share/demo/management/VerboseGC/README.txt | 31 | ||||
-rw-r--r-- | src/share/demo/management/VerboseGC/VerboseGC.java | 150 | ||||
-rw-r--r-- | src/share/demo/management/index.html | 46 |
14 files changed, 2054 insertions, 0 deletions
diff --git a/src/share/demo/management/FullThreadDump/Deadlock.java b/src/share/demo/management/FullThreadDump/Deadlock.java new file mode 100644 index 0000000000..314750f1b3 --- /dev/null +++ b/src/share/demo/management/FullThreadDump/Deadlock.java @@ -0,0 +1,204 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + */ + +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.io.IOException; + +/** + * This Deadlock class demonstrates the capability of performing + * deadlock detection programmatically within the application using + * the java.lang.management API. + * + * See ThreadMonitor.java for the use of java.lang.management.ThreadMXBean + * API. + */ +public class Deadlock { + public static void main(String[] argv) { + Deadlock dl = new Deadlock(); + + // Now find deadlock + ThreadMonitor monitor = new ThreadMonitor(); + boolean found = false; + while (!found) { + found = monitor.findDeadlock(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + System.exit(1); + } + } + + System.out.println("\nPress <Enter> to exit this Deadlock program.\n"); + waitForEnterPressed(); + } + + + private CyclicBarrier barrier = new CyclicBarrier(6); + public Deadlock() { + DeadlockThread[] dThreads = new DeadlockThread[6]; + + Monitor a = new Monitor("a"); + Monitor b = new Monitor("b"); + Monitor c = new Monitor("c"); + dThreads[0] = new DeadlockThread("MThread-1", a, b); + dThreads[1] = new DeadlockThread("MThread-2", b, c); + dThreads[2] = new DeadlockThread("MThread-3", c, a); + + Lock d = new ReentrantLock(); + Lock e = new ReentrantLock(); + Lock f = new ReentrantLock(); + + dThreads[3] = new DeadlockThread("SThread-4", d, e); + dThreads[4] = new DeadlockThread("SThread-5", e, f); + dThreads[5] = new DeadlockThread("SThread-6", f, d); + + // make them daemon threads so that the test will exit + for (int i = 0; i < 6; i++) { + dThreads[i].setDaemon(true); + dThreads[i].start(); + } + } + + class DeadlockThread extends Thread { + private Lock lock1 = null; + private Lock lock2 = null; + private Monitor mon1 = null; + private Monitor mon2 = null; + private boolean useSync; + + DeadlockThread(String name, Lock lock1, Lock lock2) { + super(name); + this.lock1 = lock1; + this.lock2 = lock2; + this.useSync = true; + } + DeadlockThread(String name, Monitor mon1, Monitor mon2) { + super(name); + this.mon1 = mon1; + this.mon2 = mon2; + this.useSync = false; + } + public void run() { + if (useSync) { + syncLock(); + } else { + monitorLock(); + } + } + private void syncLock() { + lock1.lock(); + try { + try { + barrier.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + System.exit(1); + } catch (BrokenBarrierException e) { + e.printStackTrace(); + System.exit(1); + } + goSyncDeadlock(); + } finally { + lock1.unlock(); + } + } + private void goSyncDeadlock() { + try { + barrier.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + System.exit(1); + } catch (BrokenBarrierException e) { + e.printStackTrace(); + System.exit(1); + } + lock2.lock(); + throw new RuntimeException("should not reach here."); + } + private void monitorLock() { + synchronized (mon1) { + try { + barrier.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + System.exit(1); + } catch (BrokenBarrierException e) { + e.printStackTrace(); + System.exit(1); + } + goMonitorDeadlock(); + } + } + private void goMonitorDeadlock() { + try { + barrier.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + System.exit(1); + } catch (BrokenBarrierException e) { + e.printStackTrace(); + System.exit(1); + } + synchronized (mon2) { + throw new RuntimeException(getName() + " should not reach here."); + } + } + } + + class Monitor { + String name; + Monitor(String name) { + this.name = name; + } + } + + private static void waitForEnterPressed() { + try { + boolean done = false; + while (!done) { + char ch = (char) System.in.read(); + if (ch<0||ch=='\n') { + done = true; + } + } + } + catch (IOException e) { + e.printStackTrace(); + System.exit(0); + } + } +} diff --git a/src/share/demo/management/FullThreadDump/FullThreadDump.java b/src/share/demo/management/FullThreadDump/FullThreadDump.java new file mode 100644 index 0000000000..0b220e0edc --- /dev/null +++ b/src/share/demo/management/FullThreadDump/FullThreadDump.java @@ -0,0 +1,113 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + */ + +import javax.management.*; +import javax.management.remote.*; +import java.io.IOException; +import java.net.MalformedURLException; + +/** + * This FullThreadDump class demonstrates the capability to get + * a full thread dump and also detect deadlock remotely. + */ +public class FullThreadDump { + private MBeanServerConnection server; + private JMXConnector jmxc; + public FullThreadDump(String hostname, int port) { + System.out.println("Connecting to " + hostname + ":" + port); + + // Create an RMI connector client and connect it to + // the RMI connector server + String urlPath = "/jndi/rmi://" + hostname + ":" + port + "/jmxrmi"; + connect(urlPath); + } + + public void dump() { + try { + ThreadMonitor monitor = new ThreadMonitor(server); + monitor.threadDump(); + if (!monitor.findDeadlock()) { + System.out.println("No deadlock found."); + } + } catch (IOException e) { + System.err.println("\nCommunication error: " + e.getMessage()); + System.exit(1); + } + } + + /** + * Connect to a JMX agent of a given URL. + */ + private void connect(String urlPath) { + try { + JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath); + this.jmxc = JMXConnectorFactory.connect(url); + this.server = jmxc.getMBeanServerConnection(); + } catch (MalformedURLException e) { + // should not reach here + } catch (IOException e) { + System.err.println("\nCommunication error: " + e.getMessage()); + System.exit(1); + } + } + + public static void main(String[] args) { + if (args.length != 1) { + usage(); + } + + String[] arg2 = args[0].split(":"); + if (arg2.length != 2) { + usage(); + } + String hostname = arg2[0]; + int port = -1; + try { + port = Integer.parseInt(arg2[1]); + } catch (NumberFormatException x) { + usage(); + } + if (port < 0) { + usage(); + } + + // get full thread dump and perform deadlock detection + FullThreadDump ftd = new FullThreadDump(hostname, port); + ftd.dump(); + } + + private static void usage() { + System.out.println("Usage: java FullThreadDump <hostname>:<port>"); + } +} diff --git a/src/share/demo/management/FullThreadDump/README.txt b/src/share/demo/management/FullThreadDump/README.txt new file mode 100644 index 0000000000..1571fb8659 --- /dev/null +++ b/src/share/demo/management/FullThreadDump/README.txt @@ -0,0 +1,52 @@ +FullThreadDump demonstrates the use of the java.lang.management API +to print the full thread dump. JDK 6 defines a new API to dump +the information about monitors and java.util.concurrent ownable +synchronizers. + +This demo also illustrates how to monitor JDK 5 and JDK 6 VMs with +two versions of APIs. + +It contains two parts: +a) Local monitoring within the application +b) Remote monitoring by connecting to a JMX agent with a JMX service URL: + service:jmx:rmi:///jndi/rmi://<hostName>:<portNum>/jmxrmi + where <hostName> is the hostname and <portNum> is the port number + to which the JMX agent will be connected. + +To run the demo +--------------- +a) Local Monitoring + + java -cp <JDK_HOME>/demo/management/FullThreadDump/FullThreadDump.jar Deadlock + + This will dump the stack trace and then detect deadlocks locally + within the application. + +b) Remote Monitoring + + (1) Start the Deadlock application (or any other application) + with the JMX agent as follows: + + java -Dcom.sun.management.jmxremote.port=1090 + -Dcom.sun.management.jmxremote.ssl=false + -Dcom.sun.management.jmxremote.authenticate=false + -cp <JDK_HOME>/demo/management/FullThreadDump/FullThreadDump.jar + Deadlock + + This instruction uses the Sun's built-in support to enable a JMX agent. + You can programmatically start a JMX agent with the RMI connector + using javax.management.remote API. See the javadoc and examples for + javax.management.remote API for details. + + (2) Run FullThreadDump + + java -jar <JDK_HOME>/demo/management/FullThreadDump/FullThreadDump.jar \ + localhost:1090 + + This will dump the stack trace and then print out the deadlocked threads. + +These instructions assume that this installation's version of the java +command is in your path. If it isn't, then you should either +specify the complete path to the java command or update your +PATH environment variable as described in the installation +instructions for the Java(TM) SDK. diff --git a/src/share/demo/management/FullThreadDump/ThreadMonitor.java b/src/share/demo/management/FullThreadDump/ThreadMonitor.java new file mode 100644 index 0000000000..419275ebc2 --- /dev/null +++ b/src/share/demo/management/FullThreadDump/ThreadMonitor.java @@ -0,0 +1,256 @@ +/* + * Copyright 2004-2005 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + */ + +import static java.lang.management.ManagementFactory.*; +import java.lang.management.ThreadMXBean; +import java.lang.management.ThreadInfo; +import java.lang.management.LockInfo; +import java.lang.management.MonitorInfo; +import javax.management.*; +import java.io.*; +import java.util.*; + +/** + * Example of using the java.lang.management API to dump stack trace + * and to perform deadlock detection. + * + * @author Mandy Chung + */ +public class ThreadMonitor { + private MBeanServerConnection server; + private ThreadMXBean tmbean; + private ObjectName objname; + + // default - JDK 6+ VM + private String findDeadlocksMethodName = "findDeadlockedThreads"; + private boolean canDumpLocks = true; + + /** + * Constructs a ThreadMonitor object to get thread information + * in a remote JVM. + */ + public ThreadMonitor(MBeanServerConnection server) throws IOException { + this.server = server; + this.tmbean = newPlatformMXBeanProxy(server, + THREAD_MXBEAN_NAME, + ThreadMXBean.class); + try { + objname = new ObjectName(THREAD_MXBEAN_NAME); + } catch (MalformedObjectNameException e) { + // should not reach here + InternalError ie = new InternalError(e.getMessage()); + ie.initCause(e); + throw ie; + } + parseMBeanInfo(); + } + + /** + * Constructs a ThreadMonitor object to get thread information + * in the local JVM. + */ + public ThreadMonitor() { + this.tmbean = getThreadMXBean(); + } + + /** + * Prints the thread dump information to System.out. + */ + public void threadDump() { + if (canDumpLocks) { + if (tmbean.isObjectMonitorUsageSupported() && + tmbean.isSynchronizerUsageSupported()) { + // Print lock info if both object monitor usage + // and synchronizer usage are supported. + // This sample code can be modified to handle if + // either monitor usage or synchronizer usage is supported. + dumpThreadInfoWithLocks(); + } + } else { + dumpThreadInfo(); + } + } + + private void dumpThreadInfo() { + System.out.println("Full Java thread dump"); + long[] tids = tmbean.getAllThreadIds(); + ThreadInfo[] tinfos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE); + for (ThreadInfo ti : tinfos) { + printThreadInfo(ti); + } + } + + /** + * Prints the thread dump information with locks info to System.out. + */ + private void dumpThreadInfoWithLocks() { + System.out.println("Full Java thread dump with locks info"); + + ThreadInfo[] tinfos = tmbean.dumpAllThreads(true, true); + for (ThreadInfo ti : tinfos) { + printThreadInfo(ti); + LockInfo[] syncs = ti.getLockedSynchronizers(); + printLockInfo(syncs); + } + System.out.println(); + } + + private static String INDENT = " "; + + private void printThreadInfo(ThreadInfo ti) { + // print thread information + printThread(ti); + + // print stack trace with locks + StackTraceElement[] stacktrace = ti.getStackTrace(); + MonitorInfo[] monitors = ti.getLockedMonitors(); + for (int i = 0; i < stacktrace.length; i++) { + StackTraceElement ste = stacktrace[i]; + System.out.println(INDENT + "at " + ste.toString()); + for (MonitorInfo mi : monitors) { + if (mi.getLockedStackDepth() == i) { + System.out.println(INDENT + " - locked " + mi); + } + } + } + System.out.println(); + } + + private void printThread(ThreadInfo ti) { + StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" + + " Id=" + ti.getThreadId() + + " in " + ti.getThreadState()); + if (ti.getLockName() != null) { + sb.append(" on lock=" + ti.getLockName()); + } + if (ti.isSuspended()) { + sb.append(" (suspended)"); + } + if (ti.isInNative()) { + sb.append(" (running in native)"); + } + System.out.println(sb.toString()); + if (ti.getLockOwnerName() != null) { + System.out.println(INDENT + " owned by " + ti.getLockOwnerName() + + " Id=" + ti.getLockOwnerId()); + } + } + + private void printMonitorInfo(ThreadInfo ti, MonitorInfo[] monitors) { + System.out.println(INDENT + "Locked monitors: count = " + monitors.length); + for (MonitorInfo mi : monitors) { + System.out.println(INDENT + " - " + mi + " locked at "); + System.out.println(INDENT + " " + mi.getLockedStackDepth() + + " " + mi.getLockedStackFrame()); + } + } + + private void printLockInfo(LockInfo[] locks) { + System.out.println(INDENT + "Locked synchronizers: count = " + locks.length); + for (LockInfo li : locks) { + System.out.println(INDENT + " - " + li); + } + System.out.println(); + } + + /** + * Checks if any threads are deadlocked. If any, print + * the thread dump information. + */ + public boolean findDeadlock() { + long[] tids; + if (findDeadlocksMethodName.equals("findDeadlockedThreads") && + tmbean.isSynchronizerUsageSupported()) { + tids = tmbean.findDeadlockedThreads(); + if (tids == null) { + return false; + } + + System.out.println("Deadlock found :-"); + ThreadInfo[] infos = tmbean.getThreadInfo(tids, true, true); + for (ThreadInfo ti : infos) { + printThreadInfo(ti); + printLockInfo(ti.getLockedSynchronizers()); + System.out.println(); + } + } else { + tids = tmbean.findMonitorDeadlockedThreads(); + if (tids == null) { + return false; + } + ThreadInfo[] infos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE); + for (ThreadInfo ti : infos) { + // print thread information + printThreadInfo(ti); + } + } + + return true; + } + + + private void parseMBeanInfo() throws IOException { + try { + MBeanOperationInfo[] mopis = server.getMBeanInfo(objname).getOperations(); + + // look for findDeadlockedThreads operations; + boolean found = false; + for (MBeanOperationInfo op : mopis) { + if (op.getName().equals(findDeadlocksMethodName)) { + found = true; + break; + } + } + if (!found) { + // if findDeadlockedThreads operation doesn't exist, + // the target VM is running on JDK 5 and details about + // synchronizers and locks cannot be dumped. + findDeadlocksMethodName = "findMonitorDeadlockedThreads"; + canDumpLocks = false; + } + } catch (IntrospectionException e) { + InternalError ie = new InternalError(e.getMessage()); + ie.initCause(e); + throw ie; + } catch (InstanceNotFoundException e) { + InternalError ie = new InternalError(e.getMessage()); + ie.initCause(e); + throw ie; + } catch (ReflectionException e) { + InternalError ie = new InternalError(e.getMessage()); + ie.initCause(e); + throw ie; + } + } +} diff --git a/src/share/demo/management/JTop/JTop.java b/src/share/demo/management/JTop/JTop.java new file mode 100644 index 0000000000..121b159762 --- /dev/null +++ b/src/share/demo/management/JTop/JTop.java @@ -0,0 +1,364 @@ +/* + * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * + * Example of using the java.lang.management API to sort threads + * by CPU usage. + * + * JTop class can be run as a standalone application. + * It first establishs a connection to a target VM specified + * by the given hostname and port number where the JMX agent + * to be connected. It then polls for the thread information + * and the CPU consumption of each thread to display every 2 + * seconds. + * + * It is also used by JTopPlugin which is a JConsolePlugin + * that can be used with JConsole (see README.txt). The JTop + * GUI will be added as a JConsole tab by the JTop plugin. + * + * @see com.sun.tools.jconsole.JConsolePlugin + * + * @author Mandy Chung + */ +import java.lang.management.*; +import javax.management.*; +import javax.management.remote.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedMap; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeMap; +import java.util.concurrent.ExecutionException; +import java.text.NumberFormat; +import java.net.MalformedURLException; +import static java.lang.management.ManagementFactory.*; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; +import javax.swing.table.*; + +/** + * JTop is a JPanel to display thread's name, CPU time, and its state + * in a table. + */ +public class JTop extends JPanel { + private MBeanServerConnection server; + private ThreadMXBean tmbean; + private MyTableModel tmodel; + public JTop() { + super(new GridLayout(1,0)); + + tmodel = new MyTableModel(); + JTable table = new JTable(tmodel); + table.setPreferredScrollableViewportSize(new Dimension(500, 300)); + + // Set the renderer to format Double + table.setDefaultRenderer(Double.class, new DoubleRenderer()); + // Add some space + table.setIntercellSpacing(new Dimension(6,3)); + table.setRowHeight(table.getRowHeight() + 4); + + // Create the scroll pane and add the table to it. + JScrollPane scrollPane = new JScrollPane(table); + + // Add the scroll pane to this panel. + add(scrollPane); + } + + // Set the MBeanServerConnection object for communicating + // with the target VM + public void setMBeanServerConnection(MBeanServerConnection mbs) { + this.server = mbs; + try { + this.tmbean = newPlatformMXBeanProxy(server, + THREAD_MXBEAN_NAME, + ThreadMXBean.class); + } catch (IOException e) { + e.printStackTrace(); + } + if (!tmbean.isThreadCpuTimeSupported()) { + System.err.println("This VM does not support thread CPU time monitoring"); + } else { + tmbean.setThreadCpuTimeEnabled(true); + } + } + + class MyTableModel extends AbstractTableModel { + private String[] columnNames = {"ThreadName", + "CPU(sec)", + "State"}; + // List of all threads. The key of each entry is the CPU time + // and its value is the ThreadInfo object with no stack trace. + private List<Map.Entry<Long, ThreadInfo>> threadList = + Collections.EMPTY_LIST; + + public MyTableModel() { + } + + public int getColumnCount() { + return columnNames.length; + } + + public int getRowCount() { + return threadList.size(); + } + + public String getColumnName(int col) { + return columnNames[col]; + } + + public Object getValueAt(int row, int col) { + Map.Entry<Long, ThreadInfo> me = threadList.get(row); + switch (col) { + case 0 : + // Column 0 shows the thread name + return me.getValue().getThreadName(); + case 1 : + // Column 1 shows the CPU usage + long ns = me.getKey().longValue(); + double sec = ns / 1000000000; + return new Double(sec); + case 2 : + // Column 2 shows the thread state + return me.getValue().getThreadState(); + default: + return null; + } + } + + public Class getColumnClass(int c) { + return getValueAt(0, c).getClass(); + } + + void setThreadList(List<Map.Entry<Long, ThreadInfo>> list) { + threadList = list; + } + } + + /** + * Get the thread list with CPU consumption and the ThreadInfo + * for each thread sorted by the CPU time. + */ + private List<Map.Entry<Long, ThreadInfo>> getThreadList() { + // Get all threads and their ThreadInfo objects + // with no stack trace + long[] tids = tmbean.getAllThreadIds(); + ThreadInfo[] tinfos = tmbean.getThreadInfo(tids); + + // build a map with key = CPU time and value = ThreadInfo + SortedMap<Long, ThreadInfo> map = new TreeMap<Long, ThreadInfo>(); + for (int i = 0; i < tids.length; i++) { + long cpuTime = tmbean.getThreadCpuTime(tids[i]); + // filter out threads that have been terminated + if (cpuTime != -1 && tinfos[i] != null) { + map.put(new Long(cpuTime), tinfos[i]); + } + } + + // build the thread list and sort it with CPU time + // in decreasing order + Set<Map.Entry<Long, ThreadInfo>> set = map.entrySet(); + List<Map.Entry<Long, ThreadInfo>> list = + new ArrayList<Map.Entry<Long, ThreadInfo>>(set); + Collections.reverse(list); + return list; + } + + + /** + * Format Double with 4 fraction digits + */ + class DoubleRenderer extends DefaultTableCellRenderer { + NumberFormat formatter; + public DoubleRenderer() { + super(); + setHorizontalAlignment(JLabel.RIGHT); + } + + public void setValue(Object value) { + if (formatter==null) { + formatter = NumberFormat.getInstance(); + formatter.setMinimumFractionDigits(4); + } + setText((value == null) ? "" : formatter.format(value)); + } + } + + // SwingWorker responsible for updating the GUI + // + // It first gets the thread and CPU usage information as a + // background task done by a worker thread so that + // it will not block the event dispatcher thread. + // + // When the worker thread finishes, the event dispatcher + // thread will invoke the done() method which will update + // the UI. + class Worker extends SwingWorker<List<Map.Entry<Long, ThreadInfo>>,Object> { + private MyTableModel tmodel; + Worker(MyTableModel tmodel) { + this.tmodel = tmodel; + } + + // Get the current thread info and CPU time + public List<Map.Entry<Long, ThreadInfo>> doInBackground() { + return getThreadList(); + } + + // fire table data changed to trigger GUI update + // when doInBackground() is finished + protected void done() { + try { + // Set table model with the new thread list + tmodel.setThreadList(get()); + // refresh the table model + tmodel.fireTableDataChanged(); + } catch (InterruptedException e) { + } catch (ExecutionException e) { + } + } + } + + // Return a new SwingWorker for UI update + public SwingWorker<?,?> newSwingWorker() { + return new Worker(tmodel); + } + + public static void main(String[] args) throws Exception { + // Validate the input arguments + if (args.length != 1) { + usage(); + } + + String[] arg2 = args[0].split(":"); + if (arg2.length != 2) { + usage(); + } + String hostname = arg2[0]; + int port = -1; + try { + port = Integer.parseInt(arg2[1]); + } catch (NumberFormatException x) { + usage(); + } + if (port < 0) { + usage(); + } + + // Create the JTop Panel + final JTop jtop = new JTop(); + // Set up the MBeanServerConnection to the target VM + MBeanServerConnection server = connect(hostname, port); + jtop.setMBeanServerConnection(server); + + // A timer task to update GUI per each interval + TimerTask timerTask = new TimerTask() { + public void run() { + // Schedule the SwingWorker to update the GUI + jtop.newSwingWorker().execute(); + } + }; + + // Create the standalone window with JTop panel + // by the event dispatcher thread + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + createAndShowGUI(jtop); + } + }); + + // refresh every 2 seconds + Timer timer = new Timer("JTop Sampling thread"); + timer.schedule(timerTask, 0, 2000); + + } + + // Establish a connection with the remote application + // + // You can modify the urlPath to the address of the JMX agent + // of your application if it has a different URL. + // + // You can also modify the following code to take + // username and password for client authentication. + private static MBeanServerConnection connect(String hostname, int port) { + // Create an RMI connector client and connect it to + // the RMI connector server + String urlPath = "/jndi/rmi://" + hostname + ":" + port + "/jmxrmi"; + MBeanServerConnection server = null; + try { + JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath); + JMXConnector jmxc = JMXConnectorFactory.connect(url); + server = jmxc.getMBeanServerConnection(); + } catch (MalformedURLException e) { + // should not reach here + } catch (IOException e) { + System.err.println("\nCommunication error: " + e.getMessage()); + System.exit(1); + } + return server; + } + + private static void usage() { + System.out.println("Usage: java JTop <hostname>:<port>"); + System.exit(1); + } + /** + * Create the GUI and show it. For thread safety, + * this method should be invoked from the + * event-dispatching thread. + */ + private static void createAndShowGUI(JPanel jtop) { + // Create and set up the window. + JFrame frame = new JFrame("JTop"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + // Create and set up the content pane. + JComponent contentPane = (JComponent) frame.getContentPane(); + contentPane.add(jtop, BorderLayout.CENTER); + contentPane.setOpaque(true); //content panes must be opaque + contentPane.setBorder(new EmptyBorder(12, 12, 12, 12)); + frame.setContentPane(contentPane); + + // Display the window. + frame.pack(); + frame.setVisible(true); + } + +} diff --git a/src/share/demo/management/JTop/JTopPlugin.java b/src/share/demo/management/JTop/JTopPlugin.java new file mode 100644 index 0000000000..44866af34a --- /dev/null +++ b/src/share/demo/management/JTop/JTopPlugin.java @@ -0,0 +1,119 @@ +/* + * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * + * Example of a JConsole Plugin. This loads JTop as a JConsole tab. + * + * @author Mandy Chung + */ + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.LinkedHashMap; +import java.util.Map; +import javax.management.MBeanServerConnection; +import javax.swing.JPanel; +import javax.swing.SwingWorker; + +import com.sun.tools.jconsole.JConsolePlugin; +import com.sun.tools.jconsole.JConsoleContext; +import com.sun.tools.jconsole.JConsoleContext.ConnectionState; + +/** + * JTopPlugin is a subclass to com.sun.tools.jconsole.JConsolePlugin + * + * JTopPlugin is loaded and instantiated by JConsole. One instance + * is created for each window that JConsole creates. It listens to + * the connected property change so that it will update JTop with + * the valid MBeanServerConnection object. JTop is a JPanel object + * displaying the thread and its CPU usage information. + */ +public class JTopPlugin extends JConsolePlugin implements PropertyChangeListener +{ + private JTop jtop = null; + private Map<String, JPanel> tabs = null; + + public JTopPlugin() { + // register itself as a listener + addContextPropertyChangeListener(this); + } + + /* + * Returns a JTop tab to be added in JConsole. + */ + public synchronized Map<String, JPanel> getTabs() { + if (tabs == null) { + jtop = new JTop(); + jtop.setMBeanServerConnection( + getContext().getMBeanServerConnection()); + // use LinkedHashMap if you want a predictable order + // of the tabs to be added in JConsole + tabs = new LinkedHashMap<String, JPanel>(); + tabs.put("JTop", jtop); + } + return tabs; + } + + /* + * Returns a SwingWorker which is responsible for updating the JTop tab. + */ + public SwingWorker<?,?> newSwingWorker() { + return jtop.newSwingWorker(); + } + + // You can implement the dispose() method if you need to release + // any resource when the plugin instance is disposed when the JConsole + // window is closed. + // + // public void dispose() { + // } + + /* + * Property listener to reset the MBeanServerConnection + * at reconnection time. + */ + public void propertyChange(PropertyChangeEvent ev) { + String prop = ev.getPropertyName(); + if (prop == JConsoleContext.CONNECTION_STATE_PROPERTY) { + ConnectionState oldState = (ConnectionState)ev.getOldValue(); + ConnectionState newState = (ConnectionState)ev.getNewValue(); + // JConsole supports disconnection and reconnection + // The MBeanServerConnection will become invalid when + // disconnected. Need to use the new MBeanServerConnection object + // created at reconnection time. + if (newState == ConnectionState.CONNECTED && jtop != null) { + jtop.setMBeanServerConnection( + getContext().getMBeanServerConnection()); + } + } + } +} diff --git a/src/share/demo/management/JTop/META-INF/services/com.sun.tools.jconsole.JConsolePlugin b/src/share/demo/management/JTop/META-INF/services/com.sun.tools.jconsole.JConsolePlugin new file mode 100644 index 0000000000..11aa2aea38 --- /dev/null +++ b/src/share/demo/management/JTop/META-INF/services/com.sun.tools.jconsole.JConsolePlugin @@ -0,0 +1 @@ +JTopPlugin diff --git a/src/share/demo/management/JTop/README.txt b/src/share/demo/management/JTop/README.txt new file mode 100644 index 0000000000..b2d3ae43c0 --- /dev/null +++ b/src/share/demo/management/JTop/README.txt @@ -0,0 +1,61 @@ +JTop monitors the CPU usage of all threads in a remote application +which has remote management enabled. JTop demonstrates the use of +the java.lang.management API to obtain the CPU consumption for +each thread. + +JTop is also a JConsole Plugin. See below for details. + +JTop Standalone GUI +=================== + +JTop first establishes a connection to a JMX agent in a remote +application with a JMX service URL: + service:jmx:rmi:///jndi/rmi://<hostName>:<portNum>/jmxrmi + +where <hostName> is the hostname and <portNum> is the port number +to which the JMX agent will be connected. + +To run the demo +--------------- +(1) Start the application with the JMX agent - here's an example of + how the Java2D is started + + java -Dcom.sun.management.jmxremote.port=1090 + -Dcom.sun.management.jmxremote.ssl=false + -Dcom.sun.management.jmxremote.authenticate=false + -jar <JDK_HOME>/demo/jfc/Java2D/Java2Demo.jar + + This instruction uses the Sun's built-in support to enable a JMX agent + with a JMX service URL as described above. + You can programmatically start a JMX agent with the RMI connector + using javax.management.remote API. See the javadoc and examples for + javax.management.remote API for details. + +(2) Run JTop on a different machine: + + java -jar <JDK_HOME>/demo/management/JTop/JTop.jar <hostname>:1090 + + where <hostname> is where the Java2Demo.jar runs in step (1). + +These instructions assume that this installation's version of the java +command is in your path. If it isn't, then you should either +specify the complete path to the java command or update your +PATH environment variable as described in the installation +instructions for the Java(TM) SDK. + +JTop JConsole Plugin +==================== + +JTop is a JConsole Plugin which adds a "JTop" tab to JConsole. + +To run JConsole with the JTop plugin +------------------------------------ + jconsole -pluginpath <JDK_HOME>/demo/management/JTop/JTop.jar + + +To compile +---------- + javac -classpath <JDK_HOME>/lib/jconsole.jar JTopPlugin.java + +com.sun.tools.jconsole API is in jconsole.jar which is needed +in the classpath for compilation. diff --git a/src/share/demo/management/MemoryMonitor/MemoryMonitor.java b/src/share/demo/management/MemoryMonitor/MemoryMonitor.java new file mode 100644 index 0000000000..c44e5f2c2d --- /dev/null +++ b/src/share/demo/management/MemoryMonitor/MemoryMonitor.java @@ -0,0 +1,463 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + */ + +import java.awt.*; +import java.awt.event.*; +import java.awt.image.BufferedImage; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.util.Date; +import javax.swing.*; +import javax.swing.border.EtchedBorder; +import javax.swing.border.TitledBorder; +import java.lang.management.*; +import java.util.*; + + +/** + * Demo code which plots the memory usage by all memory pools. + * The memory usage is sampled at some time interval using + * java.lang.management API. This demo code is modified based + * java2d MemoryMonitor demo. + */ +public class MemoryMonitor extends JPanel { + + static JCheckBox dateStampCB = new JCheckBox("Output Date Stamp"); + public Surface surf; + JPanel controls; + boolean doControls; + JTextField tf; + // Get memory pools. + static java.util.List<MemoryPoolMXBean> mpools = + ManagementFactory.getMemoryPoolMXBeans(); + // Total number of memory pools. + static int numPools = mpools.size(); + + public MemoryMonitor() { + setLayout(new BorderLayout()); + setBorder(new TitledBorder(new EtchedBorder(), "Memory Monitor")); + add(surf = new Surface()); + controls = new JPanel(); + controls.setPreferredSize(new Dimension(135,80)); + Font font = new Font("serif", Font.PLAIN, 10); + JLabel label = new JLabel("Sample Rate"); + label.setFont(font); + label.setForeground(Color.red); + controls.add(label); + tf = new JTextField("1000"); + tf.setPreferredSize(new Dimension(45,20)); + controls.add(tf); + controls.add(label = new JLabel("ms")); + label.setFont(font); + label.setForeground(Color.red); + controls.add(dateStampCB); + dateStampCB.setFont(font); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + removeAll(); + if ((doControls = !doControls)) { + surf.stop(); + add(controls); + } else { + try { + surf.sleepAmount = Long.parseLong(tf.getText().trim()); + } catch (Exception ex) {} + surf.start(); + add(surf); + } + validate(); + repaint(); + } + }); + } + + + public class Surface extends JPanel implements Runnable { + + public Thread thread; + public long sleepAmount = 1000; + public int usageHistCount = 20000; + private int w, h; + private BufferedImage bimg; + private Graphics2D big; + private Font font = new Font("Times New Roman", Font.PLAIN, 11); + private int columnInc; + private float usedMem[][]; + private int ptNum[]; + private int ascent, descent; + private Rectangle graphOutlineRect = new Rectangle(); + private Rectangle2D mfRect = new Rectangle2D.Float(); + private Rectangle2D muRect = new Rectangle2D.Float(); + private Line2D graphLine = new Line2D.Float(); + private Color graphColor = new Color(46, 139, 87); + private Color mfColor = new Color(0, 100, 0); + private String usedStr; + + + public Surface() { + setBackground(Color.black); + addMouseListener(new MouseAdapter() { + public void mouseClicked(MouseEvent e) { + if (thread == null) start(); else stop(); + } + }); + int i = 0; + usedMem = new float[numPools][]; + ptNum = new int[numPools]; + } + + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + public Dimension getMaximumSize() { + return getPreferredSize(); + } + + public Dimension getPreferredSize() { + return new Dimension(135,80); + } + + + public void paint(Graphics g) { + + if (big == null) { + return; + } + + big.setBackground(getBackground()); + big.clearRect(0,0,w,h); + + + h = h / ((numPools + numPools%2) / 2); + w = w / 2; + + int k=0; // index of memory pool. + for (int i=0; i < 2;i++) { + for (int j=0; j < (numPools + numPools%2)/ 2; j++) { + plotMemoryUsage(w*i,h*j,w,h,k); + if (++k >= numPools) { + i = 3; + j = (numPools + numPools%2)/ 2; + break; + } + } + } + g.drawImage(bimg, 0, 0, this); + } + + public void plotMemoryUsage(int x1, int y1, int x2, int y2, int npool) { + + MemoryPoolMXBean mp = mpools.get(npool); + float usedMemory = mp.getUsage().getUsed(); + float totalMemory = mp.getUsage().getMax(); + + // .. Draw allocated and used strings .. + big.setColor(Color.green); + + // Print Max memory allocated for this memory pool. + big.drawString(String.valueOf((int)totalMemory/1024) + "K Max ", x1+4.0f, (float) y1 + ascent+0.5f); + big.setColor(Color.yellow); + + // Print the memory pool name. + big.drawString(mp.getName(), x1+x2/2, (float) y1 + ascent+0.5f); + + // Print the memory used by this memory pool. + usedStr = String.valueOf((int)usedMemory/1024) + + "K used"; + big.setColor(Color.green); + big.drawString(usedStr, x1+4, y1+y2-descent); + + // Calculate remaining size + float ssH = ascent + descent; + float remainingHeight = (float) (y2 - (ssH*2) - 0.5f); + float blockHeight = remainingHeight/10; + float blockWidth = 20.0f; + float remainingWidth = (float) (x2 - blockWidth - 10); + + // .. Memory Free .. + big.setColor(mfColor); + int MemUsage = (int) (((totalMemory - usedMemory) / totalMemory) * 10); + int i = 0; + for ( ; i < MemUsage ; i++) { + mfRect.setRect(x1+5,(float) y1+ssH+i*blockHeight, + blockWidth,(float) blockHeight-1); + big.fill(mfRect); + } + + // .. Memory Used .. + big.setColor(Color.green); + for ( ; i < 10; i++) { + muRect.setRect(x1+5,(float) y1 + ssH+i*blockHeight, + blockWidth,(float) blockHeight-1); + big.fill(muRect); + } + + // .. Draw History Graph .. + if (remainingWidth <= 30) remainingWidth = (float)30; + if (remainingHeight <= ssH) remainingHeight = (float)ssH; + big.setColor(graphColor); + int graphX = x1+30; + int graphY = y1 + (int) ssH; + int graphW = (int) remainingWidth; + int graphH = (int) remainingHeight; + + graphOutlineRect.setRect(graphX, graphY, graphW, graphH); + big.draw(graphOutlineRect); + + int graphRow = graphH/10; + + // .. Draw row .. + for (int j = graphY; j <= graphH+graphY; j += graphRow) { + graphLine.setLine(graphX,j,graphX+graphW,j); + big.draw(graphLine); + } + + // .. Draw animated column movement .. + int graphColumn = graphW/15; + + if (columnInc == 0) { + columnInc = graphColumn; + } + + for (int j = graphX+columnInc; j < graphW+graphX; j+=graphColumn) { + graphLine.setLine(j,graphY,j,graphY+graphH); + big.draw(graphLine); + } + + --columnInc; + + // Plot memory usage by this memory pool. + if (usedMem[npool] == null) { + usedMem[npool] = new float[usageHistCount]; + ptNum[npool] = 0; + } + + // save memory usage history. + usedMem[npool][ptNum[npool]] = usedMemory; + + big.setColor(Color.yellow); + + int w1; // width of memory usage history. + if (ptNum[npool] > graphW) { + w1 = graphW; + } else { + w1 = ptNum[npool]; + } + + + for (int j=graphX+graphW-w1, k=ptNum[npool]-w1; k < ptNum[npool]; + k++, j++) { + if (k != 0) { + if (usedMem[npool][k] != usedMem[npool][k-1]) { + int h1 = (int)(graphY + graphH * ((totalMemory -usedMem[npool][k-1])/totalMemory)); + int h2 = (int)(graphY + graphH * ((totalMemory -usedMem[npool][k])/totalMemory)); + big.drawLine(j-1, h1, j, h2); + } else { + int h1 = (int)(graphY + graphH * ((totalMemory -usedMem[npool][k])/totalMemory)); + big.fillRect(j, h1, 1, 1); + } + } + } + if (ptNum[npool]+2 == usedMem[npool].length) { + // throw out oldest point + for (int j = 1;j < ptNum[npool]; j++) { + usedMem[npool][j-1] = usedMem[npool][j]; + } + --ptNum[npool]; + } else { + ptNum[npool]++; + } + } + + + public void start() { + thread = new Thread(this); + thread.setPriority(Thread.MIN_PRIORITY); + thread.setName("MemoryMonitor"); + thread.start(); + } + + + public synchronized void stop() { + thread = null; + notify(); + } + + public void run() { + + Thread me = Thread.currentThread(); + + while (thread == me && !isShowing() || getSize().width == 0) { + try { + thread.sleep(500); + } catch (InterruptedException e) { return; } + } + + while (thread == me && isShowing()) { + Dimension d = getSize(); + if (d.width != w || d.height != h) { + w = d.width; + h = d.height; + bimg = (BufferedImage) createImage(w, h); + big = bimg.createGraphics(); + big.setFont(font); + FontMetrics fm = big.getFontMetrics(font); + ascent = (int) fm.getAscent(); + descent = (int) fm.getDescent(); + } + repaint(); + try { + thread.sleep(sleepAmount); + } catch (InterruptedException e) { break; } + if (MemoryMonitor.dateStampCB.isSelected()) { + System.out.println(new Date().toString() + " " + usedStr); + } + } + thread = null; + } + } + + + // Test thread to consume memory + static class Memeater extends ClassLoader implements Runnable { + Object y[]; + public Memeater() {} + public void run() { + y = new Object[10000000]; + int k =0; + while(true) { + if (k == 5000000) k=0; + y[k++] = new Object(); + try { + Thread.sleep(20); + } catch (Exception x){} + + // to consume perm gen storage + try { + // the classes are small so we load 10 at a time + for (int i=0; i<10; i++) { + loadNext(); + } + } catch (ClassNotFoundException x) { + // ignore exception + } + + } + + } + + Class loadNext() throws ClassNotFoundException { + + // public class TestNNNNNN extends java.lang.Object{ + // public TestNNNNNN(); + // Code: + // 0: aload_0 + // 1: invokespecial #1; //Method java/lang/Object."<init>":()V + // 4: return + // } + + int begin[] = { + 0xca, 0xfe, 0xba, 0xbe, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x0a, 0x0a, 0x00, 0x03, 0x00, 0x07, 0x07, + 0x00, 0x08, 0x07, 0x00, 0x09, 0x01, 0x00, 0x06, + 0x3c, 0x69, 0x6e, 0x69, 0x74, 0x3e, 0x01, 0x00, + 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, + 0x6f, 0x64, 0x65, 0x0c, 0x00, 0x04, 0x00, 0x05, + 0x01, 0x00, 0x0a, 0x54, 0x65, 0x73, 0x74 }; + + int end [] = { + 0x01, 0x00, 0x10, + 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, + 0x67, 0x2f, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x00, 0x21, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x05, 0x2a, 0xb7, 0x00, 0x01, 0xb1, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00 }; + + + // TestNNNNNN + + String name = "Test" + Integer.toString(count++); + + byte value[]; + try { + value = name.substring(4).getBytes("UTF-8"); + } catch (java.io.UnsupportedEncodingException x) { + throw new Error(); + } + + // construct class file + + int len = begin.length + value.length + end.length; + byte b[] = new byte[len]; + int i, pos=0; + for (i=0; i<begin.length; i++) { + b[pos++] = (byte)begin[i]; + } + for (i=0; i<value.length; i++) { + b[pos++] = value[i]; + } + for (i=0; i<end.length; i++) { + b[pos++] = (byte)end[i]; + } + + return defineClass(name, b, 0, b.length); + + } + static int count = 100000; + + } + + public static void main(String s[]) { + final MemoryMonitor demo = new MemoryMonitor(); + WindowListener l = new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + public void windowDeiconified(WindowEvent e) { demo.surf.start(); } + public void windowIconified(WindowEvent e) { demo.surf.stop(); } + }; + JFrame f = new JFrame("MemoryMonitor"); + f.addWindowListener(l); + f.getContentPane().add("Center", demo); + f.pack(); + f.setSize(new Dimension(400,500)); + f.setVisible(true); + demo.surf.start(); + Thread thr = new Thread(new Memeater()); + thr.start(); + } + +} diff --git a/src/share/demo/management/MemoryMonitor/README.txt b/src/share/demo/management/MemoryMonitor/README.txt new file mode 100644 index 0000000000..4133bab29c --- /dev/null +++ b/src/share/demo/management/MemoryMonitor/README.txt @@ -0,0 +1,48 @@ +# +# Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of Sun Microsystems nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +MemoryMonitor demonstrates the use of the java.lang.management API +in observing the memory usage of all memory pools consumed by +the application. + +This simple demo program queries the memory usage of each memory pool +and plots the memory usage history graph. + +To run the MemoryMonitor demo + + java -jar <JDK_HOME>/demo/management/MemoryMonitor.jar + +These instructions assume that this installation's version of the java +command is in your path. If it isn't, then you should either +specify the complete path to the java command or update your +PATH environment variable as described in the installation +instructions for the Java(TM) SDK. + diff --git a/src/share/demo/management/VerboseGC/PrintGCStat.java b/src/share/demo/management/VerboseGC/PrintGCStat.java new file mode 100644 index 0000000000..5ac52d77d2 --- /dev/null +++ b/src/share/demo/management/VerboseGC/PrintGCStat.java @@ -0,0 +1,146 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + */ + +import static java.lang.management.ManagementFactory.*; +import java.lang.management.*; +import javax.management.*; +import java.io.*; +import java.util.*; + +/** + * Example of using the java.lang.management API to monitor + * the memory usage and garbage collection statistics. + * + * @author Mandy Chung + */ +public class PrintGCStat { + private RuntimeMXBean rmbean; + private MemoryMXBean mmbean; + private List<MemoryPoolMXBean> pools; + private List<GarbageCollectorMXBean> gcmbeans; + + /** + * Constructs a PrintGCStat object to monitor a remote JVM. + */ + public PrintGCStat(MBeanServerConnection server) throws IOException { + // Create the platform mxbean proxies + this.rmbean = newPlatformMXBeanProxy(server, + RUNTIME_MXBEAN_NAME, + RuntimeMXBean.class); + this.mmbean = newPlatformMXBeanProxy(server, + MEMORY_MXBEAN_NAME, + MemoryMXBean.class); + ObjectName poolName = null; + ObjectName gcName = null; + try { + poolName = new ObjectName(MEMORY_POOL_MXBEAN_DOMAIN_TYPE+",*"); + gcName = new ObjectName(GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE+",*"); + } catch (MalformedObjectNameException e) { + // should not reach here + assert(false); + } + + Set mbeans = server.queryNames(poolName, null); + if (mbeans != null) { + pools = new ArrayList<MemoryPoolMXBean>(); + Iterator iterator = mbeans.iterator(); + while (iterator.hasNext()) { + ObjectName objName = (ObjectName) iterator.next(); + MemoryPoolMXBean p = + newPlatformMXBeanProxy(server, + objName.getCanonicalName(), + MemoryPoolMXBean.class); + pools.add(p); + } + } + + mbeans = server.queryNames(gcName, null); + if (mbeans != null) { + gcmbeans = new ArrayList<GarbageCollectorMXBean>(); + Iterator iterator = mbeans.iterator(); + while (iterator.hasNext()) { + ObjectName objName = (ObjectName) iterator.next(); + GarbageCollectorMXBean gc = + newPlatformMXBeanProxy(server, + objName.getCanonicalName(), + GarbageCollectorMXBean.class); + gcmbeans.add(gc); + } + } + } + + /** + * Constructs a PrintGCStat object to monitor the local JVM. + */ + public PrintGCStat() { + // Obtain the platform mxbean instances for the running JVM. + this.rmbean = getRuntimeMXBean(); + this.mmbean = getMemoryMXBean(); + this.pools = getMemoryPoolMXBeans(); + this.gcmbeans = getGarbageCollectorMXBeans(); + } + + /** + * Prints the verbose GC log to System.out to list the memory usage + * of all memory pools as well as the GC statistics. + */ + public void printVerboseGc() { + System.out.print("Uptime: " + formatMillis(rmbean.getUptime())); + for (GarbageCollectorMXBean gc : gcmbeans) { + System.out.print(" [" + gc.getName() + ": "); + System.out.print("Count=" + gc.getCollectionCount()); + System.out.print(" GCTime=" + formatMillis(gc.getCollectionTime())); + System.out.print("]"); + } + System.out.println(); + for (MemoryPoolMXBean p : pools) { + System.out.print(" [" + p.getName() + ":"); + MemoryUsage u = p.getUsage(); + System.out.print(" Used=" + formatBytes(u.getUsed())); + System.out.print(" Committed=" + formatBytes(u.getCommitted())); + System.out.println("]"); + } + } + + private String formatMillis(long ms) { + return String.format("%.4fsec", ms / (double) 1000); + } + private String formatBytes(long bytes) { + long kb = bytes; + if (bytes > 0) { + kb = bytes / 1024; + } + return kb + "K"; + } +} diff --git a/src/share/demo/management/VerboseGC/README.txt b/src/share/demo/management/VerboseGC/README.txt new file mode 100644 index 0000000000..f7a9011e42 --- /dev/null +++ b/src/share/demo/management/VerboseGC/README.txt @@ -0,0 +1,31 @@ +VerboseGC demonstrates the use of the java.lang.management API to +print the garbage collection statistics and memory usage remotely +by connecting to a JMX agent with a JMX service URL: + service:jmx:rmi:///jndi/rmi://<hostName>:<portNum>/jmxrmi +where <hostName> is the hostname and <portNum> is the port number +to which the JMX agent will be connected. + +To run the VerboseGC demo + +(1) Start the application with the JMX agent - here's an example of + how the Java2D is started + + java -Dcom.sun.management.jmxremote.port=1090 + -Dcom.sun.management.jmxremote.ssl=false + -Dcom.sun.management.jmxremote.authenticate=false + -jar <JDK_HOME>/demo/jfc/Java2D/Java2Demo.jar + + This instruction uses the Sun's built-in support to enable a JMX agent. + You can programmatically start a JMX agent with the RMI connector + using javax.management.remote API. See the javadoc and examples for + javax.management.remote API for details. + +(2) Run VerboseGC + + java -jar <JDK_HOME>/demo/management/VerboseGC/VerboseGC.jar localhost:1090 + +These instructions assume that this installation's version of the java +command is in your path. If it isn't, then you should either +specify the complete path to the java command or update your +PATH environment variable as described in the installation +instructions for the Java(TM) SDK. diff --git a/src/share/demo/management/VerboseGC/VerboseGC.java b/src/share/demo/management/VerboseGC/VerboseGC.java new file mode 100644 index 0000000000..bb8e06f08e --- /dev/null +++ b/src/share/demo/management/VerboseGC/VerboseGC.java @@ -0,0 +1,150 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Sun Microsystems nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + */ + +import javax.management.*; +import javax.management.remote.*; +import java.io.IOException; +import java.net.MalformedURLException; + +/** + * This VerboseGC class demonstrates the capability to get + * the garbage collection statistics and memory usage remotely. + */ +public class VerboseGC { + private MBeanServerConnection server; + private JMXConnector jmxc; + public VerboseGC(String hostname, int port) { + System.out.println("Connecting to " + hostname + ":" + port); + + // Create an RMI connector client and connect it to + // the RMI connector server + String urlPath = "/jndi/rmi://" + hostname + ":" + port + "/jmxrmi"; + connect(urlPath); + } + + public void dump(long interval, long samples) { + try { + PrintGCStat pstat = new PrintGCStat(server); + for (int i = 0; i < samples; i++) { + pstat.printVerboseGc(); + try { + Thread.sleep(interval); + } catch (InterruptedException e) { + System.exit(1); + } + } + } catch (IOException e) { + System.err.println("\nCommunication error: " + e.getMessage()); + System.exit(1); + } + } + + /** + * Connect to a JMX agent of a given URL. + */ + private void connect(String urlPath) { + try { + JMXServiceURL url = new JMXServiceURL("rmi", "", 0, urlPath); + this.jmxc = JMXConnectorFactory.connect(url); + this.server = jmxc.getMBeanServerConnection(); + } catch (MalformedURLException e) { + // should not reach here + } catch (IOException e) { + System.err.println("\nCommunication error: " + e.getMessage()); + System.exit(1); + } + } + + public static void main(String[] args) { + if (args.length < 1) { + usage(); + } + + String hostname = ""; + int port = -1; + long interval = 5000; // default is 5 second interval + long mins = 5; + for (int argIndex = 0; argIndex < args.length; argIndex++) { + String arg = args[argIndex]; + if (args[argIndex].startsWith("-")) { + if (arg.equals("-h") || + arg.equals("-help") || + arg.equals("-?")) { + usage(); + } else if (arg.startsWith("-interval=")) { + try { + interval = Integer.parseInt(arg.substring(10)) * 1000; + } catch (NumberFormatException ex) { + usage(); + } + } else if (arg.startsWith("-duration=")) { + try { + mins = Integer.parseInt(arg.substring(10)); + } catch (NumberFormatException ex) { + usage(); + } + } else { + // Unknown switch + System.err.println("Unrecognized option: " + arg); + usage(); + } + } else { + String[] arg2 = arg.split(":"); + if (arg2.length != 2) { + usage(); + } + hostname = arg2[0]; + try { + port = Integer.parseInt(arg2[1]); + } catch (NumberFormatException x) { + usage(); + } + if (port < 0) { + usage(); + } + } + } + + // get full thread dump and perform deadlock detection + VerboseGC vgc = new VerboseGC(hostname, port); + long samples = (mins * 60 * 1000) / interval; + vgc.dump(interval, samples); + + } + + private static void usage() { + System.out.print("Usage: java VerboseGC <hostname>:<port> "); + System.out.println(" [-interval=seconds] [-duration=minutes]"); + } +} diff --git a/src/share/demo/management/index.html b/src/share/demo/management/index.html new file mode 100644 index 0000000000..f9f3506422 --- /dev/null +++ b/src/share/demo/management/index.html @@ -0,0 +1,46 @@ +<html> +<head> <title>java.lang.management Demonstration Code</title> </head> + + +<h1>java.lang.management Demonstration Code</h1> + +<ul> + +<li> +<A HREF="FullThreadDump">FullThreadDump</A> +<br> +Shows how to get thread dumps and look for deadlocks. +</li> + +<li> +<A HREF="VerboseGC">VerboseGC</A> +<br> +Shows how you can find out about Garbage Collection in the VM. +</li> + +<li> +<A HREF="MemoryMonitor">MemoryMonitor</A> +<br> +Shows how you can find out the memory usage in the VM. +</li> + +<li> +<A HREF="JTop">JTop</A> +<br> +Shows how you can find out the threads with top CPU usage. +</li> + + +</ul> + + + +<h2>Comments and Feedback</h2> + +<p> +Comments regarding java.lang.management API or on any of these +demonstrations should be sent through +<A HREF="http://java.sun.com/mail">http://java.sun.com/mail/</A> + + +</html> |