在java中,如何通过访问内存拿到线程列表,用于跟踪线程的运行状态,这也是jstack的主要功能。 在jvm里,有没有F的参数实现笔者前面的博客已经说明了。因为-F是通过访问java的内存来取的信息的,所以当使用-F参数的时候,需要知道java运行过程中内存的结构,从而通过访问内存能获取到你所需要的信息。
eg.
VMStructEntry 里面定义 typeName="Threads", fieldName ="_thread_list" typeString="JavaThread*" isstatic="1" offset="0" address="&Threads::_thread_list"
gHotSpotVMTypeEntryTypeNameOffset ,
gHotSpotVMTypeEntrySuperclassNameOffset ,
gHotSpotVMTypeEntryIsOopTypeOffset ,
gHotSpotVMTypeEntryIsIntegerTypeOffset,
gHotSpotVMTypeEntryIsUnsignedOffset,
gHotSpotVMTypeEntrySizeOffset,gHotSpotVMTypeEntryArrayStride
在jvm中定义的gHotSpotVMStructs ,和gHotSpotVMTypes是全局指针,可以直接通过访问符号表通过偏移计算得到真实的地址,请参考博客(Java 工具(jmap,jstack)在linux上的源码分析(六) -F 参数 读取动态链接共享库文件中的符号表)
然后通过上面一章节里提到的gHotSpotVM....参数获取到已经定义的类的名字,成员。
在thread.hpp里定义了在jvm里所定义的线程的类, 其中 Threads/ JavaThread 类如下
如何取的_thread_list?
查看宏定义VM_STRUCTS和VM_TYPES,可以看到已经将 _thread_list 和Threads加到VMStructEntry ,VMTypeEntry中去,因为对static 变量是保存地址的,所以可以通过访问当取的第一个线程后,取的 JavaThread 的VMStructEntry 中的offset,通过地址+offset取的_next的指针的地址,以此类推,通过链表遍历所有线程,知道_next的地址为0为止。
在jvm中定义了许多不同的内部的线程类型,都是通过继承javathread类来实现的,具体详细可参考在thread.hpp
当在遍历线程的列表,如何判断是什么线程的类型呢,对每个对象来说,指针的值就是对象的地址,读取这个地址的后8个byte(64位机器),所存放的值就是虚拟表中的标识这个类的地址。
这个虚拟表也在elf文件中,以_ZTV为标识, class CompilerThread 格式为 _ZTV14CompilerThread.
比较2者地址是否相等,就可以判断是否是这个类型的类。