Java应用程序运行时监控方法(一)——JVMTI的应用
2017-06-30 16:39
295 查看
The JVM Tool Interface (JVMTI) 是一个由JVM提供的用于开发针对Java程序开发与监控工具的编程接口,通过JVMTI接口(Native API)可以创建代理程序(Agent)以监视和控制 Java 应用程序,包括剖析、调试、监控、分析线程等。著名的JProfiler利用该项技术实现其对Java程序的运行态监控与性能分析。
值得注意的是JVMTI 并不一定在所有的 Java 虚拟机上都得到实现,目前Oracle(SUN)、IBM、OpenJDK以及一些开源的如 Apache Harmony DRLVM均对其进行了标准实现 。
由于JVMTI 是一套Native接口,因此使用 JVMTI 需要我们使用C/C++ 操纵JNI。
JVMTI程序通常通过Agent方式在JVM OnLoad phase(启动时)Start-Up,这个加载处于虚拟机初始化的早期,此时所有的 Java 类都未被初始化、所有的对象实例也都未被创建(也支持Live phase(运行时)的Start-Up)。在启动Java应用程序时,需加入以下JVM参数:
JVMTI是基于事件驱动的,JVM每执行到一定的逻辑就会主动调用一些事件的回调接口,这些接口可以供开发者扩展自己的逻辑,实际上,对于JVMTI程序的Load过程可以遵循一种模板式的流程框架来完成:
(1)获取JVMTI环境(JVMTI Environment)
(2)注册所需的功能(Capabilities)
(3)注册事件通知(Event Notification)
(4)指定事件回调函数(Callback Method)
可以通过http://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html 和https://www.ibm.com/developerworks/cn/java/j-lo-jpda2/ 进一步了解相关知识。
接下来,我们通过举例的方式,看看JVMTI能够为Java应用监测带来些什么?
(1)Foo类
(2)Main类
我们将项目打包为tester.jar包,运行后输出结果如下:
利用Instrumentation实现字节码增强是许多监控工具针对Java应用程序实现非“侵入式”监控技术的基础,JVMTI为其提供了Native接口,Java SE 5将其从本地代码中解放出来通过JavaAgent利用该本地接口实现了Java语言层级的接口。
我们这里先不讨论JavaAgent的上层实现方式,你可以直接利用JVMTI的Native接口完成class字节码加载时的字节码修改增强。在JVM加载class字节码时会产生一个JVMTI_EVENT_CLASS_FILE_LOAD_HOOK事件,你可以通过ClassFileLoadHook回调函数完成新字节码的定义工作。
需要特别注意的地方是,对字节码的修改需要开辟出一块新的内存空间,因此就像向操作系统申请内存空间使用如malloc一样,你需要使用(*jvmti)->Allocate在JVM内部申请出一块内存空间,参考如下代码:
字节码增强的意义是你可以在原有执行方法内部添加自己的代码逻辑如一些方法执行期的性能监控逻辑,并且无需修改原程序Class文件,以完全无侵入式的代价完成对Java程序的监测。
我们的例子非常简单,将org/xreztento/tester/Foo类字节码中的E字符全部替换成P字符。
首先,编译一个JVMTI程序的静态库,参考以下脚本:
之后带agent运行我们的测试程序,如:
运行后输出结果如下:
我们使用一个HashMap数据结构来对方法的执行过程进行保存,key为执行方法的线程标识+方法名,value记录Entry方法时的系统nanos。(本例中hashmap采用https://github.com/japeq/hashmap)
实现一个记录bar方法执行时的运行时间的逻辑,参考如下代码实现:
编译时,增加hashmap.c,运行后输出结果如下:
我们可以记录该线程的执行和CPU-Time,参考代码如下:
运行后输出结果如下:
JVMTI提供了对Moniter的支持,可以监视Lock,并且可以获取一个执行方法的本地对象,我们可以结合Method完成一个父子线程关系的监视,参考代码如下:
运行后输出结果如下:
我们需要在JVMTI_EVENT_VM_INIT事件被触发时,在回调函数中利用RunAgentThread方法创建一个Agent级别的thread(创建过程非常类似pthread),之后按固定时间间隔,获取相关线程的Stack Trace信息。
参考如下代码:
运行后输出结果如下:
值得注意的是JVMTI 并不一定在所有的 Java 虚拟机上都得到实现,目前Oracle(SUN)、IBM、OpenJDK以及一些开源的如 Apache Harmony DRLVM均对其进行了标准实现 。
由于JVMTI 是一套Native接口,因此使用 JVMTI 需要我们使用C/C++ 操纵JNI。
JVMTI程序通常通过Agent方式在JVM OnLoad phase(启动时)Start-Up,这个加载处于虚拟机初始化的早期,此时所有的 Java 类都未被初始化、所有的对象实例也都未被创建(也支持Live phase(运行时)的Start-Up)。在启动Java应用程序时,需加入以下JVM参数:
-agentlib:agent-lib-name=options -agentpath:path-to-agent=options
JVMTI是基于事件驱动的,JVM每执行到一定的逻辑就会主动调用一些事件的回调接口,这些接口可以供开发者扩展自己的逻辑,实际上,对于JVMTI程序的Load过程可以遵循一种模板式的流程框架来完成:
(1)获取JVMTI环境(JVMTI Environment)
(2)注册所需的功能(Capabilities)
(3)注册事件通知(Event Notification)
(4)指定事件回调函数(Callback Method)
可以通过http://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html 和https://www.ibm.com/developerworks/cn/java/j-lo-jpda2/ 进一步了解相关知识。
接下来,我们通过举例的方式,看看JVMTI能够为Java应用监测带来些什么?
测试程序
我们首先编写一个简单的测试程序,用于展示我们举例中JVMTI Agent程序的功能,程序清单参考如下:(1)Foo类
package org.xreztento.tester; public class Foo { public void bar() throws InterruptedException { Thread.sleep(500); System.out.println("Executing Foo.bar()"); } public void baz() { System.out.println("Executing Foo.baz()"); } }
(2)Main类
package org.xreztento.tester; public class Main { public static void main(String[] args) throws InterruptedException{ Thread[] threads = new Thread[5]; Foo foo = new Foo(); foo.bar(); foo.baz(); for(int i = 0; i < threads.length; i++){ threads[i] = new Thread(new Runnable(){ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); } for(Thread thread : threads){ thread.start(); thread.join(); } } }
我们将项目打包为tester.jar包,运行后输出结果如下:
Bytecode Instrumentation
使用 Instrumentation开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。利用Instrumentation实现字节码增强是许多监控工具针对Java应用程序实现非“侵入式”监控技术的基础,JVMTI为其提供了Native接口,Java SE 5将其从本地代码中解放出来通过JavaAgent利用该本地接口实现了Java语言层级的接口。
我们这里先不讨论JavaAgent的上层实现方式,你可以直接利用JVMTI的Native接口完成class字节码加载时的字节码修改增强。在JVM加载class字节码时会产生一个JVMTI_EVENT_CLASS_FILE_LOAD_HOOK事件,你可以通过ClassFileLoadHook回调函数完成新字节码的定义工作。
需要特别注意的地方是,对字节码的修改需要开辟出一块新的内存空间,因此就像向操作系统申请内存空间使用如malloc一样,你需要使用(*jvmti)->Allocate在JVM内部申请出一块内存空间,参考如下代码:
#include <stdio.h> #include <memory.h> #include <string.h> #include <jvmti.h> void JNICALL callbackClassFileLoadHook(jvmtiEnv *jvmti, JNIEnv *jni, jclass class_being_redefined, jobject loader, const char *name, jobject protection_domain, jint class_data_len, const unsigned char *class_data, jint *new_class_data_len, unsigned char **new_class_data) { jvmtiError error; if(strcmp(name, "org/xreztento/tester/Foo") == 0){ printf("loaded class name=%s\n ", name); jint size = class_data_len; *new_class_data_len = size; //为新的class字节码数据区分配JVM内存 error = (*jvmti)->Allocate(jvmti, size, new_class_data); memset(*new_class_data, 0, size); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI"); } int i; //遍历旧的字节码字符,将E字符修改为P for(i = 0; i < size; i++){ if(class_data[i] == 'E'){ (*new_class_data)[i] = 'P'; } else { (*new_class_data)[i] = class_data[i]; } } } } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ jvmtiEnv *jvmti = NULL; jvmtiError error; //获取JVMTI environment error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1); if (error != JNI_OK) { fprintf(stderr, "ERROR: Couldn't get JVMTI environment"); return JNI_ERR; } //注册功能 jvmtiCapabilities capabilities; (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities)); capabilities.can_generate_all_class_hook_events = 1 ; capabilities.can_retransform_classes = 1 ; capabilities.can_retransform_any_class = 1 ; error = (*jvmti)->AddCapabilities(jvmti, &capabilities); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI"); return error; } //设置JVM事件回调 jvmtiEventCallbacks callbacks; callbacks.ClassFileLoadHook = &callbackClassFileLoadHook; error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks)); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!"); return error; } //设置事件通知 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } return JNI_OK; } JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char *options, void *reserved){ //do nothing } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm){ //do nothing }
字节码增强的意义是你可以在原有执行方法内部添加自己的代码逻辑如一些方法执行期的性能监控逻辑,并且无需修改原程序Class文件,以完全无侵入式的代价完成对Java程序的监测。
我们的例子非常简单,将org/xreztento/tester/Foo类字节码中的E字符全部替换成P字符。
首先,编译一个JVMTI程序的静态库,参考以下脚本:
gcc agent.c -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-3.b17.el7.x86_64/include -I/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-3.b17.el7.x86_64/include/linux -shared -fPIC -o ./libtestagent.so
之后带agent运行我们的测试程序,如:
java -jar -agentpath:/root/jvmti/libtestagent.so tester.jar
运行后输出结果如下:
Method执行性能
JVMTI提供了对每个Java方法执行的监控事件,当进入方法时触发JVMTI_EVENT_METHOD_ENTRY事件,方法执行完成触发JVMTI_EVENT_METHOD_EXIT,我们可以为两个事件编写回调函数完成对指定方法的执行性能数据的记录。我们使用一个HashMap数据结构来对方法的执行过程进行保存,key为执行方法的线程标识+方法名,value记录Entry方法时的系统nanos。(本例中hashmap采用https://github.com/japeq/hashmap)
实现一个记录bar方法执行时的运行时间的逻辑,参考如下代码实现:
#include <stdio.h> #include <stdint.h> #include <sys/time.h> #include <memory.h> #include <string.h> #include <stdlib.h> #include <jvmti.h> #include "hashmap.h" #define KEY_MAX_LENGTH 256 #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #define container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - offsetof(type, member))) struct method_t { struct hash_node node; int hash; jlong start_time; }; /** hash算法 **/ static unsigned int djb_hash(char* str, unsigned int len) { unsigned int hash = 5381; unsigned int i = 0; for(i = 0; i < len; str++, i++) { hash = ((hash << 5) + hash) + (*str); } return hash; } static size_t hash_test(void *key) { unsigned int i = djb_hash(key, strlen(key)); return i; } static int cmp_test(struct hash_node *node, void *key) { struct method_t *t = container_of(node, struct method_t, node); int i = hash_test(key); return t->hash == i; } static struct hashmap map; void JNICALL callbackMethodEntry(jvmtiEnv *jvmti, JNIEnv* env, jthread thr, jmethodID method) { char *name; char *signature; char *generic; (*jvmti)->GetMethodName(jvmti, method, &name, &signature, &generic); if (strcmp(name, "bar") == 0){ jvmtiThreadInfo info; jlong nanos; struct method_t *t; char key[KEY_MAX_LENGTH] = "thread-"; (*jvmti)->GetThreadInfo(jvmti, thr, &info); strcat(key, info.name); strcat(key, ":"); strcat(key, name); strcat(key, "\0"); (*jvmti)->GetTime(jvmti, &nanos); t = calloc(1, sizeof(*t)); t->hash = hash_test(key); t->start_time = nanos; hashmap_insert(&map, &t->node, key); (*jvmti)->Deallocate(jvmti, (void *)info.name); } } void JNICALL callbackMethodExit(jvmtiEnv *jvmti, JNIEnv* env, jthread thr, jmethodID method){ char *name; char *signature; char *generic; (*jvmti)->GetMethodName(jvmti, method, &name, &signature, &generic); if (strcmp(name, "bar")== 0){ jvmtiThreadInfo info; jlong nanos; struct method_t *t; char key[KEY_MAX_LENGTH] = "thread-"; (*jvmti)->GetThreadInfo(jvmti, thr, &info); strcat(key, info.name); strcat(key, ":"); strcat(key, name); struct hash_node *node = hashmap_get(&map, key); if (node == NULL) { printf("%s not found\n", key); } else { (*jvmti)->GetTime(jvmti, &nanos); t = container_of(node, struct method_t, node); printf("method<%s> running: %ld ms\n", key, (nanos - t->start_time) / (1000 * 1000)); } (*jvmti)->Deallocate(jvmti, (void *)info.name); } } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ jvmtiEnv *jvmti = NULL; jvmtiError error; hashmap_init(&map, hash_test, cmp_test); //获取JVMTI environment error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1); if (error != JNI_OK) { fprintf(stderr, "ERROR: Couldn't get JVMTI environment"); return JNI_ERR; } //注册功能 jvmtiCapabilities capabilities; (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities)); capabilities.can_generate_method_entry_events = 1; capabilities.can_generate_method_exit_events = 1; capabilities.can_access_local_variables = 1; error = (*jvmti)->AddCapabilities(jvmti, &capabilities); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI"); return error; } //设置JVM事件回调 jvmtiEventCallbacks callbacks; callbacks.MethodEntry = &callbackMethodEntry; callbacks.MethodExit = &callbackMethodExit; error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks)); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!"); return error; } //设置事件通知 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } return JNI_OK; } JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char *options, void *reserved){ //do nothing } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm){ hashmap_free(&map); }
编译时,增加hashmap.c,运行后输出结果如下:
Thread监视
JVMTI提供了对JVM内部所有线程生命周期的监控事件,并可以控制这些线程的行为,比如当一个Java线程开始执行时触发JVMTI_EVENT_THREAD_START事件,结束时触发JVMTI_EVENT_THREAD_END,通过实现回调函数,可以获得触发该事件下的线程,并获取线程信息或操作该线程。我们可以记录该线程的执行和CPU-Time,参考代码如下:
#include <stdio.h> #include <memory.h> #include <string.h> #include <jvmti.h> void JNICALL callbackThreadStart(jvmtiEnv *jvmti, JNIEnv* env, jthread thr){ jvmtiThreadInfo info; jlong cpu_time; jvmtiFrameInfo frames[5]; jint count; jvmtiError err; //获取启动线程信息 (*jvmti)->GetThreadInfo(jvmti, thr, &info); //获取启动线程CPU-Time (*jvmti)->GetThreadCpuTime(jvmti, thr, &cpu_time); printf("thread-%s start...\n", info.name); printf("thread-%s cpu-time : %ld nanos\n", info.name, cpu_time); } void JNICALL callbackThreadEnd(jvmtiEnv *jvmti, JNIEnv* env, jthread thr){ jvmtiThreadInfo info; (*jvmti)->GetThreadInfo(jvmti, thr, &info); printf("thread-%s end...\n", info.name); } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ jvmtiEnv *jvmti = NULL; jvmtiError error; //获取JVMTI environment error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1); if (error != JNI_OK) { fprintf(stderr, "ERROR: Couldn't get JVMTI environment"); return JNI_ERR; } //注册功能 jvmtiCapabilities capabilities; (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities)); capabilities.can_get_current_thread_cpu_time = 1 ; capabilities.can_get_thread_cpu_time = 1 ; error = (*jvmti)->AddCapabilities(jvmti, &capabilities); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI"); return error; } //jvmtiEventThreadStart ThreadStart; //jvmtiEventThreadEnd ThreadEnd; //设置JVM事件回调 jvmtiEventCallbacks callbacks; callbacks.ThreadStart = &callbackThreadStart; callbacks.ThreadEnd = &callbackThreadEnd; error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks)); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!"); return error; } //设置事件通知 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_THREAD_END, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } return JNI_OK; } JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char *options, void *reserved){ //do nothing } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm){ //do nothing }
运行后输出结果如下:
JVMTI提供了对Moniter的支持,可以监视Lock,并且可以获取一个执行方法的本地对象,我们可以结合Method完成一个父子线程关系的监视,参考代码如下:
#include <stdio.h> #include <memory.h> #include <jvmti.h> void JNICALL callbackMethodEntry(jvmtiEnv *jvmti, JNIEnv *env, jthread thr, jmethodID method) { jrawMonitorID monitor; char *name; (*jvmti)->RawMonitorEnter(jvmti, monitor); (*jvmti)->GetMethodName(jvmti, method, &name, NULL, NULL); if (strcmp(name, "start") == 0 || strcmp(name, "interrupt") == 0 || strcmp(name, "join") == 0 || strcmp(name, "stop") == 0 || strcmp(name, "suspend") == 0 || strcmp(name, "resume") == 0){ jobject *thd_ptr; jint hash_code; jvmtiThreadInfo info; //获取子线程对象 (*jvmti)->GetLocalObject(jvmti, thr, 0, 0, thd_ptr); //获取父线程信息 (*jvmti)->GetThreadInfo(jvmti, thr, &info); //获取子线程对象hashcode (*jvmti)->GetObjectHashCode(jvmti, *thd_ptr, &hash_code); printf("<thread-%s %s thread@%d>\n", info.name, name, hash_code); (*jvmti)->Deallocate(jvmti, (void *)info.name); } (*jvmti)->RawMonitorExit(jvmti, monitor); } void JNICALL callbackMethodExit(jvmtiEnv *jvmti, JNIEnv* env, jthread thr, jmethodID method){ } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ jvmtiEnv *jvmti = NULL; jvmtiError error; //获取JVMTI environment error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1); if (error != JNI_OK) { fprintf(stderr, "ERROR: Couldn't get JVMTI environment"); return JNI_ERR; } //注册功能 jvmtiCapabilities capabilities; (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities)); capabilities.can_generate_method_entry_events = 1; capabilities.can_generate_method_exit_events = 1; capabilities.can_access_local_variables = 1; capabilities.can_get_monitor_info = 1; error = (*jvmti)->AddCapabilities(jvmti, &capabilities); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI"); return error; } //设置JVM事件回调 jvmtiEventCallbacks callbacks; callbacks.MethodEntry = &callbackMethodEntry; callbacks.MethodExit = &callbackMethodExit; error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks)); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!"); return error; } //设置事件通知 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } return JNI_OK; } JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char *options, void *reserved){ //do nothing } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm){ //do nothing }
运行后输出结果如下:
获取Stack Trace
JVMTI可以获取当前JVM下所有线程以及线程内执行方法的Stack Trace。我们需要在JVMTI_EVENT_VM_INIT事件被触发时,在回调函数中利用RunAgentThread方法创建一个Agent级别的thread(创建过程非常类似pthread),之后按固定时间间隔,获取相关线程的Stack Trace信息。
参考如下代码:
#include <stdio.h> #include <memory.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <jvmti.h> static jthread mt; static JNICALL monitor_runnable(jvmtiEnv* jvmti, JNIEnv* env, jthread thr, int *args){ jthread* threads; jint thread_count; while (1) { printf("----------------------Stack Trace-------------------------\n"); int i; //获取当前JVM所有线程 (*jvmti)->GetAllThreads(jvmti, &thread_count, &threads); for(i = 0; i < thread_count; i++){ jvmtiFrameInfo frames[5]; jint count; jvmtiError err; err = (*jvmti)->GetStackTrace(jvmti, threads[i], 0, 5, frames, &count); if (err == JVMTI_ERROR_NONE && count >= 1) { char *methodName; err = (*jvmti)->GetMethodName(jvmti, frames[0].method, &methodName, NULL, NULL); if (err == JVMTI_ERROR_NONE) { printf("Thread Stack Trace Executing method: %s\n", methodName); } } } sleep(2); } } void JNICALL callbackVMInit(jvmtiEnv *jvmti, JNIEnv* env, jthread thr){ (*jvmti)->RunAgentThread(jvmti, thr, (void *)monitor_runnable, (void *)NULL, 1); } void JNICALL callbackVMDeath(jvmtiEnv *jvmti, JNIEnv* env){ (*jvmti)->StopThread(jvmti, mt, NULL); } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char *options, void *reserved){ jvmtiEnv *jvmti = NULL; jvmtiError error; //获取JVMTI environment error = (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_1); if (error != JNI_OK) { fprintf(stderr, "ERROR: Couldn't get JVMTI environment"); return JNI_ERR; } //注册功能 jvmtiCapabilities capabilities; (void)memset(&capabilities, 0, sizeof(jvmtiCapabilities)); capabilities.can_get_current_thread_cpu_time = 1 ; capabilities.can_get_thread_cpu_time = 1 ; capabilities.can_signal_thread = 1; capabilities.can_pop_frame = 1; error = (*jvmti)->AddCapabilities(jvmti, &capabilities); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to AddCapabilities JVMTI"); return error; } //jvmtiEventThreadStart ThreadStart; //jvmtiEventThreadEnd ThreadEnd; //设置JVM事件回调 jvmtiEventCallbacks callbacks; callbacks.VMInit = &callbackVMInit; callbacks.VMDeath = &callbackVMDeath; error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks)); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, "ERROR: Unable to SetEventCallbacks JVMTI!"); return error; } //设置事件通知 error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, (jthread)NULL); if(error != JVMTI_ERROR_NONE) { fprintf(stderr, " ERROR: Unable to SetEventNotificationMode JVMTI!"); return error; } return JNI_OK; } JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char *options, void *reserved){ //do nothing } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm){ //do nothing }
运行后输出结果如下:
相关文章推荐
- 将java应用程序打包成独立运行的.exe方法
- 【转载】一种不安装JRE环境即可运行Java应用程序的方法
- 在Docker中监控Java应用程序的5个方法
- 用Annotation,Future,动态代理实现自己的JAVA方法运行超时应用
- Java 运行时监控,第 3 部分: 监控应用程序生态系统的性能与可用性
- 【java基础】运行jar应用程序引用其他jar包的四种方法
- Java 运行时监控,第 3 部分: 监控应用程序生态系统的性能与可用性
- 将java应用程序打包成独立运行的.exe方法
- 运行java applet时提示:您的安全设置已阻止本地应用程序运行 解决方法
- 运行Java应用必须通过main()方法吗?
- Java 运行时监控第 3 部分: 监控应用程序生态系统的性能与可用性
- 一种不安装JRE环境即可运行Java应用程序的方法
- 网页上运行java小程序时出现“应用程序已被安全设置阻止”的解决方法
- java如何监控一个方法的运行时间 [问题点数
- 一种不安装JRE环境即可运行Java应用程序的方法
- 运行jar应用程序引用其他jar包的四种方法(java命令)
- 在CentOS系统中检测Java安装及运行jar应用的方法
- 将java应用程序打包成独立运行的.exe方法
- 使用Kieker(基于AspectJ)监控Java桌面应用的基本方法
- 运行Java应用必须通过main()方法吗?