JVMTI 经验笔记
一、简介
JVMTI 是 Java 虚拟机工具接口 (Java Virtual Machine Tool Interface) 的缩写,它提供了一系列的 API,允许工具(如性能分析器、调试器和配置工具)与 JVM 交互。通过 JVMTI,你可以访问正在运行的 Java 应用程序的状态信息,包括类加载、线程状态、内存分配、垃圾收集等事件。
二、JVMTI Agent 的创建
要创建一个 JVMTI Agent,你需要完成以下步骤:
-
编写代理代码:
- 使用 C 或 C++ 编写代理代码,其中定义了必要的回调函数。
- 通常需要包含
<jvmti.h>
头文件来使用 JVMTI 的 API。
-
初始化代理:
- 在代理代码中实现
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
函数。 - 在此函数中注册你的回调函数。
- 在代理代码中实现
-
编译代理:
- 将代理代码编译成动态链接库。
gcc -shared -o myagent.so myagent.c -I/path/to/jdk/include -I/path/to/jdk/include/linux -ljvmti
- 将代理代码编译成动态链接库。
-
启动 Java 应用:
- 使用
-agentpath:/path/to/your/agent.so
或-javaagent:/path/to/your/agent.jar
启动 Java 应用程序。java -agentpath:/path/to/myagent.so -jar yourapplication.jar
- 使用
三、示例:创建一个 JVMTI Agent 来监视对象分配
假设我们要创建一个 JVMTI Agent 来监视 Java 应用程序中对象的分配。我们将重载 jvmtiEventCallbacks.VMObjectAlloc
回调函数,并在对象分配时打印相关信息。
-
编写代理代码:
#include <jvmti.h> #include <stdio.h> static jvmtiEnv *jvmti = NULL; static void JNICALL ObjectAlloc(jvmtiEnv *env, jobject object, jclass class_name) { // 打印分配的对象信息 jvmtiError err; char *name = NULL; err = (*env)->GetClassName(env, class_name, &name, NULL); if (err == JVMTI_ERROR_NONE) { printf("Object allocated of class '%s'\n", name); (*env)->Deallocate(env, (unsigned char *)name); } } static jboolean JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { jvmtiError err; jvmtiCapabilities caps; err = (*jvm)->GetEnv(jvm, (void **)&jvmti, JVMTI_VERSION_1_0); if (err != JVMTI_ERROR_NONE) return JNI_FALSE; memset(&caps, 0, sizeof(caps)); caps.can_generate_object_alloc_events = 1; err = (*jvmti)->AddCapabilities(jvmti, &caps); if (err != JVMTI_ERROR_NONE) return JNI_FALSE; jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.VMObjectAlloc = &ObjectAlloc; err = (*jvmti)->SetEventCallbacks(jvmti, &callbacks); if (err != JVMTI_ERROR_NONE) return JNI_FALSE; err = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, NULL); if (err != JVMTI_ERROR_NONE) return JNI_FALSE; return JNI_TRUE; } // 编译时链接 jvmti 库 // gcc -shared -o myagent.so myagent.c -I/path/to/jdk/include -I/path/to/jdk/include/linux -ljvmti
-
编译代理:
gcc -shared -o myagent.so myagent.c -I/path/to/jdk/include -I/path/to/jdk/include/linux -ljvmti
-
启动 Java 应用:
java -agentpath:/path/to/myagent.so -jar yourapplication.jar
四、使用 GDB 调试 JVMTI Agent
一旦你的 JVMTI Agent 被加载并开始运行,你可以使用 GDB 来调试你的代理代码。例如,如果你想要在 ObjectAlloc
回调函数中设置断点,你可以这样做:
-
启动 GDB:
gdb -p <pid-of-your-java-application>
-
设置断点:
(gdb) break ObjectAlloc
-
继续执行:
(gdb) c
-
检查回溯:
当断点被触发时,你可以使用bt
命令来获取回溯信息:(gdb) bt
五、总结
JVMTI 提供了一种灵活的方式来监控和调试 Java 应用程序。通过使用 JVMTI Agent,你可以获得详细的运行时信息,并使用 GDB 进行更深入的调试。这种方法对于理解 Java 应用程序的行为以及解决复杂问题非常有用。