jps,jmap,jstack等一系列jdk tools的实现原理.

jmap,jstack,jconsole等一系列jdk所实现的小工具对学习JVM的内部原理和现实中的性能分析都很有用处.

这是我分析其实现原理中的笔记.

示例代码如下:

package com.hongl;

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;
import sun.tools.attach.HotSpotVirtualMachine;
import java.io.InputStream;


public class TestThreadDump {
public static void dumpStream(InputStream is) throws Exception{
byte [] b=new byte[128];
int n;

do{
n=is.read(b);
if (n>0){
String s=new String(b,"UTF-8");
System.out.print(s);
}
}while (n>0);
}

public static void main(String [] args){
HotSpotVirtualMachine vm=null;

if (args.length == 0 ){
System.out.println("Usage:TestThreadDump pid");
System.out.println("Choose following process:");
//jps
for (VirtualMachineDescriptor jp :VirtualMachine.list()){
System.out.println(jp.id()+":"+jp.displayName());
}
return;
}

try{
vm = (HotSpotVirtualMachine)VirtualMachine.attach(args[0]);
//jstack
InputStream ins=vm.remoteDataDump("-1");
dumpStream(ins);

//jmap -dump
ins = vm.dumpHeap("stack.dmp");
dumpStream(ins);
}
catch (Exception e){
System.err.println("attach failed");
}
finally{
try{
if (vm != null){
vm.detach();
}
}catch (Exception e){}
}
}
}

关键的Java类是com.sun.tools.attach.VirtualMachine,注意其位于JDK_HOME\lib\tools.jar中,编译时要额外加入.

VirtualMachine的静态方法list()会返回一个包含所有java进程的List,这就是jps的实现原理.

静态方法attach(pid)则返回一个和该Java进程连接的HotSpotVirtualMachine示例.

调用这个实例的remoteDataDump方法返回这个进程内的所有线程的calling stacks,即jstack.

调用dumpHeap方法返回jvm堆的文件镜像,即jmap.

还有setFlag,printFlag等,即jinfo -flags.

 

那么,再往下,这些方法又是怎么实现的呢?

居然是使用了远程代码注入的方法.下面是windows上的实现.

JNIEXPORT jlong JNICALL Java_sun_tools_attach_WindowsVirtualMachine_createPipe
(JNIEnv *env, jclass cls, jstring pipename)
{

 ....

 hPipe = CreateNamedPipe(

name, // pipe name
PIPE_ACCESS_INBOUND, // read access
PIPE_TYPE_BYTE | // byte mode
PIPE_READMODE_BYTE |
PIPE_WAIT, // blocking mode
1, // max. instances
128, // output buffer size
8192, // input buffer size
NMPWAIT_USE_DEFAULT_WAIT, // client time-out
NULL); // default security attribute

....

}

JNIEXPORT void JNICALL Java_sun_tools_attach_WindowsVirtualMachine_enqueue
(JNIEnv *env, jclass cls, jlong handle, jbyteArray stub, jstring cmd,
jstring pipename, jobjectArray args)
{
...
strcpy(data.jvmLib, "jvm");
strcpy(data.func1, "JVM_EnqueueOperation");
strcpy(data.func2, "_JVM_EnqueueOperation@20");
...
pData = (DataBlock*) VirtualAllocEx( hProcess, 0, sizeof(DataBlock), MEM_COMMIT, PAGE_READWRITE );
...
WriteProcessMemory( hProcess, (LPVOID)pData, (LPCVOID)&data, (SIZE_T)sizeof(DataBlock), NULL );
...
pCode = (PDWORD) VirtualAllocEx( hProcess, 0, stubLen, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
...
WriteProcessMemory( hProcess, (LPVOID)pCode, (LPCVOID)stubCode, (SIZE_T)stubLen, NULL );
hThread = CreateRemoteThread( hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE) pCode,
pData,
0,
NULL );
...
}

/*
* Code copied to target process
*/
#pragma check_stack (off)
static DWORD WINAPI thread_func(DataBlock *pData)
{
HINSTANCE h;
EnqueueOperationFunc addr;

h = pData->_GetModuleHandle(pData->jvmLib);
if (h == NULL) {
return ERR_OPEN_JVM_FAIL;
}

addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func1));
if (addr == NULL) {
addr = (EnqueueOperationFunc)(pData->_GetProcAddress(h, pData->func2));
}
if (addr == NULL) {
return ERR_GET_ENQUEUE_FUNC_FAIL;
}

/* "null" command - does nothing in the target VM */
if (pData->cmd[0] == ‘\0‘) {
return 0;
} else {
return (*addr)(pData->cmd, pData->arg[0], pData->arg[1], pData->arg[2], pData->pipename);
}
}

/* This function marks the end of thread_func. */
static void thread_end (void) {
}
#pragma check_stack

大概流程如下:

首先创建一个命名管道,打开相应的java进程,设定自身对其完全权限,用VirtualAllocEx在对方进程地址空间中分配2个段,然后分别把参数和自身的函数thread_func二进制代码复制到这2个段,然后CreateRemoteThread实现代码注入.

那么注入的代码thread_func又是执行的什么呢?它通过GetProcAddress来获取对方java进程中jvm.dll的JVM_EnqueueOperation函数地址.

jps,jmap,jstack等一系列jdk tools的实现原理.

然后执行,JVM_EnqueueOperation和源进程通过刚才创建的命名管道通信,把执行的结果发送给源进程.

 

Linux稍微有些区别,操作进程和Java进程通过Unix Socket来通信,通过信号SIQUIT来同步.

jps,jmap,jstack等一系列jdk tools的实现原理.,布布扣,bubuko.com

jps,jmap,jstack等一系列jdk tools的实现原理.

上一篇:HTML 5 教程 canvas svg


下一篇:JS自带函数 -字符串处理