JVM源碼分析之javaagent原理完全解讀(轉載)

概述

本文重點講述javaagent的具體實現,因為它面向的是我們Java程序員,而且agent都是用Java編寫的,不需要太多的C/C++編程基礎,不過這篇文章里也會講到JVMTIAgent(C實現的),因為javaagent的運行還是依賴于一個特殊的JVMTIAgent。

對于javaagent,或許大家都聽過,甚至使用過,常見的用法大致如下:

java

-javaagent:myagent.jar=mode=test Test

我們通過-javaagent來指定我們編寫的agent的jar路徑(./myagent.jar),以及要傳給agent的參數(mode=test),在啟動的時候這個agent就可以做一些我們希望的事了。

javaagent的主要功能如下:

·? ? ? 可以在加載class文件之前做攔截,對字節碼做修改

·? ? ? 可以在運行期對已加載類的字節碼做變更,但是這種情況下會有很多的限制,后面會詳細說

·? ? ? 還有其他一些小眾的功能

o? 獲取所有已經加載過的類

o? 獲取所有已經初始化過的類(執行過clinit方法,是上面的一個子集)

o? 獲取某個對象的大小

o? 將某個jar加入到bootstrap classpath里作為高優先級被bootstrapClassloader加載

o? 將某個jar加入到classpath里供AppClassloard去加載

o? 設置某些native方法的前綴,主要在查找native方法的時候做規則匹配

想象一下可以讓程序按照我們預期的邏輯去執行,聽起來是不是挺酷的。

JVMTI

JVMTI全稱JVM Tool Interface,是JVM暴露出來的一些供用戶擴展的接口集合。JVMTI是基于事件驅動的,JVM每執行到一定的邏輯就會調用一些事件的回調接口(如果有的話),這些接口可以供開發者擴展自己的邏輯。

比如最常見的,我們想在某個類的字節碼文件讀取之后、類定義之前修改相關的字節碼,從而使創建的class對象是我們修改之后的字節碼內容,那就可以實現一個回調函數賦給jvmtiEnv(JVMTI的運行時,通常一個JVMTIAgent對應一個jvmtiEnv,但是也可以對應多個)的回調方法集合里的ClassFileLoadHook,這樣在接下來的類文件加載過程中都會調用到這個函數中,大致實現如下:,

? jvmtiEventCallbacks callbacks;

? jvmtiEnv*? ? ? ? jvmtienv= jvmti(agent);

? jvmtiError? ? ? ? ? jvmtierror;

? ? memset(&callbacks, 0, sizeof(callbacks));

? callbacks.ClassFileLoadHook = &eventHandlerClassFileLoadHook;

? jvmtierror= (*jvmtienv)->SetEventCallbacks( jvmtienv,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &callbacks,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(callbacks));

JVMTIAgent

JVMTIAgent其實就是一個動態庫,利用JVMTI暴露出來的一些接口來干一些我們想做、但是正常情況下又做不到的事情,不過為了和普通的動態庫進行區分,它一般會實現如下的一個或者多個函數:

JNIEXPORT jint JNICALL

Agent_OnLoad(JavaVM *vm, char *options, void *reserved);

JNIEXPORT jint JNICALL

Agent_OnAttach(JavaVM* vm, char* options, void* reserved);

JNIEXPORT voidJNICALL

Agent_OnUnload(JavaVM *vm);

·? ? ? Agent_OnLoad函數,如果agent是在啟動時加載的,也就是在vm參數里通過-agentlib來指定的,那在啟動過程中就會去執行這個agent里的Agent_OnLoad函數。

·? ? ? Agent_OnAttach函數,如果agent不是在啟動時加載的,而是我們先attach到目標進程上,然后給對應的目標進程發送load命令來加載,則在加載過程中會調用Agent_OnAttach函數。

·? ? ? Agent_OnUnload函數,在agent卸載時調用,不過貌似基本上很少實現它。

