Java Instrumentation從概念到基本用法詳解
一、什么是 Java Instrumentation
Java Instrumentation 是 java.lang.instrument 包提供的 API,允許開發(fā)者在類被 JVM 加載時(shí)對(duì)其進(jìn)行修改,或者在運(yùn)行時(shí)重新定義類的字節(jié)碼。
主要用途
- 性能監(jiān)控與分析(如:JProfiler、YourKit)
- 代碼覆蓋率工具(如:JaCoCo、Cobertura)
- 動(dòng)態(tài)代理、AOP
- 安全檢查、沙箱實(shí)現(xiàn)
- 熱代碼替換(HotSwap)
二、核心概念
1. Java Agent
Instrumentation 的入口是 Java Agent,它是一種特殊的 jar 包,啟動(dòng) JVM 時(shí)通過 -javaagent 參數(shù)加載。
Agent Jar 需要在 MANIFEST.MF 里指定入口類:
Premain-Class: com.example.MyAgent
2. Premain & Agentmain
public static void premain(String agentArgs, Instrumentation inst)- JVM 啟動(dòng)時(shí)執(zhí)行(靜態(tài)加載)
public static void agentmain(String agentArgs, Instrumentation inst)- JVM 運(yùn)行中動(dòng)態(tài) attach(動(dòng)態(tài)加載)
3. Instrumentation 接口
這是核心接口,提供了如下能力:
- 添加 ClassFileTransformer
- 獲取已加載的類
- 重定義類(redefineClasses)
- 檢查是否支持 Retransform(isRetransformClassesSupported)
三、基本用法
1. 編寫 Agent
public class MyAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("Agent loaded!");
inst.addTransformer(new MyTransformer());
}
}2. 實(shí)現(xiàn) ClassFileTransformer
public class MyTransformer implements ClassFileTransformer {
@Override
public byte[] transform(
ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer
) {
// 可以用 ASM/Javassist 修改字節(jié)碼
if (className.equals("com/example/TargetClass")) {
// 返回修改后的字節(jié)碼
}
return null; // 返回 null 表示不修改
}
}3. 啟動(dòng) JVM 時(shí)加載
java -javaagent:myagent.jar -jar myapp.jar
四、進(jìn)階應(yīng)用
1. 動(dòng)態(tài) Attach
JDK 提供了 com.sun.tools.attach.VirtualMachine 實(shí)現(xiàn)動(dòng)態(tài) attach agent 到運(yùn)行中的 JVM。
VirtualMachine vm = VirtualMachine.attach("pid");
vm.loadAgent("myagent.jar");2. 字節(jié)碼修改
常用工具有 ASM、Javassist、ByteBuddy。例如用 ASM 修改方法字節(jié)碼,實(shí)現(xiàn)方法增強(qiáng)、插樁。
3. 熱代碼替換
通過 Instrumentation.redefineClasses 可以在不重啟 JVM 的情況下替換已加載類的實(shí)現(xiàn)(有一定限制)。
五、典型案例
1. 性能監(jiān)控
在所有方法入口和出口插入計(jì)時(shí)代碼,統(tǒng)計(jì)每個(gè)方法的耗時(shí)。
2. 代碼覆蓋率
在每個(gè)分支、方法插入標(biāo)記,記錄哪些代碼被執(zhí)行過。
3. 安全防護(hù)
在敏感 API 調(diào)用前插入權(quán)限檢查邏輯。
六、注意事項(xiàng)
- 并非所有類都能被重新定義(如 JVM 內(nèi)部類、已被 native 方法鎖定的類)。
- 字節(jié)碼修改需謹(jǐn)慎,容易引發(fā)兼容性和穩(wěn)定性問題。
- 動(dòng)態(tài) attach 需要目標(biāo) JVM 開啟 attach 功能(通常為同一用戶)。
七、實(shí)戰(zhàn)案例
編寫一個(gè)Java Instrumentation接口性能監(jiān)控示例
1. 創(chuàng)建 Java Agent
PerformanceMonitorAgent.java
import java.lang.instrument.Instrumentation;
public class PerformanceMonitorAgent {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("PerformanceMonitorAgent loaded.");
inst.addTransformer(new PerformanceTransformer());
}
}2. 編寫 Transformer
PerformanceTransformer.java
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
public class PerformanceTransformer implements ClassFileTransformer {
@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
// 只監(jiān)控特定包下的類(比如com/example/)
if (className != null && className.startsWith("com/example/")) {
// 使用ASM或Javassist修改字節(jié)碼,在方法前后插入耗時(shí)統(tǒng)計(jì)代碼
try {
return MethodTimer.injectTimer(classfileBuffer);
} catch (Exception e) {
e.printStackTrace();
}
}
return classfileBuffer;
}
}3. 使用 Javassist 修改字節(jié)碼
MethodTimer.java
import javassist.*;
public class MethodTimer {
public static byte[] injectTimer(byte[] classfileBuffer) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));
for (CtMethod method : ctClass.getDeclaredMethods()) {
if (!method.isEmpty() && !method.getName().equals("<init>")) {
method.addLocalVariable("_startTime", CtClass.longType);
method.insertBefore("_startTime = System.nanoTime();");
method.insertAfter(
"System.out.println(\"[Performance] Method " + method.getName() +
" executed in \" + (System.nanoTime() - _startTime) + \" ns.\");"
);
}
}
byte[] byteCode = ctClass.toBytecode();
ctClass.detach();
return byteCode;
}
}4. 目標(biāo)類示例
com/example/Demo.java
package com.example;
public class Demo {
public void test() {
try { Thread.sleep(100); } catch (Exception e) {}
}
public static void main(String[] args) {
new Demo().test();
}
}5. 編譯 & 打包 Agent
javac -cp javassist.jar:. PerformanceMonitorAgent.java PerformanceTransformer.java MethodTimer.java jar cmf MANIFEST.MF perfagent.jar PerformanceMonitorAgent.class PerformanceTransformer.class MethodTimer.class
MANIFEST.MF 內(nèi)容:
Premain-Class: PerformanceMonitorAgent Can-Redefine-Classes: true Can-Retransform-Classes: true
6. 運(yùn)行目標(biāo)程序并加載 Agent
java -javaagent:perfagent.jar -cp javassist.jar:. com.example.Demo
7. 輸出示例
PerformanceMonitorAgent loaded.
[Performance] Method test executed in 100123456 ns.
說明:
- 這個(gè)例子用 Javassist 修改字節(jié)碼,在每個(gè)方法前后插入耗時(shí)統(tǒng)計(jì)代碼。
- 你可以根據(jù)需要修改
PerformanceTransformer,選擇需要監(jiān)控的類和方法。 - 實(shí)際生產(chǎn)環(huán)境建議將統(tǒng)計(jì)結(jié)果匯總、寫入日志或上報(bào)到監(jiān)控系統(tǒng)。
到此這篇關(guān)于Java Instrumentation詳解的文章就介紹到這了,更多相關(guān)Java Instrumentation內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
最簡(jiǎn)單的spring boot打包docker鏡像的實(shí)現(xiàn)
這篇文章主要介紹了最簡(jiǎn)單的spring boot打包docker鏡像的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Java?基于Hutool實(shí)現(xiàn)DES加解密示例詳解
這篇文章主要介紹了Java基于Hutool實(shí)現(xiàn)DES加解密,本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08
spring cloud 之 客戶端負(fù)載均衡Ribbon深入理解
下面小編就為大家?guī)硪黄猻pring cloud 之 客戶端負(fù)載均衡Ribbon深入理解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06
java中重寫equals()方法的同時(shí)要重寫hashcode()方法(詳解)
下面小編就為大家?guī)硪黄猨ava中重寫equals()方法的同時(shí)要重寫hashcode()方法(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
SpringBoot4中api版本控制的實(shí)現(xiàn)示例
本文主要介紹了SpringBoot4中api版本控制,通過在請(qǐng)求注解中添加版本號(hào),可以實(shí)現(xiàn)API的版本控制,從而避免對(duì)現(xiàn)有客戶端造成兼容性問題2026-02-02
Spring AI內(nèi)置DeepSeek的詳細(xì)步驟
Spring AI 最新快照版已經(jīng)內(nèi)置 DeepSeek 了,所以以后項(xiàng)目中對(duì)接 DeepSeek 就方便多了,但因?yàn)榭煺瞻鏁?huì)有很多 Bug,所以今天咱們就來看穩(wěn)定版的 Spring AI 如何對(duì)接 DeepSeek 滿血版,感興趣的小伙伴跟著小編一起來看看吧2025-02-02

