Instrumentation:版本对比 v3 → v4
v3(2026-10-04 02:03,朝武芳乃)→ v4(2026-10-04 03:02,mcst12345)
共 41 行变更 · 新增 23 行 · 删除 6 行(左右分栏:左为旧版本,右为新版本)
| 1 | ''本条基于 Java 17(Minecraft 1.20.1 所用版本)。'' | ||
| 2 | |||
| 3 | '''Instrumentation'''(<code>java.lang.instrument.Instrumentation</code>)是 [[JavaAgent]] 修改类的标准接口,底层由 [[JVMTI]] 实现,JDK 中的具体实现类是 [[InstrumentationImpl]]。 | ||
| 1 | '''Instrumentation'''(<code>java.lang.instrument.Instrumentation</code>)是jvmtiEnv在Java層的封裝,默認實現為 [[InstrumentationImpl]]。 | ||
| 4 | 2 | ||
| 5 | == 获取 == | 3 | == 获取 == |
| 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>),常用的绕过方式见下文“运行时外部进程附加”。 | ||
| 4 | * 启动时:<code>-javaagent:xxx.jar</code>,JVM將調用Premain-Class指定的agent類的premain(String,Instrumentation)方法。 | ||
| 5 | * 运行时附加:通过 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>),常用的绕过方式见下文“运行时外部进程附加”。Attach API底層調用Agent_OnAttach函數(位於instrument動態庫內),能夠調用native函數的環境下也可直接調用Agent_OnAttach實現附加agent獲取Instrumentation。 | ||
| 8 | * 重转换 / 重定义还需清单声明 <code>Can-Retransform-Classes</code> / <code>Can-Redefine-Classes</code>。 | 6 | * 重转换 / 重定义还需清单声明 <code>Can-Retransform-Classes</code> / <code>Can-Redefine-Classes</code>。 |
| 9 | 7 | ||
| ⋯ 省略 2 行未变内容 ⋯ | |||
| 12 | 10 | ||
| 13 | === 附加时机 === | 11 | === 附加时机 === |
| 14 | 可以在 [[Forge提供的Services]] 加载、[[Mixin]](如 mixin 配置插件加载)或 [[ModMain]](模组主类构造)期间发起附加。附加得越早,越有机会在目标类加载前注册好转换器。 | ||
| 12 | javaagent可在任意時間附加,但已加載的類不會接收到ClassFileLoadHook事件,除非手動觸發redefine/retransform。 | ||
| 15 | 13 | ||
| 16 | === 实现思路 === | 14 | === 实现思路 === |
| ⋯ 省略 44 行未变内容 ⋯ | |||
| 61 | * 重转换 / 重定义只能改方法体,不能增删字段、方法,也不能改继承关系。 | 59 | * 重转换 / 重定义只能改方法体,不能增删字段、方法,也不能改继承关系。 |
| 62 | * 字节码一般配合 [[objectweb asm]] 或 [[javassist]] 修改。 | 60 | * 字节码一般配合 [[objectweb asm]] 或 [[javassist]] 修改。 |
| 61 | |||
| 62 | '''Instrumentation'''(<code>java.lang.instrument.Instrumentation</code>)is the Java wrapper of jvmtiEnv,the default implementation is [[InstrumentationImpl]]. | ||
| 63 | |||
| 64 | == Obtain == | ||
| 65 | * through commandline agent/static: use -javaagent:/path/to/your/agent.jar to load a javaagent statically. JVM will call the premain(String,Instrumentation) method declared in the class specified by Premain-Class attribute in your agent jar. | ||
| 66 | * runtime attach/dynamic: There are various ways to attach an javaagent at runtime, however they all delegate to Agent_OnAttach function in instrument library. So that if you can invoke c functions the most convenient way to load a javaagent dynamically is to invoke Agent_OnAttach function. Or that if you are using Java9+ you can just invoke sun.instrument.InstrumentationImpl.loadAgent0(String). | ||
| 67 | |||
| 68 | == Commonly used methods == | ||
| 69 | * <code>addTransformer(ClassFileTransformer, boolean)</code> Add a ClassFileTransformer that can receive and transform a class when JVM attempts to load it. The boolean argument specifies whether the transformer will receive the event when it's retransformation. | ||
| 70 | * <code>removeTransformer(ClassFileTransformer)</code> Remove a ClassFileTransformer. You should perform this operation on the same Instrumentation that you registered this transformer. | ||
| 71 | * <code>retransformClasses(Class<?>... classes)</code> retransform all the classes in the classes array. JVM will reconstruct the classfile using runtime data if no cache is available and then feed them into all present ClassFileTransformer that is registered as retransformable, in the end redefine all the classes with their new classfile. Note that if any redefination fails, all the classes will remain untouched. | ||
| 72 | * <code>redefineClasses(ClassDefinition... definitions)</code> Redefine all the classes that is required by the definitions array. ClassDefinition contains a java.lang.Class field and a byte[] field, the Class field declares which class to be redefined, the byte[] field declares its new defination. | ||
| 73 | * <code>getAllLoadedClasses()</code> get all loaded classes in current JVM. | ||
| 74 | * <code>getInitiatedClasses(ClassLoader loader)</code> get all initiated classes loaded by specific classloader | ||
| 75 | * <code>getObjectSize(Object objectToSize)</code> Get the size of a Java object. Note that the "size" here is how much memory the object consumes, not essentially the actual size of the object. (It may contain padding) | ||
| 76 | * <code>appendToBootstrapClassLoaderSearch(JarFile jarfile)</code> Append jar file to bootstrap classloader. | ||
| 77 | * <code>appendToSystemClassLoaderSearch(JarFile jarfile)</code> Append jar file to system classloader. | ||
| 78 | * <code>setNativeMethodPrefix(ClassFileTransformer transformer, String prefix)</code> Set a [[native method prefix]] corresponding to a ClassFileTransformer | ||
| 79 | * <code>redefineModule(Module module,Set<Module> extraReads,Map<String, Set<Module>> extraExports,Map<String, Set<Module>> extraOpens,Set<Class<?>> extraUses,Map<Class<?>, List<Class<?>>> extraProvides)</code> *Java9+ Redefines a module. You can only expand its reads, uses, opens, provides. | ||