其實我們每天都在和JVMTIAgent打交道,只是你可能沒有意識到而已,比如我們經常使用Eclipse等工具調試Java代碼,其實就是利用JRE自帶的jdwp agent實現的,只是Eclipse等工具在沒讓你察覺的情況下將相關參數(類似-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:61349)自動加到程序啟動參數列表里了,其中agentlib參數就用來跟要加載的agent的名字,比如這里的jdwp(不過這不是動態庫的名字,JVM會做一些名稱上的擴展,比如在Linux下會去找libjdwp.so的動態庫進行加載,也就是在名字的基礎上加前綴lib,再加后綴.so),接下來會跟一堆相關的參數,將這些參數傳給Agent_OnLoad或者Agent_OnAttach函數里對應的options。

javaagent

說到javaagent,必須要講的是一個叫做instrument的JVMTIAgent(Linux下對應的動態庫是libinstrument.so),因為javaagent功能就是它來實現的,另外instrument agent還有個別名叫JPLISAgent(Java Programming Language

Instrumentation Services Agent),這個名字也完全體現了其最本質的功能:就是專門為Java語言編寫的插樁服務提供支持的。

instrument

agent

instrument

agent實現了Agent_OnLoad和Agent_OnAttach兩方法,也就是說在使用時,agent既可以在啟動時加載,也可以在運行時動態加載。其中啟動時加載還可以通過類似-javaagent:myagent.jar的方式來間接加載instrument agent,運行時動態加載依賴的是JVM的attach機制(JVM Attach機制實現),通過發送load命令來加載agent。

instrument

agent的核心數據結構如下:

struct _JPLISAgent {

? JavaVM*? ? ? ? ? ? ? ? mJVM;? ? ? ? ? ? ? ? ? /* handle

to the JVM */

? JPLISEnvironment? ? ? mNormalEnvironment;? ? /* for

every thing but retransform stuff */

? JPLISEnvironment? ? ? mRetransformEnvironment;/* for retransform stuff only */

? jobject? ? ? ? ? ? ? ? mInstrumentationImpl;? /* handle

to the Instrumentation instance */

? jmethodID? ? ? ? ? ? ? mPremainCaller;? ? ? ? /* method

on the InstrumentationImpl that does the premain stuff (cached to save lots of

lookups) */

? ? jmethodID? ? ? ? ? ? ? mAgentmainCaller;? ? ? /* method

on the InstrumentationImpl for agents loaded via attach mechanism */

? jmethodID? ? ? ? ? ? ? mTransform;? ? ? ? ? ? /* method

on the InstrumentationImpl that does the class file transform */

? ? jboolean? ? ? ? ? ? ? ? mRedefineAvailable;? ? /* cached answer to "does this agent

support redefine" */

? jboolean? ? ? ? ? ? ? mRedefineAdded;? ? ? ? /*

indicates if can_redefine_classes capability has been added */

? jboolean? ? ? ? ? ? ? ? mNativeMethodPrefixAvailable; /* cached

answer to "does this agent support prefixing" */

? jboolean? ? ? ? ? ? ? mNativeMethodPrefixAdded;? ? /*

indicates if can_set_native_method_prefix capability has been added */

? ? char const *? ? ? ? ? ? mAgentClassName;? ? ? ? /* agent

class name */

? ? char const *? ? ? ? ? ? mOptionsString;? ? ? ? /*

-javaagent options string */

};

struct _JPLISEnvironment {

? jvmtiEnv*? ? ? ? ? ? mJVMTIEnv;? ? ? ? ? ? /* the JVM

TI environment */

? JPLISAgent*? ? ? ? ? ? mAgent;? ? ? ? ? ? ? ? /*

corresponding agent */

? jboolean? ? ? ? ? ? ? mIsRetransformer;? ? ? /*

indicates if special environment */

};

這里解釋一下幾個重要項:

·? ? ? mNormalEnvironment:主要提供正常的類transform及redefine功能。

·? ? ? mRetransformEnvironment:主要提供類retransform功能。

·? ? ? mInstrumentationImpl:這個對象非常重要,也是我們Java agent和JVM進行交互的入口,或許寫過javaagent的人在寫`premain`以及`agentmain`方法的時候注意到了有個Instrumentation參數,該參數其實就是這里的對象。

