Java/Development Class/Java Management API
Версия от 18:01, 31 мая 2010; (обсуждение)
Содержание
- 1 Demo code which plots the memory usage by all memory pools
- 2 Example of using the java.lang.management API to sort threads by CPU usage
- 3 MBeanTyperInvoker handles method invocations against the MBeanTyper target object and forwards them to the MBeanServer and ObjectName for invocation.
- 4 Non-instantiable class that provides jmx utility methods
- 5 Performing deadlock detection programmatically within the application using the java.lang.management API
- 6 Several utility functions for the JMX implementation
- 7 This FullThreadDump class demonstrates the capability to get a full thread dump and also detect deadlock remotely.
- 8 This VerboseGC class demonstrates the capability to get the garbage collection statistics and memory usage remotely
Demo code which plots the memory usage by all memory pools
/*
* @(#)MemoryMonitor.java 1.3 05/11/17
*
* Copyright (c) 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:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
/*
* @(#)MemoryMonitor.java 1.3 05/11/17
*/
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.util.Date;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
/**
* 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();
}
}
Example of using the java.lang.management API to sort threads by CPU usage
/*
* @(#)JTop.java 1.5 06/05/08
*
* Copyright (c) 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:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
/*
* @(#)JTop.java 1.5 06/05/08
*
* 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 static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME;
import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.io.IOException;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.net.MalformedURLException;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
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 javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
/**
* 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);
}
}
MBeanTyperInvoker handles method invocations against the MBeanTyper target object and forwards them to the MBeanServer and ObjectName for invocation.
/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
/**
* MBeanTyper is a helper class that creates a typed-object from an MBean
* ObjectName and a main interface class that the MBean implements. You can then
* use the returned object (casted to the appropriate main interface class) with
* the correct typed signatures instead of
* <tt>mbeanserver.invoke(objectname,<sig>,etc.)</tt>.
* <P>
* Example usage: <BR>
* <code><tt>
* MyInterfaceMBean mbean=(MyInterfaceMBean)MBeanTyper.typeMBean(server,new ObjectName(":type=MyBean"),MyInterfaceMBean.class);
* mbean.foobar();
* </tt></code>
* <P>
*
* To turn debug on for this package, set the System property
* <tt>vocalos.jmx.mbeantyper.debug</tt> to true.
*
* @author
*/
final class MBeanTyperInvoker implements java.lang.reflect.InvocationHandler {
private final MBeanServer server;
private final ObjectName mbean;
private final Map signatureCache = Collections.synchronizedMap(new HashMap());
MBeanTyperInvoker(MBeanServer server, ObjectName mbean) {
this.server = server;
this.mbean = mbean;
}
private boolean isJMXAttribute(Method m) {
String name = m.getName();
return (name.startsWith("get"));
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (MBeanTyper.DEBUG) {
System.err.println(" ++ method=" + method.getName() + ",args=" + args);
}
try {
if (method.getDeclaringClass() == Object.class) {
String name = method.getName();
if (name.equals("hashCode")) {
return new Integer(this.hashCode());
} else if (name.equals("toString")) {
return this.toString();
} else if (name.equals("equals")) {
// FIXME: this needs to be reviewed - we should be
// smarter about this ...
return new Boolean(equals(args[0]));
}
} else if (isJMXAttribute(method) && (args == null || args.length <= 0)) {
String name = method.getName().substring(3);
return server.getAttribute(mbean, name);
}
String sig[] = (String[]) signatureCache.get(method);
if (sig == null) {
// get the method signature from the method argument directly
// vs. the arguments passed, since there may be primitives that
// are wrapped as objects in the arguments
Class _args[] = method.getParameterTypes();
if (_args != null && _args.length > 0) {
sig = new String[_args.length];
for (int c = 0; c < sig.length; c++) {
if (_args[c] != null) {
sig[c] = _args[c].getName();
}
}
} else {
sig = new String[0];
}
signatureCache.put(method, sig);
}
return server.invoke(mbean, method.getName(), args, sig);
} catch (Throwable t) {
if (MBeanTyper.DEBUG) {
t.printStackTrace();
}
if (t instanceof UndeclaredThrowableException) {
UndeclaredThrowableException ut = (UndeclaredThrowableException) t;
throw ut.getUndeclaredThrowable();
} else if (t instanceof InvocationTargetException) {
InvocationTargetException it = (InvocationTargetException) t;
throw it.getTargetException();
} else if (t instanceof MBeanException) {
MBeanException me = (MBeanException) t;
throw me.getTargetException();
} else {
throw t;
}
}
}
}
Non-instantiable class that provides jmx utility methods
// Copyright 2007 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.logging.Logger;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.ReflectionException;
/**
* Non-instantiable class that provides jmx utility methods.
*/
public class Utilities {
/**
* Prints info about a bean to a {@link VarOutputSink}.
*
* @param sink The {@link VarOutputSink} to which info will be sent
* @param mbs The {@link MBeanServer} with respect to which the
* {@code objectName} is accessed
* @param objectName The {@link ObjectName} that identifies this bean
*/
public static void printMBeanInfo(VarOutputSink sink, MBeanServer mbs,
ObjectName objectName) {
MBeanInfo info = getMBeanInfoSafely(sink, mbs, objectName);
if (info == null) { return; }
sink.echo("\nCLASSNAME: \t" + info.getClassName());
sink.echo("\nDESCRIPTION: \t" + info.getDescription());
sink.echo("\nATTRIBUTES");
MBeanAttributeInfo[] attrInfo = info.getAttributes();
sink.printVariable("attrcount", Integer.toString(attrInfo.length));
if (attrInfo.length > 0) {
for (int i = 0; i < attrInfo.length; i++) {
sink.echo(" ** NAME: \t" + attrInfo[i].getName());
sink.echo(" DESCR: \t" + attrInfo[i].getDescription());
sink
.echo(" TYPE: \t" + attrInfo[i].getType() + "\tREAD: "
+ attrInfo[i].isReadable() + "\tWRITE: "
+ attrInfo[i].isWritable());
}
} else sink.echo(" ** No attributes **");
sink.echo("\nCONSTRUCTORS");
MBeanConstructorInfo[] constrInfo = info.getConstructors();
for (int i = 0; i < constrInfo.length; i++) {
sink.echo(" ** NAME: \t" + constrInfo[i].getName());
sink.echo(" DESCR: \t" + constrInfo[i].getDescription());
sink.echo(" PARAM: \t" + constrInfo[i].getSignature().length
+ " parameter(s)");
}
sink.echo("\nOPERATIONS");
MBeanOperationInfo[] opInfo = info.getOperations();
if (opInfo.length > 0) {
for (int i = 0; i < opInfo.length; i++) {
sink.echo(" ** NAME: \t" + opInfo[i].getName());
sink.echo(" DESCR: \t" + opInfo[i].getDescription());
sink.echo(" PARAM: \t" + opInfo[i].getSignature().length
+ " parameter(s)");
}
} else sink.echo(" ** No operations ** ");
sink.echo("\nNOTIFICATIONS");
MBeanNotificationInfo[] notifInfo = info.getNotifications();
if (notifInfo.length > 0) {
for (int i = 0; i < notifInfo.length; i++) {
sink.echo(" ** NAME: \t" + notifInfo[i].getName());
sink.echo(" DESCR: \t" + notifInfo[i].getDescription());
String notifTypes[] = notifInfo[i].getNotifTypes();
for (int j = 0; j < notifTypes.length; j++) {
sink.echo(" TYPE: \t" + notifTypes[j]);
}
}
} else sink.echo(" ** No notifications **");
}
private static MBeanInfo getMBeanInfoSafely(VarOutputSink sink,
MBeanServer mbs, ObjectName objectName) {
MBeanInfo info = null;
try {
info = mbs.getMBeanInfo(objectName);
} catch (InstanceNotFoundException e) {
sink.printVariable("ObjectName", "Not found");
} catch (IntrospectionException e) {
sink.printVariable("ObjectName", "IntrospectionException");
} catch (ReflectionException e) {
sink.printVariable("ObjectName", "ReflectionException");
}
return info;
}
/**
* Prints bean attributes to a {@link VarOutputSink}.
*
* @param sink The {@link VarOutputSink} to which attributes will be sent
* @param mbs The {@link MBeanServer} with respect to which the
* {@code objectName} is accessed
* @param objectName The {@link ObjectName} that identifies this bean
*/
public static void printMBeanAttributes(VarOutputSink sink, MBeanServer mbs,
ObjectName objectName) {
MBeanInfo info = getMBeanInfoSafely(sink, mbs, objectName);
if (info == null) {
sink.printVariable(objectName.getCanonicalName(), "can"t fetch info");
return;
}
MBeanAttributeInfo[] attrInfo = info.getAttributes();
if (attrInfo.length > 0) {
for (int i = 0; i < attrInfo.length; i++) {
String attrName = attrInfo[i].getName();
Object attrValue = null;
String attrValueString = null;
try {
attrValue = mbs.getAttribute(objectName, attrName);
} catch (AttributeNotFoundException e) {
attrValueString = "AttributeNotFoundException";
} catch (InstanceNotFoundException e) {
attrValueString = "InstanceNotFoundException";
} catch (MBeanException e) {
attrValueString = "MBeanException";
} catch (ReflectionException e) {
attrValueString = "ReflectionException";
}
if (attrValueString == null) {
attrValueString = attrValue.toString();
}
sink.printVariable(attrName, attrValueString);
}
}
}
/**
* Helper interface defining output sinks used with
* {@link Utilities#printMBeanInfo(com.google.enterprise.util.jmx.Utils.VarOutputSink, MBeanServer, ObjectName)}
* and
* {@link Utilities#printMBeanAttributes(com.google.enterprise.util.jmx.Utils.VarOutputSink, MBeanServer, ObjectName)}
*/
public interface VarOutputSink {
public void printVariable(String name, String value);
public void echo(String string);
}
/**
* Static {@link VarOutputSink} that uses {@link System#out}
*/
public static final VarOutputSink SYSTEM_OUT_SINK = new PrintStreamVarOutputSink(
System.out);
public static class LoggerVarOutputSink implements VarOutputSink {
private final Logger logger;
public LoggerVarOutputSink(Logger logger) {
this.logger = logger;
}
public void printVariable(String name, String value) {
logger.info(name + " " + value);
}
public void echo(String string) {
logger.info(string);
}
}
public static class PrintWriterVarOutputSink implements VarOutputSink {
private final PrintWriter printWriter;
public PrintWriterVarOutputSink(PrintWriter printWriter) {
this.printWriter = printWriter;
}
public void printVariable(String name, String value) {
printWriter.print(" <b>");
printWriter.print(name);
printWriter.print("</b> ");
printWriter.print(value);
printWriter.println("<br>");
}
public void echo(String string) {
printWriter.print(string);
}
}
public static class PrintStreamVarOutputSink implements VarOutputSink {
private final PrintStream printStream;
public PrintStreamVarOutputSink(PrintStream printStream) {
this.printStream = printStream;
}
public void printVariable(String name, String value) {
printStream.print(" <b>");
printStream.print(name);
printStream.print("</b> ");
printStream.print(value);
printStream.println("<br>");
}
public void echo(String string) {
printStream.print(string);
}
}
}
Performing deadlock detection programmatically within the application using the java.lang.management API
/*
* @(#)Deadlock.java 1.5 05/11/17
*
* Copyright (c) 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:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
/*
* @(#)Deadlock.java 1.5 05/11/17
*/
import static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME;
import static java.lang.management.ManagementFactory.getThreadMXBean;
import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy;
import java.io.IOException;
import java.lang.management.LockInfo;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
/**
* 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);
}
}
}
/*
* @(#)ThreadMonitor.java 1.6 05/12/22
*
* Copyright (c) 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:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS
* LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A
* RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
* IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT
* OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended for
* use in the design, construction, operation or maintenance of any nuclear
* facility.
*/
/*
* @(#)ThreadMonitor.java 1.6 05/12/22
*/
/**
* Example of using the java.lang.management API to dump stack trace and to
* perform deadlock detection.
*
* @author Mandy Chung
* @version %% 12/22/05
*/
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;
}
}
}
Several utility functions for the JMX implementation
/*
* Copyright (C) The MX4J Contributors.
* All rights reserved.
*
* This software is distributed under the terms of the MX4J License version 1.0.
* See the terms of the MX4J License in the documentation provided with this software.
*/
import java.lang.reflect.Array;
import java.lang.reflect.Method;
/**
* Several utility functions for the JMX implementation
*
* @version $Revision: 1.18 $
*/
public class Utils
{
/**
* This methods load a class given the classloader and the name of the class, and work for
* extended names of primitive types. <p>
* If you try to do ClassLoader.loadClass("boolean") it barfs it cannot find the class,
* so this method cope with this problem.
*/
public static Class loadClass(ClassLoader loader, String name) throws ClassNotFoundException
{
if (name == null) throw new ClassNotFoundException("null");
name = name.trim();
if (name.equals("boolean"))
return boolean.class;
else if (name.equals("byte"))
return byte.class;
else if (name.equals("char"))
return char.class;
else if (name.equals("short"))
return short.class;
else if (name.equals("int"))
return int.class;
else if (name.equals("long"))
return long.class;
else if (name.equals("float"))
return float.class;
else if (name.equals("double"))
return double.class;
else if (name.equals("java.lang.String"))
return String.class;
else if (name.equals("java.lang.Object"))
return Object.class;
else if (name.startsWith("["))
{
// It"s an array, figure out how many dimensions
int dimension = 0;
while (name.charAt(dimension) == "[")
{
++dimension;
}
char type = name.charAt(dimension);
Class cls = null;
switch (type)
{
case "Z":
cls = boolean.class;
break;
case "B":
cls = byte.class;
break;
case "C":
cls = char.class;
break;
case "S":
cls = short.class;
break;
case "I":
cls = int.class;
break;
case "J":
cls = long.class;
break;
case "F":
cls = float.class;
break;
case "D":
cls = double.class;
break;
case "L":
// Strip the semicolon at the end
String n = name.substring(dimension + 1, name.length() - 1);
cls = loadClass(loader, n);
break;
}
if (cls == null)
{
throw new ClassNotFoundException(name);
}
else
{
int[] dim = new int[dimension];
return Array.newInstance(cls, dim).getClass();
}
}
else
{
if (loader != null)
return loader.loadClass(name);
else
return Class.forName(name, false, null);
}
}
/**
* Returns the classes whose names are specified by the <code>names</code> argument, loaded with the
* specified classloader.
*/
public static Class[] loadClasses(ClassLoader loader, String[] names) throws ClassNotFoundException
{
int n = names.length;
Class[] cls = new Class[n];
for (int i = 0; i < n; ++i)
{
String name = names[i];
cls[i] = loadClass(loader, name);
}
return cls;
}
/**
* Returns true is the given method is a JMX attribute getter method
*/
public static boolean isAttributeGetter(Method m)
{
if (m == null) return false;
String name = m.getName();
Class retType = m.getReturnType();
Class[] params = m.getParameterTypes();
if (retType != Void.TYPE && params.length == 0)
{
if (name.startsWith("get") && name.length() > 3)
return true;
else if (name.startsWith("is") && name.length() > 2 && retType == Boolean.TYPE) return true;
}
return false;
}
/**
* Returns true if the method is a JMX attribute setter method
*/
public static boolean isAttributeSetter(Method m)
{
if (m == null) return false;
String name = m.getName();
Class retType = m.getReturnType();
Class[] params = m.getParameterTypes();
if (retType == Void.TYPE && params.length == 1 && name.startsWith("set") && name.length() > 3)
{
return true;
}
return false;
}
public static boolean wildcardMatch(String pattern, String string)
{
int stringLength = string.length();
int stringIndex = 0;
for (int patternIndex = 0; patternIndex < pattern.length(); ++patternIndex)
{
char c = pattern.charAt(patternIndex);
if (c == "*")
{
// Recurse with the pattern without this "*" and the actual string, until
// match is found or we inspected the whole string
while (stringIndex < stringLength)
{
if (wildcardMatch(pattern.substring(patternIndex + 1), string.substring(stringIndex)))
{
return true;
}
// No match found, try a shorter string, since we are matching "*"
++stringIndex;
}
}
else if (c == "?")
{
// Increment the string index, since "?" match a single char in the string
++stringIndex;
if (stringIndex > stringLength)
{
return false;
}
}
else
{
// A normal character in the pattern, must match the one in the string
if (stringIndex >= stringLength || c != string.charAt(stringIndex))
{
return false;
}
++stringIndex;
}
}
// I"ve inspected the whole pattern, but not the whole string
return stringIndex == stringLength;
}
public static boolean arrayEquals(Object[] arr1, Object[] arr2)
{
if (arr1 == null && arr2 == null) return true;
if (arr1 == null ^ arr2 == null) return false;
if (!arr1.getClass().equals(arr2.getClass())) return false;
if (arr1.length != arr2.length) return false;
for (int i = 0; i < arr1.length; ++i)
{
Object obj1 = arr1[i];
Object obj2 = arr2[i];
if (obj1 == null ^ obj2 == null) return false;
if (obj1 != null && !obj1.equals(obj2)) return false;
}
return true;
}
public static boolean arrayEquals(byte[] arr1, byte[] arr2)
{
if (arr1 == null && arr2 == null) return true;
if (arr1 == null ^ arr2 == null) return false;
if (!arr1.getClass().equals(arr2.getClass())) return false;
if (arr1.length != arr2.length) return false;
for (int i = 0; i < arr1.length; ++i)
{
byte b1 = arr1[i];
byte b2 = arr2[i];
if (b1 != b2) return false;
}
return true;
}
public static int arrayHashCode(Object[] arr)
{
int hash = 0;
if (arr != null)
{
// Avoid that 2 arrays of length 0 but different classes return same hash
hash ^= arr.getClass().hashCode();
for (int i = 0; i < arr.length; ++i)
{
hash ^= arr[i] == null ? 0 : arr[i].hashCode();
}
}
return hash;
}
public static int arrayHashCode(byte[] arr)
{
int hash = 0;
if (arr != null)
{
// Avoid that 2 arrays of length 0 but different classes return same hash
hash ^= arr.getClass().hashCode();
for (int i = 0; i < arr.length; ++i)
{
hash ^= arr[i];
}
}
return hash;
}
public static char[] arrayCopy(char[] chars)
{
if (chars == null) return null;
char[] copy = new char[chars.length];
System.arraycopy(chars, 0, copy, 0, chars.length);
return copy;
}
}
This FullThreadDump class demonstrates the capability to get a full thread dump and also detect deadlock remotely.
/*
* @(#)FullThreadDump.java 1.5 05/11/17
*
* Copyright (c) 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:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
/*
* @(#)FullThreadDump.java 1.5 05/11/17
*/
import static java.lang.management.ManagementFactory.THREAD_MXBEAN_NAME;
import static java.lang.management.ManagementFactory.getThreadMXBean;
import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy;
import java.io.IOException;
import java.lang.management.LockInfo;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.net.MalformedURLException;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
/**
* 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>");
}
}
/*
* @(#)ThreadMonitor.java 1.6 05/12/22
*
* Copyright (c) 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:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS
* LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A
* RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
* IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT
* OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended for
* use in the design, construction, operation or maintenance of any nuclear
* facility.
*/
/*
* @(#)ThreadMonitor.java 1.6 05/12/22
*/
/**
* Example of using the java.lang.management API to dump stack trace and to
* perform deadlock detection.
*
* @author Mandy Chung
* @version %% 12/22/05
*/
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;
}
}
}
This VerboseGC class demonstrates the capability to get the garbage collection statistics and memory usage remotely
/*
* @(#)VerboseGC.java 1.4 05/11/17
*
* Copyright (c) 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:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
/*
* @(#)VerboseGC.java 1.4 05/11/17
*/
import static java.lang.management.ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE;
import static java.lang.management.ManagementFactory.MEMORY_MXBEAN_NAME;
import static java.lang.management.ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE;
import static java.lang.management.ManagementFactory.RUNTIME_MXBEAN_NAME;
import static java.lang.management.ManagementFactory.getGarbageCollectorMXBeans;
import static java.lang.management.ManagementFactory.getMemoryMXBean;
import static java.lang.management.ManagementFactory.getMemoryPoolMXBeans;
import static java.lang.management.ManagementFactory.getRuntimeMXBean;
import static java.lang.management.ManagementFactory.newPlatformMXBeanProxy;
import java.io.IOException;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
/**
* 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]");
}
}
/*
* @(#)PrintGCStat.java 1.4 05/11/17
*
* Copyright (c) 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:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS
* LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A
* RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
* IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT
* OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended for
* use in the design, construction, operation or maintenance of any nuclear
* facility.
*/
/*
* @(#)PrintGCStat.java 1.4 05/11/17
*/
/**
* Example of using the java.lang.management API to monitor the memory usage and
* garbage collection statistics.
*
* @author Mandy Chung
* @version %% 11/17/05
*/
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";
}
}