如何通过C++在运行时加载Java Agent(无需重启JVM)
运行时动态加载Java Agent(无需重启JVM)
核心方案:HotSpot Attach API
你提到的Sun工具本质是HotSpot虚拟机提供的Attach API,它支持在JVM运行时连接并加载Agent,无需重启。以下是具体实现方式:
一、Java 层面实现(最简单可靠)
直接使用JDK自带的com.sun.tools.attach包,无需JNI/C++操作:
获取目标JVM的PID
用jps命令列出当前运行的Java进程,找到目标进程的PID。编写加载Agent的代码
import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.AgentLoadException; import com.sun.tools.attach.AgentInitializationException; import com.sun.tools.attach.VirtualMachine; import java.io.IOException; public class RuntimeAgentLoader { public static void main(String[] args) { try { String targetPid = "12345"; // 替换为目标JVM的PID VirtualMachine vm = VirtualMachine.attach(targetPid); // 加载Agent Jar,需确保Jar的MANIFEST.MF配置正确 vm.loadAgent("/absolute/path/to/your/agent.jar"); vm.detach(); System.out.println("Agent加载成功"); } catch (AttachNotSupportedException e) { System.err.println("目标JVM不支持Attach操作"); } catch (AgentLoadException | AgentInitializationException e) { System.err.println("Agent加载/初始化失败: " + e.getMessage()); } catch (IOException e) { System.err.println("IO错误: " + e.getMessage()); } } }编译运行注意事项
- JDK8及以前:需要将JDK目录下的
tools.jar添加到类路径(javac -cp $JAVA_HOME/lib/tools.jar RuntimeAgentLoader.java) - JDK9+:
com.sun.tools.attach属于jdk.attach模块,编译时需添加模块依赖(javac --add-modules jdk.attach RuntimeAgentLoader.java)
- JDK8及以前:需要将JDK目录下的
二、JNI/C++ 层面实现(解决你之前的函数找不到问题)
如果必须用C++实现,核心是调用Attach API的本地方法,步骤如下:
加载Attach库
根据系统加载对应的库:- Linux:
libattach.so(位于JDK的jre/lib/amd64或lib目录) - Windows:
attach.dll(位于JDK的jre/bin目录)
- Linux:
获取关键函数并连接目标JVM
#include <jni.h> #include <dlfcn.h> #include <stdio.h> int main() { // 加载libattach.so void* attachLib = dlopen("/path/to/jdk/jre/lib/amd64/libattach.so", RTLD_LAZY); if (!attachLib) { fprintf(stderr, "加载libattach.so失败: %s\n", dlerror()); return 1; } // 获取AttachCurrentThread函数(用于连接到本地JVM,若要连接远程JVM需用其他方法) typedef jint (*AttachCurrentThread)(JavaVM** pvm, void** penv, void* args); AttachCurrentThread attachFunc = (AttachCurrentThread)dlsym(attachLib, "AttachCurrentThread"); if (!attachFunc) { fprintf(stderr, "找不到AttachCurrentThread函数: %s\n", dlerror()); dlclose(attachLib); return 1; } JavaVM* jvm; JNIEnv* env; jint result = attachFunc(&jvm, (void**)&env, NULL); if (result != JNI_OK) { fprintf(stderr, "连接JVM失败,错误码: %d\n", result); dlclose(attachLib); return 1; } // 反射调用VirtualMachine.loadAgent方法(替代直接JNI操作,更简单) jclass vmClass = env->FindClass("com/sun/tools/attach/VirtualMachine"); jmethodID attachMethod = env->GetStaticMethodID(vmClass, "attach", "(Ljava/lang/String;)Lcom/sun/tools/attach/VirtualMachine;"); jstring pidStr = env->NewStringUTF("12345"); // 目标JVM的PID jobject vmObj = env->CallStaticObjectMethod(vmClass, attachMethod, pidStr); jmethodID loadAgentMethod = env->GetMethodID(vmClass, "loadAgent", "(Ljava/lang/String;)V"); jstring agentPath = env->NewStringUTF("/path/to/your/agent.jar"); env->CallVoidMethod(vmObj, loadAgentMethod, agentPath); // 释放资源 env->DeleteLocalRef(pidStr); env->DeleteLocalRef(agentPath); env->DeleteLocalRef(vmObj); jvm->DetachCurrentThread(); dlclose(attachLib); printf("Agent加载完成\n"); return 0; }编译注意事项
- 链接时需指定JNI库和Attach库:
g++ -o agent_loader agent_loader.cpp -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -L$JAVA_HOME/jre/lib/amd64 -ljvm -ldl
- 链接时需指定JNI库和Attach库:
三、Agent Jar 配置要求
你的Agent Jar必须在MANIFEST.MF中指定必要属性,示例:
Manifest-Version: 1.0 Agent-Class: com.example.YourAgentClass Can-Redefine-Classes: true Can-Retransform-Classes: true
Agent-Class:指定Agent的主类,需包含agentmain方法(运行时加载专用,premain是启动时加载用)Can-Redefine-Classes:允许Agent重定义已加载的类(按需配置)
内容的提问来源于stack exchange,提问作者NightmareXD
相关产品推荐
相关产品推荐

