【Java精品源码栏目提醒】:网学会员,鉴于大家对Java精品源码十分关注,论文会员在此为大家搜集整理了“【精品】Java开源工具在linux上的源码分析(详细说明配有图片) - 其它资料”一文,供大家参考学习!
Java 开源工具在 linux 上的
源码分析 详细说明配有图片 目录索引
Java 开源工具在 linux 上的
源码分析一:跟踪方式 ................................................................ 2
Java 开源工具在 linux 上的
源码分析二:信号处理 ................................................................ 4
Java 开源工具在 linux 上的
源码分析三:执行的线程 ............................................................ 9
Java 开源工具在 linux 上的
源码分析四:safe point ........................................................... 13
Java 开源工具在 linux 上的
源码分析五:-F 参数的 bug ..................................................... 19
Java 开源工具在 linux 上的
源码分析六:符号表的读取 ...................................................... 22
Java 开源工具在 linux 上的
源码分析一:跟踪方式在我们常用的 Jstack Jmap 用于分析
java 虚拟机的状态的工具,通过起另一个虚拟机通过运行 sun.tools 包下的
java 文件,去跟踪另一个虚拟机的状态。
AD: 在我们常用的 Jstack Jmap 用于分析
java 虚拟机的状态的工具,通过起另一个虚拟机通过运行 sun.tools 包下的
java 文件,去跟踪另一个虚拟机的状态。
如果让你设计一个跟踪另一个进程的方法,你也通常会考虑这几种常用的方式。
第一种,就是通知被跟踪的进程,让进程执行相应的消息,同时对该消息做出反应。
第二种,就是通过内核的调用,直接能够访问进程的内存,堆栈情况,通过分析被跟踪的进程的内存结构,从而知道当前被跟踪的进程的状态。
第一种方式 优势: 对调用者和被调用者只要达成简单的通讯协议,调用者无需知道被调用者的逻辑,结构,只需要简单的发送命令的方式,被调用者能够接受到命令,并且对该命令进行回应就可以。
缺点: 如果被调用者当时的状态本来就不正常,或者繁忙,没办法对该命令做出响应,那这个跟踪进程往往是在规定的等待时间里,无法返回正确的需要的信息。
其次被调用者在分析的过程中,有可能需要暂停进程中的其他的线程,而对被跟踪的进程有一定的影响。
第二种方式 优势: 通过内核的支持,访问被跟踪的内存,并作出快照,后台分析,很少影响被跟踪的进程。
缺点: 这种方式需要对被跟踪程的内存分配和使用非常的了解,无法解耦,而本身系统内核调用也会出问题。
Java 工具类中也是大致实现了这2中方式,工具中会先选择第一种方式,如果发现第一种方式不能成功,将会建议使用-F 参数,也就是第二种方式。
我们先讲第一种方式。
既然是需要向被跟踪进程发出命令,在 linux 中可以选择多种方式进行进程中通讯 共享内存,文件之类,其中创建 socket 的文件实现通讯是比较简单的方法。
下面是整个的流程图: 原文链接:http://blog.csdn.net/raintungli/article/details/7023092
Java 开源工具在 linux 上的
源码分析二:信号处理当
java 虚拟机启动的时候,会启动很多内部的线程,这些线程主要在 thread.cpp 里的create_vm 方法体里实现。
而在 thread.cpp 里主要起了2个线程来处理信号相关的。
详细请看下文 AD: 当
java 虚拟机启动的时候,会启动很多内部的线程,这些线程主要在 thread.cpp 里的create_vm 方法体里实现。
而在 thread.cpp 里主要起了2个线程来处理信号相关的: 1 JvmtiExport::enter_live_phase 2 3 // Signal Dispatcher needs to be started before VMInit event is posted 4 os::signal_init 5 6 // Start Attach Listener if StartAttachListener or it cant be started lazily 7 if DisableAttachMechanism 8 if StartAttachListener AttachListener::init_at_startup 9 AttachListener::init 10 11 1. Signal Dispatcher 线程 在 os.cpp 中的 signal_init函数中,启动了 signal dispatcher 线程,对 signaldispather 线 程 主 要 是 用 于 处 理 信 号 , 等 待 信 号 并 且 分 发 处 理 , 可 以 详 细 看signal_thread_entry 的方法: 12 static void signal_thread_entryJavaThread thread TRAPS 13 os::set_prioritythread NearMaxPriority14 while true 15 int sig16 17 // FIXME : Currently we have not decieded what should be the status18 // for this
java thread blocked here. Once we decide about19 // that we should fix this.20 sig os::signal_wait21 22 if sig os::sigexitnum_pd 23 // Terminate the signal thread24 return25 2627 switch sig 28 case SIGBREAK: 29 // Check if the signal is a trigger to start the Attach Listener - in that30 // case dont print stack traces.31 if DisableAttachMechanism ampamp AttachListener::is_init_trigger 32 continue33 34 // Print stack traces35 // Any SIGBREAK operations added here should make sure to flush36 // the output stream e.g. tty-gtflush after output. See 4803766.37 // Each module also prints an extra carriage return after its output.38 VM_PrintThreads op39 VMThread::executeampop40 VM_PrintJNI jni_op41 VMThread::executeampjni_op42 VM_FindDeadlocks op1tty43 VMThread::executeampop144 Universe::print_heap_at_SIGBREAK45 if PrintClassHistogram 46 VM_GC_HeapInspection op1gclog_or_tty true / force full GC before heapinspection /47 true / need_prologue /48 VMThread::executeampop149 50 if JvmtiExport::should_post_data_dump 51 JvmtiExport::post_data_dump52 53 break54 55 default: 56 // Dispatch the signal to
java57 HandleMark hmTHREAD58 klassOop k SystemDictionary::resolve_or_nullvmSymbolHandles::sun_misc_SignalTHREAD59 KlassHandle klass THREAD k60 if klass.not_null 61 JavaValue resultT_VOID62 JavaCallArguments args63 args.push_intsig64 JavaCalls::call_static65 ampresult66 klass67 vmSymbolHandles::dispatch_name68 vmSymbolHandles::int_void_signature69 ampargs70 THREAD71 72 73 if HAS_PENDING_EXCEPTION 74 // tty is initialized early so we dont expect it to be null but75 // if it is we cant risk doing an initialization that might76 // trigger additional out-of-memory conditions77 if tty NULL 78 char klass_name25679 char tmp_sig_name1680 const char sig_name quotUNKNOWNquot81 instanceKlass::castPENDING_EXCEPTION-gtklass-gt82 name-gtas_klass_external_nameklass_name 25683 if os::exception_namesig tmp_sig_name 16 NULL84 sig_name tmp_sig_name85 warningquotException s occurred dispatching signal s to handlerquot86 quot- the VM may need to be forcibly terminatedquot87 klass_name sig_name 88 89 CLEAR_PENDING_EXCEPTION90 91 92 93 94 可以看到通过 os::signal_wait等待信号,而在 linux 里是通过 sem_wait来实现,接受到 SIGBREAKlinux 中的 QUIT信号的时候(关于信号处理请参考笔者的另一篇博客 :
java 中 关 于 信 号 的 处 理 在 linux 下 的 实 现 ), 第 一 次 通 过 调 用AttachListener::is_init_trigger()初始化 attach listener 线程,详细见2.Attach Listener线程。
第一次收到信号,会开始初始化,当初始化成功,将会直接返回,而且不返回任何线程stack 的信息(通过 socket file 的操作返回),并且第二次将不在需要初始化。
如果初始化不成功,将直接在控制台的 outputstream 中打印线程栈信息。
第二次收到信号,如果已经初始化过,将直接在控制台中打印线程的栈信息。
如果没有初始化,继续初始化,走和第一次相同的流程。
2. Attach Listener 线程 Attach Listener 线程是负责接收到外部的命令,而对该命令进行执行的并且吧结果返回给发送者。
在 jvm 启动的时候,如果没有指定StartAttachListener,该线程是不会启动的,刚才我们讨论到了在接受到 quit 信号之后,会调用 AttachListener::is_init_trigger()通过调用用 AttachListener::init()启动了 Attach Listener 线程,同时在不同的操作系统下初始化,在 linux 中 是在 attachListener_Linux.cpp 文件中实现的。
在 linux 中如果发现文件.attach_pidpid 存在,才会启动 attach listener 线程,同时初始化了 socket 文件,也就是通常 jmapjstack tool 干的事情,先创立 attach_pidpid文件,然后发 quit 信号通过这种方式暗式的启动了 Attach Listener 线程(见博客:http://blog.csdn.net/raintungli/article/details/7023092)。
线程的实现在 attach_listener_thread_entry 方法体中实现: 95 static void attach_listener_thread_entryJavaThread thread TRAPS 96 os::set_prioritythread NearMaxPriority 97 98 if AttachListener::pd_init 0 99 return100 101 AttachListener::set_initialized102103 for 104 AttachOperation op AttachListener::dequeue105 if op NULL 106 return // dequeue failed or shutdown107 108109 ResourceMark rm110 bufferedStream st111 jint res JNI_OK112113 // handle special detachall operation114 if strcmpop-gtname AttachOperation::detachall_operation_name 0115 AttachListener::detachall116 else 117 // find the function to dispatch too118 AttachOperationFunctionInfo info NULL119 for int i0 funcsi.name NULL i 120 const char name funcsi.name121 assertstrlenname lt AttachOperation::name_length_max quotoperation ltname_length_maxquot122 if strcmpop-gtname name 0 123 info ampfuncsi124 break125 126 127128 // check for platform dependent attach operation129 if info NULL 130 info AttachListener::pd_find_operationop-gtname131 132133 if info NULL 134 // dispatch to the function that implements this operation135 res info-gtfuncop ampst136 else 137 st.printquotOperation s not recognizedquot op-gtname138 res JNI_ERR139 140 141 142 // operation complete - send result and output to client 143 op-gtcompleteres ampst 144 145 在 AttachListener::dequeue 在 liunx 里的实现就是监听刚才创建的 socket 的文件,如果有请求进来,找到请求对应的操作,调用操作得到结果并把结果写到这个 socket 的文件,如果你把 socket 的文件删除,jstack/jmap 会出现错误信息 unable to open socketfile:........ 我们经常使用 kill -3 pid 的操作打印出线程栈信息,我们可以看到具体的实现是在Signal Dispatcher 线程中完成的,因为 kill -3 pid 并不会创建.attach_pidpid 文件,所以一直初始化不成功,从而线程的栈信息被打印到控制台中。
Java 开源工具在 linux 上的
源码分析三:执行的线程在前面的博客中所提到的信号转发线程,Attach Listener 线程都只是操作 socket 文件,并没有去执行比如 stack 分析,或者 heap 的分析,真正的工作线程其实是 vm thread. AD: 在前面的博客中(http://blog.csdn.net/raintungli/article/details/7034005)所提到的信号转发线程,Attach Listener 线程都只是操作 socket 文件,并没有去执行比如 stack分析,或者 heap 的分析,真正的工作线程其实是 vm thread. (一)启动 vm thread 1 jint Threads::create_vmJavaVMInitArgs args bool canTryAgain 2 ... 3 // Create the VMThread 4 TraceTime timerquotStart VMThreadquot TraceStartupTime 5 VMThread::create 6 Thread vmthread VMThread::vm_thread 7 8 if os::create_threadvmthread os::vm_thread 9 vm_exit_during_initializationquotCannot create VM thread. Out of system resources.quot 10 11 // Wait for the VM thread to become ready and VMThread::run to initialize 12 // Monitors can have spurious returns must always check another state flag 13 14 MutexLocker mlNotify_lock 15 os::start_threadvmthread 16 while vmthread-gtactive_handles NULL 17 Notify_lock-gtwait 18 19 20 21 ... 22 23 24 我们可以看到,在 thread.cpp 里启动了线程 vm thread,在这里我们同时也稍微的略带的讲一下 jvm 在 linux 里如何启动线程的。
通常在 linux 中启动线程,是调用: 25 int pthread_createpthread_t __thread __const pthread_attr_t __attrvoid __start_routine void void __arg 而在
java 里却增加了 os:create_thread --初始化线程 和 os:start_thread--启动线程。
我们去看一下 jvm 里面是如何在 linux 里做到的。
在 os_linux.cpp 中来看 create_thread 的方法: 26 bool os::create_threadThread thread ThreadType thr_type size_t stack_size 27 .... 28 int ret pthread_createamptid ampattr void void
java_start thread 29 .... 30 继续看
java_start 方法: 31 static void
java_startThread thread 32 .... 33 // handshaking with parent thread 34 35 MutexLockerEx mlsync Mutex::_no_safepoint_check_flag 36 37 // notify parent thread 38 osthread-gtset_stateINITIALIZED 39 sync-gtnotify_all 40 41 // wait until os::start_thread 42 while osthread-gtget_state INITIALIZED 43 sync-gtwaitMutex::_no_safepoint_check_flag 44 45 46 47 // call one more level start routine 48 thread-gtrun 49 50 return 0 51 首先 jvm 先设置了当前线程的状态是 Initialized 然后 notify 所有的线程, 52 while osthread-gtget_state INITIALIZED 53 sync-gtwaitMutex::_no_safepoint_check_flag 54 不停的查看线程的当前状态是不是 Initialized 如果是的话,调用了 sync-gtwait的方法等待。
来看 os:start_thread 的方法 os.cpp 55 void os::start_threadThread thread 56 // guard suspend/resume 57 MutexLockerEx mlthread-gtSR_lock Mutex::_no_safepoint_check_flag 58 OSThread osthread thread-gtosthread 59 osthread-gtset_stateRUNNABLE 60 pd_start_threadthread 61 这时候设置了线程的状态为 runnable但没有 notify 线程。
在 pd_start_threadthread中 os_linux.cpp 中: 62 void os::pd_start_threadThread thread 63 OSThread osthread thread-gtosthread 64 assertosthread-gtget_state INITIALIZED quotjust checkingquot 65 Monitor sync_with_child osthread-gtstartThread_lock 66 MutexLockerEx mlsync_with_child Mutex::_no_safepoint_check_flag 67 sync_with_child-gtnotify 68 这时候我们看到了 notify 线程的操作,也就是这时候 notify 了线程,因为这时候的线程的状态是 RUNNABLE 方法
java_start 继续往下执行,于是调用了 thread-gtrun的方法。
对于线程 vm Thread 也就是调用了 vmthread::run 方法。
vmThread.cpp 69 void VMThread::run 70 ... 71 this-gtloop 72 ... 73 调用了 loop 函数,处理了 VM_Operation 的 queue 关于 queue 的级别和优先级处理算法:可以参考 另一篇博客:http://blog.csdn.net/raintungli/article/details/6553337 (二)Jstack 运行在 vm thread 里的 VM_Operation jstack 处理也就是在前面博客所提到的 attach Listener 线程所做的 operation 74 static jint thread_dumpAttachOperation op outputStream out 75 bool print_concurrent_locks false 76 if op-gtarg0 NULL ampamp strcmpop-gtarg0 quot-lquot 0 77 print_concurrent_locks true 78 79 80 // thread stacks 81 VM_PrintThreads op1out print_concurrent_locks 82 VMThread::executeampop1 83 84 // JNI global handles 85 VM_PrintJNI op2out 86 VMThread::executeampop2 87 88 // Deadlock detection 89 VM_FindDeadlocks op3out 90 VMThread::executeampop3 91 92 return JNI_OK 93 简单看一下类 VM_PrintThreads 它 继承了 VM_Operation 94 class VM_PrintThreads: public VM_Operation 95 private: 96 outputStream _out 97 bool _print_concurrent_locks 98 public: 99 VM_PrintThreads _out tty _print_concurrent_locks PrintConcurrentLocks 100 VM_PrintThreadsoutputStream out bool print_concurrent_locks _out out _print_concurrent_locks print_concurrent_locks 101 VMOp_Type type const return VMOp_PrintThreads 102 void doit 103 bool doit_prologue 104 void doit_epilogue 105 当调用 VMThread::execute也就是将 VM_PrintThreads 放入了_vm_queue 中,交给 vm thread 处理,对 vm thread 来说取出 queue 里的 VM_Operation并且调用 doit方法。
在 jstack 里,attach listener 的线程产生了 VM_PrintThreads,VM_PrintJNI,VM_FindDeadlocks 3个 operations,交给了 vm thread 的线程处理。
Java 开源工具在 linux 上的
源码分析四:safe pointsafe point 顾明思意,就是安全点,当需要 jvm 做一些操作的时候,需要把当前正在运行的线程进入一个安全点的状态(也可以说停止状态),这样才能做一些安全的操作,比如线程的 dump,堆栈的信息。
AD: safe point 顾明思意,就是安全点,当需要 jvm 做一些操作的时候,需要把当前正在运行的线程进入一个安全点的状态(也可以说停止状态),这样才能做一些安全的操作,比如线程的 dump,堆栈的信息。
(我们一直在谈论.
上一篇:
java简单计算器源代码
下一篇:
《南方周末》新闻特稿的采写特点及发展趋势