Mmc秒杀圈百科 

Instrumentation:版本对比 v1 → v2

v1(2026-10-04 01:17,朝武芳乃)→ v2(2026-10-04 01:28,朝武芳乃)

共 53 行变更 · 新增 44 行 · 删除 1 行(左右分栏:左为旧版本,右为新版本)

⋯ 省略 4 行未变内容 ⋯
5== 获取 ==5== 获取 ==
6* 启动时:<code>-javaagent:xxx.jar</code>,jar 清单声明 <code>Premain-Class</code>,JVM 调用 <code>premain(String, Instrumentation)</code>。6* 启动时:<code>-javaagent:xxx.jar</code>,jar 清单声明 <code>Premain-Class</code>,JVM 调用 <code>premain(String, Instrumentation)</code>。
7* 运行时附加:通过 Attach API(<code>VirtualMachine.attach(pid).loadAgent(jar)</code>),清单声明 <code>Agent-Class</code>,JVM 调用 <code>agentmain</code>。Java 9 起默认不允许附加到自身进程(需 <code>-Djdk.attach.allowAttachSelf=true</code>)。
7* 运行时附加:通过 Attach API(<code>VirtualMachine.attach(pid).loadAgent(jar)</code>),清单声明 <code>Agent-Class</code>,JVM 调用 <code>agentmain</code>。Java 9 起默认不允许附加到自身进程(需 <code>-Djdk.attach.allowAttachSelf=true</code>),常用的绕过方式见下文“运行时外部进程附加”。
8* 重转换 / 重定义还需清单声明 <code>Can-Retransform-Classes</code> / <code>Can-Redefine-Classes</code>。8* 重转换 / 重定义还需清单声明 <code>Can-Retransform-Classes</code> / <code>Can-Redefine-Classes</code>。
99
10== 运行时外部进程附加 ==
11目前最常用的做法。Java 9 起 JVM 默认不允许附加到自身进程,所以由模组另起一个 Java 子进程,让子进程附加到游戏进程并加载 agent。整个过程'''不需要任何启动参数'''(既不用 <code>-javaagent</code>,也不用 <code>-Djdk.attach.allowAttachSelf=true</code>)。
12
13=== 附加时机 ===
14可以在 [[Forge提供的Services]] 加载、[[Mixin]](如 mixin 配置插件加载)或 [[ModMain]](模组主类构造)期间发起附加。附加得越早,越有机会在目标类加载前注册好转换器。
15
16=== 实现思路 ===
17# 取得游戏进程 PID:<code>ProcessHandle.current().pid()</code>。
18# 准备 agent jar:通常就是模组自身的 jar,清单中写 <code>Agent-Class</code> 和 <code>Can-Retransform-Classes: true</code>。
19# 用当前 JVM 的 java 可执行文件(<code>java.home</code> 下的 <code>bin/java</code>)启动子进程运行下面的辅助类,参数为 PID 和 agent jar 路径;父进程等待子进程结束。
20# 子进程通过反射依次调用 <code>VirtualMachine.attach</code> → <code>loadAgent</code> → <code>detach</code>。用反射是为了编译时不依赖 <code>jdk.attach</code> 模块。
21# 游戏进程里,JVM 调用 agent 的 <code>agentmain(String, Instrumentation)</code>,agent 由此拿到 Instrumentation 实例。
22
23辅助类示例:
24<code>import java.lang.reflect.Method;</code>
25<code>public final class ExternalAttachHelper {</code>
26<code>    private ExternalAttachHelper() {</code>
27<code>    }</code>
28<code>    public static void main(String[] args) throws Exception {</code>
29<code>        if (args.length < 2) {</code>
30<code>            throw new IllegalArgumentException("Usage: ExternalAttachHelper <pid> <agentJar>");</code>
31<code>        }</code>
32<code>        String pid = args[0];</code>
33<code>        String agentJar = args[1];</code>
34<code>        Class<?> vmClass = Class.forName("com.sun.tools.attach.VirtualMachine");</code>
35<code>        Method attach = vmClass.getMethod("attach", String.class);</code>
36<code>        Object vm = attach.invoke(null, pid);</code>
37<code>        try {</code>
38<code>            Method loadAgent = vmClass.getMethod("loadAgent", String.class, String.class);</code>
39<code>            loadAgent.invoke(vm, agentJar, "external-attach");</code>
40<code>        } finally {</code>
41<code>            Method detach = vmClass.getMethod("detach");</code>
42<code>            detach.invoke(vm);</code>
43<code>        }</code>
44<code>    }</code>
45<code>}</code>
46
47=== 注意事项 ===
48* 运行子进程的 Java 需要包含 <code>jdk.attach</code> 模块(完整 JDK 有,部分精简 JRE 没有)。
49* <code>agentmain</code> 所在类由系统类加载器加载,和模组类不在同一个类加载器里,通常要借助反射或系统类加载器可见的类,把 Instrumentation 交回模组。
50* 目标 JVM 带 <code>-XX:+DisableAttachMechanism</code> 时无法附加。
51* Java 21 起动态加载 agent 会打印警告(JEP 451),将来可能需要 <code>-XX:+EnableDynamicAgentLoading</code> 才能使用;1.20.1 所用的 Java 17 不受影响。
52
10== 常用方法 ==53== 常用方法 ==
11* <code>addTransformer(ClassFileTransformer, boolean)</code>:注册转换器,类加载或重转换时收到字节码54* <code>addTransformer(ClassFileTransformer, boolean)</code>:注册转换器,类加载或重转换时收到字节码
⋯ 省略 8 行未变内容 ⋯

其他版本