java linux 内置工具_Java内置工具包tools.jar(二)sun.tools.jstack.JStack

jstack命令或许是java开发人员在排查问题最常用的命令之一,它输出了当前时刻指定进程中java线程的堆栈信息。我们从jstack开始阅读,它的入口在sun.tools.jstack.JStack中。

在参数校验的逻辑之后,我们发现有两个入口 runJStackTool 和 runThreadDump ,这里涉及到两种实现。runJStackTool 是SA的jstack实现,由于它是在进程之外的审视工具,所以jstack在普通模式导不出数据时(hung)加上-F参数即可使用它来导出进程信息。它的实现本文不做解释(TODO)。在runThreadDump中我们看到

VirtualMachine.attach(pid)

这里是获取进程信息的关键,这里使用的就是Hotspot的attach,它提供了进程之间互相通信的机制,再这里可以描述成为jstack命令和目标jvm进程之间的通信连接。如何进行连接,先来看一份jstack结果中的两个线程:

"Attach Listener" daemon prio=10 tid=0x000000000755f800 nid=0xe74 waiting on condition [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:

- None

"Signal Dispatcher" daemon prio=10 tid=0x0000000007563000 nid=0xa00 runnable [0x0000000000000000]

java.lang.Thread.State: RUNNABLE

Locked ownable synchronizers:

- None

attach和这两个线程有何关系,请attach的linux实现LinuxVirtualMachine,在attach(pid)中,它做了下面几个事情:

1、查找pid文件。

static void SendQuitCallback(const pid_t pid, void* user_data) {

SendQuitContext* context = (SendQuitContext*)user_data;

pid_t parent = getParent(pid);

if (parent == context->ppid) {

kill(pid, SIGQUIT);

}

}

/*

* Class: sun_tools_attach_LinuxVirtualMachine

* Method: sendQuitToChildrenOf

* Signature: (I)V

*/

JNIEXPORT void JNICALL Java_sun_tools_attach_LinuxVirtualMachine_sendQuitToChildrenOf

(JNIEnv *env, jclass cls, jint pid)

{

SendQuitContext context;

context.ppid = (pid_t)pid;

/*

* Iterate over all children of 'pid' and send a QUIT signal to each.

*/

forEachProcess(SendQuitCallback, (void*)&context);

}

可以看到实际是向linux进程发起了kill(pid, SIGQUIT);信号。该信号在jvm中只有一个线程在监听:那就是上图中的“Signal Dispatcher”,可以从监听代码中得到一些信息:

79c2a6506e0f8a9f8aaa30d05533c055.png

即执行了AttachListener::is_init_trigger(),在AttachListener的初始化中我们看到了一行熟悉的名字:

const char thread_name[] = "Attach Listener";

没错,就是创建了一个新的线程即Attach Listener。该线程在init以后随即创建了一个文件sprintf(fn, ".attach_pid%d", os::current_process_id()); PID文件以及监听了该文件的socket端口,于此同时,attach(pid)做了第三件事:

3、循环等待直至发现由Attach Listener创建的pid文件。

4、检查文件权限,建立socket通道。

至此Attach和jstack“撩上了”。

接下来是执行了方法JStack#runThreadDump,可以看到向pid文件中发起了数据写入

this.writeString(s, "1");

this.writeString(s, "threaddump");

从attach方法表中对应的方法有如下定义:

static AttachOperationFunctionInfo funcs[] = {

{ "agentProperties", get_agent_properties },

{ "datadump", data_dump },

{ "dumpheap", dump_heap },

{ "load", JvmtiExport::load_agent_library },

{ "properties", get_system_properties },

{ "threaddump", thread_dump },

{ "inspectheap", heap_inspection },

{ "setflag", set_flag },

{ "printflag", print_flag },

{ "jcmd", jcmd },

{ NULL, NULL }

};

对应的方法中使用VM_PrintThreads打印了线程信息,然后返回给jstack。值得注意的是,在导出线程信息时需要所有线程位于安全点(is_at_safepoint),此刻所有线程将阻塞,然后获取所有线程数据后释放线程锁,格式化输出。

// Implementation of "threaddump" command - essentially a remote ctrl-break

// See also: ThreadDumpDCmd class

//

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;

}

至此,线程信息就输出到你的眼前了。

总结下来,由外部线程(jstack)发起握手命令(SIGQUIT),目标jvm监听该命令后启动attach机制,外部线程检查attch机制响应,从而建立链接会话,然后根据请求返回数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值