我想,没有简单的方法来获取监视器的地址。这是 jstack 是如何做到的
import com.sun.tools.attach.VirtualMachine;
import sun.tools.attach.HotSpotVirtualMachine;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
public class Main {
public static void main(String[] args) throws Exception {
VirtualMachine vm = VirtualMachine.attach(getPid());
HotSpotVirtualMachine hsvm = (HotSpotVirtualMachine) vm;
InputStream in = hsvm.remoteDataDump("-l");
byte b[] = new byte[256];
int n;
do {
n = in.read(b);
if (n > 0) {
String s = new String(b, 0, n, "UTF-8");
System.out.print(s);
}
} while (n > 0);
in.close();
}
private static String getPid() {
String name = ManagementFactory.getRuntimeMXBean().getName();
int ind = name.indexOf('@');
return name.substring(0, ind);
}
}
要运行此代码段,请不要忘记添加$JDK_HOME/lib/tools.jar
到类路径。
这是它产生的输出 2012-10-31 08:48:08 Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.5-b03 mixed mode):
"Monitor Ctrl-Break" daemon prio=6 tid=0x0000000006b98000 nid=0x1d70 runnable [0x00000000074df000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
- locked <0x00000007d5d53148> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:462)
at java.net.ServerSocket.accept(ServerSocket.java:430)
at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:82)
at java.lang.Thread.run(Thread.java:662)
Locked ownable synchronizers:
- None
...
让我们仔细看看是hsvm.remoteDataDump("-l")
什么
...
public InputStream remoteDataDump(Object ... args) throws IOException {
return executeCommand("threaddump", args);
}
/*
* Execute the given command in the target VM - specific platform
* implementation must implement this.
*/
abstract InputStream execute(String cmd, Object ... args)
throws AgentLoadException, IOException;
/*
* Convenience method for simple commands
*/
private InputStream executeCommand(String cmd, Object ... args) throws IOException {
try {
return execute(cmd, args);
} catch (AgentLoadException x) {
throw new InternalError("Should not get here");
}
}
...
这里是 windows 的 execute 方法的一个实现(你可以在 中找到它sun.tools.attach.WindowsVirtualMachine
)
InputStream execute(String cmd, Object ... args)
throws AgentLoadException, IOException {
assert args.length <= 3; // includes null
// create a pipe using a random name
int r = (new Random()).nextInt();
String pipename = "\\\\.\\pipe\\javatool" + r;
long hPipe = createPipe(pipename);
// check if we are detached - in theory it's possible that detach is invoked
// after this check but before we enqueue the command.
if (hProcess == -1) {
closePipe(hPipe);
throw new IOException("Detached from target VM");
}
try {
// enqueue the command to the process
enqueue(hProcess, stub, cmd, pipename, args);
// wait for command to complete - process will connect with the
// completion status
connectPipe(hPipe);
// create an input stream for the pipe
PipedInputStream is = new PipedInputStream(hPipe);
// read completion status
int status = readInt(is);
if (status != 0) {
// special case the load command so that the right exception is thrown
if (cmd.equals("load")) {
throw new AgentLoadException("Failed to load agent library");
} else {
throw new IOException("Command failed in target VM");
}
}
// return the input stream
return is;
} catch (IOException ioe) {
closePipe(hPipe);
throw ioe;
}
}
static native void init();
static native byte[] generateStub();
static native long openProcess(int pid) throws IOException;
static native void closeProcess(long hProcess) throws IOException;
static native long createPipe(String name) throws IOException;
static native void closePipe(long hPipe) throws IOException;
static native void connectPipe(long hPipe) throws IOException;
static native int readPipe(long hPipe, byte buf[], int off, int buflen) throws IOException;
static native void enqueue(long hProcess, byte[] stub,
String cmd, String pipename, Object ... args) throws IOException;
所以基本上命名管道是打开的,一些命令在它上面执行,所有的魔法都在本机代码中hotspot/src/share/vm/services/attachListener.cpp
// Implementation of "threaddump" command - essentially a remote ctrl-break
//
static jint thread_dump(AttachOperation* op, outputStream* out) {
bool print_concurrent_locks = false;
if (op->arg(0) != NULL && strcmp(op->arg(0), "-l") == 0) {
print_concurrent_locks = true;
}
// thread stacks
VM_PrintThreads op1(out, print_concurrent_locks);
VMThread::execute(&op1);
// JNI global handles
VM_PrintJNI op2(out);
VMThread::execute(&op2);
// Deadlock detection
VM_FindDeadlocks op3(out);
VMThread::execute(&op3);
return JNI_OK;
}
一般来说,如果您想提取您已获得监视器的对象的地址,您可以解析第一个片段的输出并提取必要的片段,例如,通过线程 id。
其他选项是在调试模式下附加到您的进程并使用调试器 api 或 JNI。