bigywater.tech
Java · 202

JVMTI:本地诊断接口能观察什么

JVMTI 是 JVM 提供给本地(C/C++)agent 的诊断接口,调试器与各种 profiler 都建立在它上面。这篇自己写一个最小的 JVMTI agent,看它到底能观察到什么(Java 层的 Instrumentation 在前面一篇,Attach 与 HotSwap 在后面)。

1234 个
JVMTI 堆遍历数出的 app.Foo 实例数(与应用创建的 1234 个一致)
68 项
这个 JVM 在 OnLoad 阶段可申请的 JVMTI 能力数
GC 9 / 9
agent 收到的 GC 开始 / 结束事件数

被观察的应用

实测 · 本机执行输出
  [app] 结束,持有 1234 个 Foo

JVMTI agent

实测 · 本机执行输出
  [jvmti] Agent_OnLoad:AddCapabilities(can_tag_objects, can_generate_garbage_collection_events) → 错误码 0(0 = 成功)
  [jvmti] 这个 JVM 在 OnLoad 阶段可申请的能力一共 68 项
  [app] 结束,持有 1234 个 Foo
  [jvmti] VMDeath:GetLoadedClasses 返回 661 个类
  [jvmti] 遍历堆:app.Foo 的实例有 1234 个(包括还没被回收的)
  [jvmti] 存活的 Java 线程 6 个:Reference Handler(RUNNABLE,daemon) Finalizer(WAITING,daemon) Signal Dispatcher(RUNNABLE,daemon) Notification Thread(RUNNABLE,daemon) Common-Cleaner(WAITING,daemon) DestroyJavaVM(RUNNABLE)
  [jvmti] 事件计数:ClassPrepare 172,ThreadStart 5,GC 开始 9、GC 结束 9
#include <jvmti.h>
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
  (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_2);
  jvmtiCapabilities caps = {0}; caps.can_tag_objects = 1; caps.can_generate_garbage_collection_events = 1;
  (*jvmti)->AddCapabilities(jvmti, &caps);              // 申请能力
  cb.GarbageCollectionStart = cbGCStart; cb.ClassPrepare = cbClassPrepare; cb.VMDeath = cbVMDeath;
  (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof cb);   // 注册回调
  (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);  // 打开事件
  return JNI_OK; }

分步演示

JVMTI
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,clang(Xcode 命令行工具)编译的 C agent,-agentpath 加载
    • 没有验证:MethodEntry 等高开销事件、断点与单步(调试器用的能力)、Agent_OnAttach(运行中加载)、字节码重定义(见后面 HotSwap 一篇)、各回收器下堆遍历的耗时与停顿;C 代码很小,只演示了申请能力、事件与查询三类用法
    京ICP备2026067502号-1京公网安备11010502063487号