·? ? ? mPremainCaller:指向`sun.instrument.InstrumentationImpl.loadClassAndCallPremain`方法,如果agent是在啟動時加載的,則該方法會被調用。

·? ? ? mAgentmainCaller:指向`sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain`方法,該方法在通過attach的方式動態加載agent的時候調用。

·? ? ? mTransform:指向`sun.instrument.InstrumentationImpl.transform`方法。

·? ? ? mAgentClassName:在我們javaagent的MANIFEST.MF里指定的`Agent-Class`。

·? ? ? mOptionsString:傳給agent的一些參數。

·? ? ? mRedefineAvailable:是否開啟了redefine功能,在javaagent的MANIFEST.MF里設置`Can-Redefine-Classes:true`。

·? ? ? mNativeMethodPrefixAvailable:是否支持native方法前綴設置,同樣在javaagent的MANIFEST.MF里設置`Can-Set-Native-Method-Prefix:true`。

·? ? ? mIsRetransformer:如果在javaagent的MANIFEST.MF文件里定義了`Can-Retransform-Classes:true`,將會設置mRetransformEnvironment的mIsRetransformer為true。

在啟動時加載instrument agent

正如前面“概述”里提到的方式,就是啟動時加載instrument agent,具體過程都在`InvocationAdapter.c`的`Agent_OnLoad`方法里,這里簡單描述下過程:

·? ? ? 創建并初始化JPLISAgent

·? ? ? 監聽VMInit事件,在vm初始化完成之后做下面的事情:

o? 創建InstrumentationImpl對象

o? 監聽ClassFileLoadHook事件

o? 調用InstrumentationImpl的`loadClassAndCallPremain`方法,在這個方法里會調用javaagent里MANIFEST.MF里指定的`Premain-Class`類的premain方法

·? ? ? 解析javaagent里MANIFEST.MF里的參數,并根據這些參數來設置JPLISAgent里的一些內容

在運行時加載instrument agent

在運行時加載的方式,大致按照下面的方式來操作:

VirtualMachine vm = VirtualMachine.attach(pid);

vm.loadAgent(agentPath, agentArgs);

上面會通過JVM的attach機制來請求目標JVM加載對應的agent,過程大致如下:

·? ? ? 創建并初始化JPLISAgent

·? ? ? 解析javaagent里MANIFEST.MF里的參數

·? ? ? 創建InstrumentationImpl對象

·? ? ? 監聽ClassFileLoadHook事件

·? ? ? 調用InstrumentationImpl的loadClassAndCallAgentmain方法,在這個方法里會調用javaagent里MANIFEST.MF里指定的Agent-Class類的agentmain方法

instrument

agent的ClassFileLoadHook回調實現

不管是啟動時還是運行時加載的instrument agent,都關注著同一個jvmti事件——ClassFileLoadHook,這個事件是在讀取字節碼文件之后回調時用的,這樣可以對原來的字節碼做修改,那這里面究竟是怎樣實現的呢?

voidJNICALL

eventHandlerClassFileLoadHook(? jvmtiEnv*? ? ? ? ? ? ? jvmtienv,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? JNIEnv*? ? ? ? ? ? ? ? jnienv,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? jclass? ? ? ? ? ? ? ? ? class_being_redefined,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? jobject? ? ? ? ? ? ? ? loader,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const char*? ? ? ? ? ? name,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? jobject? ? ? ? ? ? ? ? protectionDomain,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? jint? ? ? ? ? ? ? ? ? ? class_data_len,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? const unsigned char*? ? class_data,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? jint*? ? ? ? ? ? ? ? ? new_class_data_len,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? unsigned char**? ? ? ? new_class_data) {

? JPLISEnvironment*environment = NULL;

? ? environment= getJPLISEnvironment(jvmtienv);

? ? /* if

something is internally inconsistent (no agent), just silently return without

touching the buffer */

? ? if ( environment != NULL ) {

? ? ? jthrowable outstandingException= preserveThrowable(jnienv);

? ? ? transformClassFile( environment->mAgent,

? ? ? ? ? ? ? ? ? ? ? ? ? ? jnienv,

? ? ? ? ? ? ? ? ? ? ? ? ? ? loader,

? ? ? ? ? ? ? ? ? ? ? ? ? ? name,

? ? ? ? ? ? ? ? ? ? ? ? ? class_being_redefined,

? ? ? ? ? ? ? ? ? ? ? ? ? ? protectionDomain,

? ? ? ? ? ? ? ? ? ? ? ? ? ? class_data_len,

? ? ? ? ? ? ? ? ? ? ? ? ? ? class_data,

? ? ? ? ? ? ? ? ? ? ? ? ? ? new_class_data_len,

? ? ? ? ? ? ? ? ? ? ? ? ? ? new_class_data,

? ? ? ? ? ? ? ? ? ? ? ? ? ? environment->mIsRetransformer);

? ? ? ? restoreThrowable(jnienv, outstandingException);

? ? }

}

