一、什麼是Javaagent
Javaagent是一個在Java應用程序啟動時運行的程序,可以在不修改原始代碼的情況下,對目標應用程序進行修改和增強。
Javaagent通過將提供的位元組碼轉換器織入到目標應用程序的類載入器中來實現這種修改。
Javaagent可以用於很多方面,例如:性能監控、代碼跟蹤、JVM安全等。
二、Javaagent的使用方法
使用Javaagent需要在啟動應用程序時,將Javaagent指定為啟動參數。具體方法如下:
java -javaagent:/path/to/agent.jar -jar yourapp.jar
其中,/path/to/agent.jar為Javaagent的路徑。在運行目標應用程序時,Java虛擬機會自動下載並載入Javaagent。
三、Javaagent的應用場景
1. 性能監控
Javaagent可以在目標應用程序中注入跟蹤代碼,用於監控應用程序的性能指標,例如:
1.1 監控方法調用次數
通過在方法調用前後記錄時間戳,並統計方法調用次數,即可得到該方法的平均調用時間和總調用次數。
public class MethodCountAgent { private static final ConcurrentHashMap<String, AtomicLong> methodCountMap = new ConcurrentHashMap<String, AtomicLong>(); public static void premain(String agentArgs) { new AgentBuilder.Default().type(ElementMatchers.any()) .transform((builder, typeDescription, classLoader, module) -> builder .method(ElementMatchers.any()) .intercept(Advice.to(CountMethodAdvise.class))) .installOnBytecodeArtifact(); } public static class CountMethodAdvise { @Advice.OnMethodEnter public static void enter(@Advice.Origin Method method) { AtomicLong count = methodCountMap.get(method.getName()); if (count == null) { count = new AtomicLong(0L); methodCountMap.put(method.getName(), count); } count.incrementAndGet(); } @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit(@Advice.Origin Method method) { // do nothing } } }
1.2 監控方法執行時間
可以在方法調用前後記錄時間戳,然後計算時間差值,即可得到方法的執行時間。
public class MethodTimeAgent { private static final ConcurrentHashMap<String, LongAdder> methodTimeMap = new ConcurrentHashMap<String, LongAdder>(); public static void premain(String agentArgs) { new AgentBuilder.Default().type(ElementMatchers.any()) .transform((builder, typeDescription, classLoader, module) -> builder .method(ElementMatchers.any()) .intercept(Advice.to(TimeMethodAdvise.class))) .installOnBytecodeArtifact(); } public static class TimeMethodAdvise { private static final ThreadLocal<Long> THREAD_LOCAL = new ThreadLocal<>(); @Advice.OnMethodEnter public static void enter() { THREAD_LOCAL.set(System.currentTimeMillis()); } @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit(@Advice.Origin Method method) { long startTime = THREAD_LOCAL.get(); long endTime = System.currentTimeMillis(); String methodName = method.getName(); LongAdder timeAdder = methodTimeMap.get(methodName); if (timeAdder == null) { timeAdder = new LongAdder(); methodTimeMap.put(methodName, timeAdder); } timeAdder.add(endTime - startTime); } } }
1.3 監控方法調用鏈
可以在方法調用前後記錄方法名和時間戳,並將這些信息保存在線程上下文中或者日誌文件中,即可得到方法調用鏈信息。
public class MethodTraceAgent { public static void premain(String agentArgs) { new AgentBuilder.Default().type(ElementMatchers.any()) .transform((builder, typeDescription, classLoader, module) -> builder .method(ElementMatchers.any()) .intercept(Advice.to(TraceMethodAdvise.class))) .installOnBytecodeArtifact(); } public static class TraceMethodAdvise { private static final ThreadLocal<LinkedList<String>> THREAD_LOCAL = ThreadLocal.withInitial(LinkedList::new); @Advice.OnMethodEnter public static void enter(@Advice.Origin Method method, @Advice.Local("methodStack") LinkedList<String> methodStack) { methodStack.addFirst(method.getName() + "<" + System.currentTimeMillis() + ">"); } @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit(@Advice.Origin Method method, @Advice.Local("methodStack") LinkedList<String> methodStack) { String poppedMethod = methodStack.removeFirst(); String methodName = method.getName(); int start = poppedMethod.length() - methodName.length() - 1; String currentMethod = poppedMethod.substring(0, start); System.out.println("Call " + currentMethod + " -> " + methodName); } @SuppressWarnings("unused") @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit(@Advice.Origin Method method) { // do nothing } @SuppressWarnings("unused") @Advice.OnMethodEnter public static void enter(@Advice.Origin Method method) { // do nothing } } }
2. JMX監控
Javaagent可以注入MBean服務,用於Java管理擴展(JMX)監控,以監控應用程序的性能指標。
public class MetricAgent { public static void premain(String agentArgs) { MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); ObjectName objectName = new ObjectName("com.example:type=Metric"); mBeanServer.registerMBean(new Metric(), objectName); new AgentBuilder.Default().type(ElementMatchers.any()) .transform((builder, typeDescription, classLoader, module) -> builder .method(ElementMatchers.any()) .intercept(Advice.to(MetricAdvise.class))) .installOnBytecodeArtifact(); } public static class Metric { // ... } public static class MetricAdvise { // ... } }
3. 特定應用場景
Javaagent可用於特定應用場景,例如:
3.1 改變類的行為
Javaagent可以使用位元組碼增強技術,來修改類的行為。例如,可以用Javaagent修改String類,使其在執行equals時,不區分大小寫。
public class StringEqualsAgent { public static void premain(String agentArgs) { new AgentBuilder.Default().type(ElementMatchers.named("java.lang.String")) .transform((builder, typeDescription, classLoader, module) -> builder .defineMethod("equalsIgnoreCase", boolean.class, Visibility.PUBLIC) .withParameters(String.class) .intercept(MethodDelegation.to(StringEqualsIgnoreCase.class))) .installOnBytecodeArtifact(); } public static class StringEqualsIgnoreCase { @SuppressWarnings("unused") @RuntimeType public static boolean equalsIgnoreCase( @This Object thiz, @AllArguments Object[] arguments) { String anotherString = (String) arguments[0]; if (thiz instanceof String) { String thisString = (String) thiz; return thisString.equalsIgnoreCase(anotherString); } return false; } } }
3.2 拒絕反射機制的訪問
Javaagent也可用於增強JVM的安全性。例如,可以拒絕應用程序通過反射機制訪問java.lang.Runtime的exec()方法。
public class RuntimeAccessAgent { public static void premain(String agentArgs) { new AgentBuilder.Default().type(ElementMatchers.any()) .transform((builder, typeDescription, classLoader, module) -> builder .method(ElementMatchers.named("exec")) .intercept(MethodDelegation.to(RejectRuntimeAccess.class))) .installOnBytecodeArtifact(); } public static class RejectRuntimeAccess { @SuppressWarnings("unused") @RuntimeType public static Process exec(@This Object thiz, @AllArguments Object[] arguments) { throw new SecurityException("Javaagent rejected Runtime.exec()"); } } }
四、總結
Javaagent可以對Java應用程序進行監控、增強和安全管理等操作,不需要修改應用程序代碼。
在大型生產環境中,Javaagent通常與監控系統結合使用,用於實時監控應用程序的性能和行為,並對問題進行分析和診斷。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/245507.html