在运行时卸载JVMTI代理?

10

我正在使用attach API在运行时加载JVMTI代理。 我希望在程序完成时卸载JVMTI代理,而不终止加载代理的JVM。 根据这份文档,从attach API中没有方法可以做到这一点。是否有其他方法可以通过Java API或JVMTI代理内部来强制代理自行卸载?


这并不是一个正式的答案,只是一个建议。我曾经在JNI中遇到了类似的问题(我想卸载一个模块)。我找到的最好的解决方案就是:生成一个新的JVM实例,让它处理该模块,并等待它终止,当它终止时,该模块显然已被卸载。我敢打赌,在挣扎了一段时间后,你也会这样做的。我建议你跳过“挣扎”阶段 :P - gd1
2个回答

6

JVMTI 规范 表示,卸载(不涉及JVM终止)是可能的,但具体实现取决于平台,超出了规范的范围。


0

你需要通过程序来加载JVMTI代理:

// attach to target VM
VirtualMachine vm = VirtualMachine.attach("2177");

// get system properties in target VM
Properties props = vm.getSystemProperties();

// construct path to management agent
String home = props.getProperty("java.home");
String agent = home + File.separator + "lib" + File.separator 
    + "your-agent-example.jar";

// load agent into target VM
vm.loadAgent(agent, "com.sun.management.jmxremote.port=5000");

// detach
vm.detach();

查看此处的文档

之后,您必须使用与默认值不同的classLoad:

您必须将系统属性“java.system.class.loader”设置为您的目标JVM的自定义类加载器的名称。

查看此处的文档

“Java的内置类加载器在加载类之前始终检查是否已加载该类。因此,使用Java的内置类加载器无法重新加载类。要重新加载类,您必须实现自己的ClassLoader子类。”

在您的情况下,您必须实现一个ClassLoader,其中ClassLoader.getSystemClassLoader()是父级。

即使使用自定义的ClassLoader子类,您仍然会面临挑战。每个加载的类都需要链接。这是使用ClassLoader.resolve()方法完成的。该方法是final的,因此无法在您的ClassLoader子类中重写。resolve()方法不允许任何给定的ClassLoader实例链接同一个类两次。因此,每次想要重新加载类时,必须使用ClassLoader子类的新实例。这并非不可能,但在设计类重新加载时需要知道这一点。
请参见动态类重新加载

这对于原生代码的JVMTI代理程序没有帮助。从JVM中分离不会导致JVMTI代理程序被卸载。 - Jared
我并不是在说那个。这段代码仅仅是为了展示如何加载代理。从JVM中卸载代理与卸载类一样,唯一的解决方案就是使用自己的类加载器。 - EricParis16
如果您查看附加文档,它清楚地说明loadAgent用于加载用Java编写的代理,而loadAgentLibrary和loadAgentPath用于加载JVMTI代理,这些代理是本机代码。由于JVMTI代理是本机代码而不是Java类,因此您对类加载器的讨论不适用。请参见http://download.oracle.com/javase/6/docs/jdk/api/attach/spec/index.html。 - Jared
我现在同意,我的解决方案只适用于Java代理。 - EricParis16

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接