先根據jvmtiEnv取得對應的JPLISEnvironment,因為上面我已經說到其實有兩個JPLISEnvironment(并且有兩個jvmtiEnv),其中一個是專門做retransform的,而另外一個用來做其他事情,根據不同的用途,在注冊具體的ClassFileTransformer時也是分開的,對于作為retransform用的ClassFileTransformer,我們會注冊到一個單獨的TransformerManager里。

接著調用transformClassFile方法,由于函數實現比較長,這里就不貼代碼了,大致意思就是調用InstrumentationImpl對象的transform方法,根據最后那個參數來決定選哪個TransformerManager里的ClassFileTransformer對象們做transform操作。

private byte[]

? ? transform(? ClassLoader? ? ? ? loader,

? ? ? ? ? ? ? String? ? ? ? ? ? ? classname,

? ? ? ? ? ? ? ? Class? ? ? ? ? ? ? classBeingRedefined,

? ? ? ? ? ? ? ? ProtectionDomain? ? protectionDomain,

? ? ? ? ? ? ? ? byte[]? ? ? ? ? ? ? classfileBuffer,

? ? ? ? ? ? ? ? boolean? ? ? ? ? ? isRetransformer) {

? ? ? TransformerManager mgr= isRetransformer?

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mRetransfomableTransformerManager:

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mTransformerManager;

? ? ? if (mgr == null) {

? ? ? ? ? return null; // no

manager, no transform

? ? ? } else {

? ? ? ? ? return mgr.transform(? loader,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? classname,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? classBeingRedefined,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? protectionDomain,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? classfileBuffer);

? ? ? }

? ? }

? public byte[]

? ? transform(? ClassLoader? ? ? ? loader,

? ? ? ? ? ? ? ? String? ? ? ? ? ? ? classname,

? ? ? ? ? ? ? ? Class? ? ? ? ? ? ? classBeingRedefined,

? ? ? ? ? ? ? ? ProtectionDomain? ? protectionDomain,

? ? ? ? ? ? ? ? byte[]? ? ? ? ? ? ? classfileBuffer) {

? ? ? booleansomeoneTouchedTheBytecode = false;

? ? ? TransformerInfo[] transformerList= getSnapshotTransformerList();

? ? ? byte[]? bufferToUse= classfileBuffer;

? ? ? // order

matters, gotta run 'em in the order they were added

? ? ? for ( int x = 0; x < transformerList.length; x++ ) {

? ? ? ? ? TransformerInfo? ? ? ? transformerInfo= transformerList[x];

? ? ? ? ? ClassFileTransformer? transformer= transformerInfo.transformer();

? ? ? ? ? byte[]? ? ? ? ? ? ? ? ? transformedBytes= null;

? ? ? ? ? try {

? ? ? ? ? ? ? ? transformedBytes= transformer.transform(? loader,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? classname,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? classBeingRedefined,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? protectionDomain,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bufferToUse);

? ? ? ? ? }

? ? ? ? ? catch (Throwable t) {

? ? ? ? ? ? ? ? // don't

let any one transformer mess it up for the others.

? ? ? ? ? ? ? ? // This is

where we need to put some logging. What should go here? FIXME

? ? ? ? ? }

? ? ? ? ? if ( transformedBytes != null ) {

? ? ? ? ? ? ? ? someoneTouchedTheBytecode= true;

? ? ? ? ? ? ? ? bufferToUse= transformedBytes;

? ? ? ? ? }

? ? ? }

? ? ? // if

someone modified it, return the modified buffer.

? ? ? //

otherwise return null to mean "no transforms occurred"

? ? ? byte [] result;

? ? ? if (someoneTouchedTheBytecode ) {

? ? ? ? ? result= bufferToUse;

? ? ? }

? ? ? else {

? ? ? ? ? result= null;

? ? ? }

? ? ? return result;

? ? }?

以上是最終調到的java代碼,可以看到已經調用到我們自己編寫的javaagent代碼里了,我們一般是實現一個ClassFileTransformer類,然后創建一個對象注冊到對應的TransformerManager里。

Class

Transform的實現

這里說的class transform其實是狹義的,主要是針對第一次類文件加載時就要求被transform的場景,在加載類文件的時候發出ClassFileLoad事件,然后交給instrumenat agent來調用javaagent里注冊的ClassFileTransformer實現字節碼的修改。

Class

Redefine的實現

類重新定義,這是Instrumentation提供的基礎功能之一,主要用在已經被加載過的類上,想對其進行修改,要做這件事,我們必須要知道兩個東西,一個是要修改哪個類,另外一個是想將那個類修改成怎樣的結構,有了這兩個信息之后就可以通過InstrumentationImpl下面的redefineClasses方法操作了:

public void redefineClasses(ClassDefinition[]? definitions) throws? ClassNotFoundException{

? ? ? if (!isRedefineClassesSupported()) {

? ? ? ? ? throw new UnsupportedOperationException("redefineClasses

is not supported in this environment");

? ? ? }

? ? ? if (definitions == null) {

? ? ? ? ? ? throw new NullPointerException("null

passed as 'definitions' in redefineClasses");

? ? ? }

? ? ? for (int i = 0; i < definitions.length; ++i) {

? ? ? ? ? if (definitions[i] == null) {

? ? ? ? ? ? ? ? throw new NullPointerException("element

of 'definitions' is null in redefineClasses");

? ? ? ? ? }

? ? ? }

? ? ? if (definitions.length == 0) {

? ? ? ? ? return; //

short-circuit if there are no changes requested

? ? ? }

? ? ? redefineClasses0(mNativeAgent, definitions);

? ? }

在JVM里對應的實現是創建一個VM_RedefineClasses的VM_Operation,注意執行它的時候會stop-the-world:

jvmtiError

JvmtiEnv::RedefineClasses(jint class_count, const jvmtiClassDefinition* class_definitions) {

//TODO: add locking

VM_RedefineClassesop(class_count, class_definitions,jvmti_class_load_kind_redefine);

VMThread::execute(&op);

? return (op.check_error());

} /* end RedefineClasses */

這個過程我盡量用語言來描述清楚,不詳細貼代碼了,因為代碼量實在有點大:

·? ? ? 挨個遍歷要批量重定義的jvmtiClassDefinition

·? ? ? 然后讀取新的字節碼,如果有關注ClassFileLoadHook事件的,還會走對應的transform來對新的字節碼再做修改

·? ? ? 字節碼解析好,創建一個klassOop對象

·? ? ? 對比新老類,并要求如下:

o? 父類是同一個

o? 實現的接口數也要相同,并且是相同的接口

o? 類訪問符必須一致

o? 字段數和字段名要一致

o? 新增的方法必須是private static/final的

o? 可以刪除修改方法

·? ? ? 對新類做字節碼校驗

·? ? ? 合并新老類的常量池

·? ? ? 如果老類上有斷點,那都清除掉

·? ? ? 對老類做JIT去優化

·? ? ? 對新老方法匹配的方法的jmethodId做更新,將老的jmethodId更新到新的method上

·? ? ? 新類的常量池的holer指向老的類

·? ? ? 將新類和老類的一些屬性做交換,比如常量池,methods,內部類

·? ? ? 初始化新的vtable和itable

·? ? ? 交換annotation的method、field、paramenter

·? ? ? 遍歷所有當前類的子類,修改他們的vtable及itable

上面是基本的過程,總的來說就是只更新了類里的內容,相當于只更新了指針指向的內容,并沒有更新指針,避免了遍歷大量已有類對象對它們進行更新所帶來的開銷。

Class

Retransform的實現

retransform

class可以簡單理解為回滾操作,具體回滾到哪個版本,這個需要看情況而定,下面不管那種情況都有一個前提,那就是javaagent已經要求要有retransform的能力了:

·? ? ? 如果類是在第一次加載的的時候就做了transform,那么做retransform的時候會將代碼回滾到transform之后的代碼

·? ? ? 如果類是在第一次加載的的時候沒有任何變化,那么做retransform的時候會將代碼回滾到最原始的類文件里的字節碼

·? ? ? 如果類已經加載了,期間類可能做過多次redefine(比如被另外一個agent做過),但是接下來加載一個新的agent要求有retransform的能力了,然后對類做redefine的動作,那么retransform的時候會將代碼回滾到上一個agent最后一次做redefine后的字節碼

我們從InstrumentationImpl的retransformClasses方法參數看猜到應該是做回滾操作,因為我們只指定了class:

? ? public void retransformClasses(Class[] classes) {

? ? ? if (!isRetransformClassesSupported()) {

? ? ? ? ? throw new UnsupportedOperationException( "retransformClasses

is not supported in this environment");

? ? ? }

? ? ? retransformClasses0(mNativeAgent, classes);

? ? }

不過retransform的實現其實也是通過redefine的功能來實現,在類加載的時候有比較小的差別,主要體現在究竟會走哪些transform上,如果當前是做retransform的話,那將忽略那些注冊到正常的TransformerManager里的ClassFileTransformer,而只會走專門為retransform而準備的TransformerManager的ClassFileTransformer,不然想象一下字節碼又被無聲無息改成某個中間態了。

private:

? void post_all_envs() {

? ? if (_load_kind !=jvmti_class_load_kind_retransform) {

? ? ? // for

class load and redefine,

? ? ? // call

the non-retransformable agents

? ? JvmtiEnvIterator it;

? ? ? for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {

? ? ? if (!env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {

? ? ? ? //

non-retransformable agents cannot retransform back,

? ? ? ? // so no

need to cache the original class file bytes

? ? ? ? post_to_env(env, false);

? ? ? }

? ? ? }

? ? }

? JvmtiEnvIterator it;

? ? for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {

? ? ? //

retransformable agents get all events

? ? ? if (env->is_retransformable() && env->is_enabled(JVMTI_EVENT_CLASS_FILE_LOAD_HOOK)) {

? ? ? //

retransformable agents need to cache the original class file

? ? ? // bytes

if changes are made via the ClassFileLoadHook

? ? ? post_to_env(env, true);

? ? ? }

? ? }

? }

javaagent的其他小眾功能

javaagent除了做字節碼上面的修改之外,其實還有一些小功能,有時候還是挺有用的

·? ? ? 獲取所有已經被加載的類:Class[]

getAllLoadedClasses();

·? ? ? 獲取所有已經初始化了的類: Class[]

getInitiatedClasses(ClassLoader loader);

·? ? ? 獲取某個對象的大小: long

getObjectSize(Object objectToSize);

·? ? ? 將某個jar加入到bootstrap classpath里優先其他jar被加載: void

appendToBootstrapClassLoaderSearch(JarFile jarfile);

·? ? ? 將某個jar加入到classpath里供appclassloard去加載:void appendToSystemClassLoaderSearch(JarFile

jarfile);

·? ? ? 設置某些native方法的前綴,主要在找native方法的時候做規則匹配: void

setNativeMethodPrefix(ClassFileTransformer transformer, String prefix)。

作者簡介

李嘉鵬,花名寒泉子,使用“你假笨”的ID混跡網絡

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容