原文地址:http://www.infoq.com/cn/articles/javaagent-illustrated
文章里也会讲到jvmtiAgent(C实现的),因为javaagent的运行还是依赖于一个特殊的jvmtiAgent。
用法大致如下:
java -javaagent:myagent.jar=mode=test Test
功能如下:
- 文件之前做拦截,对字节码做修改
- 功能
- 获取所有已经加载过的类
- 获取所有已经初始化过的类(执行过clinit方法,是上面的一个子集)
- 获取某个对象的大小
- sspath里作为高优先级被bootstrapClassloader加载
- sspath里供AppClassloard去加载
- 方法的前缀,主要在查找native方法的时候做规则匹配
jvmti
jvmtiEnv <span class="token operator">* jvmtienv <span class="token operator">= <span class="token function">jvmti<span class="token punctuation">(agent<span class="token punctuation">)<span class="token punctuation">;
jvmtiError jvmtierror<span class="token punctuation">;
<span class="token function">memset<span class="token punctuation">(<span class="token operator">&callbacks<span class="token punctuation">,<span class="token number">0<span class="token punctuation">,<span class="token keyword">sizeof<span class="token punctuation">(callbacks<span class="token punctuation">)<span class="token punctuation">)<span class="token punctuation">;
callbacks<span class="token punctuation">.ClassFileLoadHook <span class="token operator">= <span class="token operator">&eventHandlerClassFileLoadHook<span class="token punctuation">;
jvmtierror <span class="token operator">= <span class="token punctuation">(<span class="token operator">*jvmtienv<span class="token punctuation">)<span class="token operator">-<span class="token operator">><span class="token function">SetEventCallbacks<span class="token punctuation">( jvmtienv<span class="token punctuation">,<span class="token operator">&callbacks<span class="token punctuation">,<span class="token keyword">sizeof<span class="token punctuation">(callbacks<span class="token punctuation">)<span class="token punctuation">)<span class="token punctuation">;</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<h1 class="p1"><span class="s1">JVMTIAgent
<p class="p2"><span class="s1">JVMTIAgent其实就是一个动态库,利用JVMTI暴露出来的一些接口来干一些我们想做、但是正常情况下又做不到的事情,不过为了和普通的动态库进行区分,它一般会实现如下的一个或者多个函数:
<pre class=" language-cpp"><code class=" language-cpp">JNIEXPORT jint JNICALL
<span class="token function">Agent_OnLoad<span class="token punctuation">(JavaVM <span class="token operator">vm<span class="token punctuation">,<span class="token keyword">char <span class="token operator">options<span class="token punctuation">,<span class="token keyword">void <span class="token operator">*reserved<span class="token punctuation">)<span class="token punctuation">;
JNIEXPORT jint JNICALL
<span class="token function">Agent_OnAttach<span class="token punctuation">(JavaVM<span class="token operator"> vm<span class="token punctuation">,<span class="token keyword">char<span class="token operator"> options<span class="token punctuation">,<span class="token keyword">void<span class="token operator">* reserved<span class="token punctuation">)<span class="token punctuation">;
JNIEXPORT <span class="token keyword">void JNICALL
<span class="token function">Agent_OnUnload<span class="token punctuation">(JavaVM <span class="token operator">*vm<span class="token punctuation">)<span class="token punctuation">;
<ul class="ul1">
<li class="li2"><span class="s4">Agent_OnLoad<span class="s1">函数,如果agent是在启动时加载的,也就是在vm参数里通过-agentlib来指定的,那在启动过程中就会去执行这个agent里的<span class="s4">Agent_OnLoad<span class="s1">函数。
<li class="li2"><span class="s4">Agent_OnAttach<span class="s1">函数,如果agent不是在启动时加载的,而是我们先attach到目标进程上,然后给对应的目标进程发送load命令来加载,则在加载过程中会调用<span class="s4">Agent_OnAttach<span class="s1">函数。
<li class="li2"><span class="s4">Agent_OnUnload<span class="s1">函数,在agent卸载时调用,不过貌似基本上很少实现它。
jplISAgent jplISEnvironment mnormalEnvironmentjplISEnvironment mRetransformEnvironmentredefineAvailableredefine" */
jboolean mredefineAddedredefine_classes capability has been added */
jboolean mNativeMethodPrefixAvailablesstringstruct _jplISEnvironment <span class="token punctuation">{
jvmtiEnv <span class="token operator"> mjvmtiEnv<span class="token punctuation">; <span class="token comment">/ the JVM TI environment /
jplISAgent <span class="token operator"> mAgent<span class="token punctuation">; <span class="token comment">/ corresponding agent /
jboolean mIsRetransformer<span class="token punctuation">; <span class="token comment">/ indicates if special environment /
<span class="token punctuation">}<span class="token punctuation">;
<p class="p2"><span class="s1">这里解释一下几个重要项:
- normalEnvironment:主要提供正常的类transform及redefine功能。
- 功能。
- 方法的时候注意到了有个Instrumentation参数,该参数其实就是这里的对象。
- 方法,如果agent是在启动时加载的,则该方法会被调用。
- 方法,该方法在通过attach的方式动态加载agent的时候调用。
- 方法。
- sstring:传给agent的一些参数。
- redefineAvailable:是否开启了redefine功能,在javaagent的MANIFEST.MF里设置`Can-redefine-Classes:true`。
- 支持native方法前缀设置,同样在javaagent的MANIFEST.MF里设置`Can-Set-Native-Method-Prefix:true`。
- 文件里定义了`Can-Retransform-Classes:true`,将会设置mRetransformEnvironment的mIsRetransformer为true。
方法里,这里简单描述下过程:
- jplISAgent
- jplISAgent里的一些内容
一个jvmti事件——文件之后回调时用的,这样可以对原来的字节码做修改,那这里面究竟是怎样实现的呢?
<span class="token function">eventHandlerClassFileLoadHook<span class="token punctuation">( jvmtiEnv <span class="token operator"> jvmtienv<span class="token punctuation">,jnienv <span class="token operator"> jnienv<span class="token punctuation">,jclass <span class="token class-name">classbeingredefined<span class="token punctuation">,jobject loader<span class="token punctuation">,<span class="token keyword">const <span class="token keyword">char<span class="token operator"> name<span class="token punctuation">,jobject protectionDomain<span class="token punctuation">,jint class_data_len<span class="token punctuation">,<span class="token keyword">const <span class="token keyword">unsigned <span class="token keyword">char<span class="token operator"> class_data<span class="token punctuation">,jint<span class="token operator"> new_class_data_len<span class="token punctuation">,<span class="token keyword">unsigned <span class="token keyword">char<span class="token operator"><span class="token operator">* new_class_data<span class="token punctuation">) <span class="token punctuation">{<a href="/tag/jpl/" target="_blank" class="keywords">jpl</a>ISEnvironment <span class="token operator">* environment <span class="token operator">= <span class="token constant">NULL<span class="token punctuation">; environment <span class="token operator">= <span class="token function">get<a href="/tag/jpl/" target="_blank" class="keywords">jpl</a>ISEnvironment<span class="token punctuation">(<a href="/tag/jvmti/" target="_blank" class="keywords">jvmti</a>env<span class="token punctuation">)<span class="token punctuation">; <span class="token comment">/* if something is internally inconsistent (no agent),just silently return without touching the buffer */ <span class="token keyword">if <span class="token punctuation">( environment <span class="token operator">!= <span class="token constant">NULL <span class="token punctuation">) <span class="token punctuation">{ jthrowable outstandingException <span class="token operator">= <span class="token function">preserveThrowable<span class="token punctuation">(<a href="/tag/jnienv/" target="_blank" class="keywords">jnienv</a><span class="token punctuation">)<span class="token punctuation">; <span class="token function">transformClassFile<span class="token punctuation">( environment<span class="token operator">-<span class="token operator">>mAgent<span class="token punctuation">,<a href="/tag/jnienv/" target="_blank" class="keywords">jnienv</a><span class="token punctuation">,loader<span class="token punctuation">,name<span class="token punctuation">,class_being_<a href="/tag/redefine/" target="_blank" class="keywords">redefine</a>d<span class="token punctuation">,protectionDomain<span class="token punctuation">,class_data_len<span class="token punctuation">,class_data<span class="token punctuation">,new_class_data_len<span class="token punctuation">,new_class_data<span class="token punctuation">,environment<span class="token operator">-<span class="token operator">>mIsRetransformer<span class="token punctuation">)<span class="token punctuation">; <span class="token function">restoreThrowable<span class="token punctuation">(<a href="/tag/jnienv/" target="_blank" class="keywords">jnienv</a><span class="token punctuation">,outstandingException<span class="token punctuation">)<span class="token punctuation">; <span class="token punctuation">}
<span class="token punctuation">}
<p class="p2"><span class="s1">先根据jvmtiEnv取得对应的jplISEnvironment,因为上面我已经说到其实有两个jplISEnvironment(并且有两个jvmtiEnv),其中一个是专门做retransform的,而另外一个用来做其他事情,根据不同的用途,在注册具体的ClassFileTransformer时也是分开的,对于作为retransform用的ClassFileTransformer,我们会注册到一个单独的TransformerManager里。
<p class="p2"><span class="s1">接着调用transformClassFile方法,由于函数实现比较长,这里就不贴代码了,大致意思就是调用InstrumentationImpl对象的transform方法,根据最后那个参数来决定选哪个TransformerManager里的ClassFileTransformer对象们做transform操作。
<pre class=" language-java"><code class=" language-java"><span class="token keyword">private <span class="token keyword">byte<span class="token punctuation">[<span class="token punctuation">]
<span class="token function">transform<span class="token punctuation">( ClassLoader loader<span class="token punctuation">,String classname<span class="token punctuation">,Class <span class="token class-name">classBeingredefined<span class="token punctuation">,ProtectionDomain protectionDomain<span class="token punctuation">,<span class="token keyword">byte<span class="token punctuation">[<span class="token punctuation">] classfilebuffer<span class="token punctuation">,<span class="token keyword">boolean isRetransformer<span class="token punctuation">) <span class="token punctuation">{
TransformerManager mgr <span class="token operator">= isRetransformer<span class="token operator">?
mRetransfomableTransformerManager <span class="token operator">:
mTransformerManager<span class="token punctuation">;
<span class="token keyword">if <span class="token punctuation">(mgr <span class="token operator">== null<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">return null<span class="token punctuation">; <span class="token comment">// no manager,no transform
<span class="token punctuation">} <span class="token keyword">else <span class="token punctuation">{
<span class="token keyword">return mgr<span class="token punctuation">.<span class="token function">transform<span class="token punctuation">( loader<span class="token punctuation">,classname<span class="token punctuation">,classBeing<a href="/tag/redefine/" target="_blank" class="keywords">redefine</a>d<span class="token punctuation">,class<a href="/tag/filebuf/" target="_blank" class="keywords">filebuf</a>fer<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token keyword">public <span class="token keyword">byte<span class="token punctuation">[<span class="token punctuation">]
<span class="token function">transform<span class="token punctuation">( ClassLoader loader<span class="token punctuation">,<span class="token keyword">byte<span class="token punctuation">[<span class="token punctuation">] class<a href="/tag/filebuf/" target="_blank" class="keywords">filebuf</a>fer<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">boolean someon<a href="/tag/eto/" target="_blank" class="keywords">eto</a>uchedTheBytecode <span class="token operator">= <span class="token boolean">false<span class="token punctuation">;
TransformerInfo<span class="token punctuation">[<span class="token punctuation">] transformerList <span class="token operator">= <span class="token function">getSnapsh<a href="/tag/ott/" target="_blank" class="keywords">ott</a>ransformerList<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">;
<span class="token keyword">byte<span class="token punctuation">[<span class="token punctuation">] bufferToUse <span class="token operator">= class<a href="/tag/filebuf/" target="_blank" class="keywords">filebuf</a>fer<span class="token punctuation">;
<span class="token comment">// order matters,g<a href="/tag/ott/" target="_blank" class="keywords">ott</a>a run 'em in the order t<a href="/tag/hey/" target="_blank" class="keywords">hey</a> were added
<span class="token keyword">for <span class="token punctuation">( <span class="token keyword">int x <span class="token operator">= <span class="token number">0<span class="token punctuation">; x <span class="token operator">< transformerList<span class="token punctuation">.length<span class="token punctuation">; x<span class="token operator">++ <span class="token punctuation">) <span class="token punctuation">{
TransformerInfo transformerInfo <span class="token operator">= transformerList<span class="token punctuation">[x<span class="token punctuation">]<span class="token punctuation">;
ClassFileTransformer transformer <span class="token operator">= transformerInfo<span class="token punctuation">.<span class="token function">transformer<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">;
<span class="token keyword">byte<span class="token punctuation">[<span class="token punctuation">] transformedBytes <span class="token operator">= null<span class="token punctuation">;
<span class="token keyword">try <span class="token punctuation">{
transformedBytes <span class="token operator">= transformer<span class="token punctuation">.<span class="token function">transform<span class="token punctuation">( loader<span class="token punctuation">,bufferToUse<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">catch <span class="token punctuation">(<span class="token class-name">Throwable t<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// don't let any one transformer mess it up for the others.
<span class="token comment">// This is where we need to put some logging. What should go here? FIXME
<span class="token punctuation">}
<span class="token keyword">if <span class="token punctuation">( transformedBytes <span class="token operator">!= null <span class="token punctuation">) <span class="token punctuation">{
someon<a href="/tag/eto/" target="_blank" class="keywords">eto</a>uchedTheBytecode <span class="token operator">= <span class="token boolean">true<span class="token punctuation">;
bufferToUse <span class="token operator">= transformedBytes<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token comment">// if someone modified it,return the modified buffer.
<span class="token comment">// otherwise return null to mean "no transforms occurred"
<span class="token keyword">byte <span class="token punctuation">[<span class="token punctuation">] result<span class="token punctuation">;
<span class="token keyword">if <span class="token punctuation">( someon<a href="/tag/eto/" target="_blank" class="keywords">eto</a>uchedTheBytecode <span class="token punctuation">) <span class="token punctuation">{
result <span class="token operator">= bufferToUse<span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">else <span class="token punctuation">{
result <span class="token operator">= null<span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">return result<span class="token punctuation">;
<span class="token punctuation">}
<p class="p3">以上是最终调到的java代码,可以看到已经调用到我们自己编写的javaagent代码里了,我们一般是实现一个ClassFileTransformer类,然后创建一个对象注册到对应的TransformerManager里。
<h1 class="p1"><span class="s1">Class Transform的实现
<p class="p2"><span class="s1">这里说的class transform其实是狭义的,主要是针对第一次类文件加载时就要求被transform的场景,在加载类文件的时候发出ClassFileLoad事件,然后交给instrumenat agent来调用javaagent里注册的ClassFileTransformer实现字节码的修改。
<h1 class="p1"><span class="s1">Class redefine的实现
<p class="p2"><span class="s1">类重新定义,这是Instrumentation提供的基础功能之一,主要用在已经被加载过的类上,想对其进行修改,要做这件事,我们必须要知道两个东西,一个是要修改哪个类,另外一个是想将那个类修改成怎样的结构,有了这两个信息之后就可以通过InstrumentationImpl下面的redefineClasses方法操作了:
<pre class=" language-java"><code class=" language-java"><span class="token keyword">public <span class="token keyword">void <span class="token function">redefineClasses<span class="token punctuation">(ClassDeFinition<span class="token punctuation">[<span class="token punctuation">] deFinitions<span class="token punctuation">) <span class="token keyword">throws ClassNotFoundException <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(<span class="token operator">!<span class="token function">is<a href="/tag/redefine/" target="_blank" class="keywords">redefine</a>ClassesSupported<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">throw <span class="token keyword">new <span class="token class-name">UnsupportedOperationException<span class="token punctuation">(<span class="token string">"<a href="/tag/redefine/" target="_blank" class="keywords">redefine</a>Classes is not supported in this environment"<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">if <span class="token punctuation">(de<a href="/tag/Finitio/" target="_blank" class="keywords">Finitio</a>ns <span class="token operator">== null<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">throw <span class="token keyword">new <span class="token class-name">NullPointerException<span class="token punctuation">(<span class="token string">"null passed as 'de<a href="/tag/Finitio/" target="_blank" class="keywords">Finitio</a>ns' in <a href="/tag/redefine/" target="_blank" class="keywords">redefine</a>Classes"<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token keyword">for <span class="token punctuation">(<span class="token keyword">int i <span class="token operator">= <span class="token number">0<span class="token punctuation">; i <span class="token operator">< de<a href="/tag/Finitio/" target="_blank" class="keywords">Finitio</a>ns<span class="token punctuation">.length<span class="token punctuation">; <span class="token operator">++i<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(de<a href="/tag/Finitio/" target="_blank" class="keywords">Finitio</a>ns<span class="token punctuation">[i<span class="token punctuation">] <span class="token operator">== null<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">throw <span class="token keyword">new <span class="token class-name">NullPointerException<span class="token punctuation">(<span class="token string">"element of 'de<a href="/tag/Finitio/" target="_blank" class="keywords">Finitio</a>ns' is null in <a href="/tag/redefine/" target="_blank" class="keywords">redefine</a>Classes"<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token keyword">if <span class="token punctuation">(de<a href="/tag/Finitio/" target="_blank" class="keywords">Finitio</a>ns<span class="token punctuation">.length <span class="token operator">== <span class="token number">0<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">return<span class="token punctuation">; <span class="token comment">// short-circuit if there are no changes requested
<span class="token punctuation">}
<span class="token function"><a href="/tag/redefine/" target="_blank" class="keywords">redefine</a>Classes0<span class="token punctuation">(mNativeAgent<span class="token punctuation">,de<a href="/tag/Finitio/" target="_blank" class="keywords">Finitio</a>ns<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<p class="p3"><span class="s1">在JVM里对应的实现是创建一个<span class="s4">VM_redefineClasses<span class="s1">的<span class="s4">VM_Operation<span class="s1">,注意执行它的时候会stop-the-world:
<pre class=" language-cpp"><code class=" language-cpp">jvmtiError
jvmtiEnv<span class="token operator">::<span class="token function">redefineClasses<span class="token punctuation">(jint class_count<span class="token punctuation">,<span class="token keyword">const jvmtiClassDeFinition<span class="token operator">* class_deFinitions<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">//Todo: add locking
VM_redefineClasses <span class="token function">op<span class="token punctuation">(class_count<span class="token punctuation">,class_deFinitions<span class="token punctuation">,jvmti_class_loadkindredefine<span class="token punctuation">)<span class="token punctuation">;
VMThread<span class="token operator">::<span class="token function">execute<span class="token punctuation">(<span class="token operator">&op<span class="token punctuation">)<span class="token punctuation">;
<span class="token keyword">return <span class="token punctuation">(op<span class="token punctuation">.<span class="token function">check_error<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">} <span class="token comment">/ end redefineClasses /
<p class="p2"><span class="s1">这个过程我尽量用语言来描述清楚,不详细贴代码了,因为代码量实在有点大:
<ul class="ul1">
<li class="li2"><span class="s1">挨个遍历要批量重定义的jvmtiClassDeFinition
<li class="li2"><span class="s1">然后读取新的字节码,如果有关注ClassFileLoadHook事件的,还会走对应的transform来对新的字节码再做修改
<li class="li2"><span class="s1">字节码解析好,创建一个klassOop对象
<li class="li2">
<span class="s1"><span class="s1">对比新老类,并要求如下:
<ul class="ul2">
<li class="li2"><span class="s1">父类是同一个
<li class="li2"><span class="s1">实现的接口数也要相同,并且是相同的接口
<li class="li2"><span class="s1">类访问符必须一致
<li class="li2"><span class="s1">字段数和字段名要一致
<li class="li2"><span class="s1">新增的方法必须是private static/final的
<li class="li2"><span class="s1">可以删除修改方法
内容,相当于只更新了指针指向的内容,并没有更新指针,避免了遍历大量已有类对象对它们进行更新所带来的开销。
一个前提,那就是javaagent已经要求要有retransform的能力了:
- 代码回滚到transform之后的代码
- 代码回滚到最原始的类文件里的字节码
- redefine(比如被另外一个agent做过),但是接下来加载一个新的agent要求有retransform的能力了,然后对类做redefine的动作,那么retransform的时候会将代码回滚到上一个agent最后一次做redefine后的字节码
方法参数看猜到应该是做回滚操作,因为我们只指定了class:
<span class="token keyword">if <span class="token punctuation">(<span class="token operator">!<span class="token function">isRetransformClassesSupported<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">throw <span class="token keyword">new <span class="token class-name">UnsupportedOperationException<span class="token punctuation">( <span class="token string">"retransformClasses is not supported in this environment"<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token function">retransformClasses0<span class="token punctuation">(mNativeAgent<span class="token punctuation">,classes<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p class="p2"><span class="s1">不过retransform的实现其实也是通过redefine的功能来实现,在类加载的时候有比较小的差别,主要体现在究竟会走哪些transform上,如果当前是做retransform的话,那将忽略那些注册到正常的TransformerManager里的ClassFileTransformer,而只会走专门为retransform而准备的TransformerManager的ClassFileTransformer,不然想象一下字节码又被无声无息改成某个中间态了。
<pre class=" language-java"><code class=" language-java"><span class="token keyword">private<span class="token operator">:
<span class="token keyword">void <span class="token function">post_all_envs<span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(_load_kind <span class="token operator">!= <a href="/tag/jvmti/" target="_blank" class="keywords">jvmti</a>_class_load_kind_retransform<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// for class load and <a href="/tag/redefine/" target="_blank" class="keywords">redefine</a>,<span class="token comment">// call the non-retransformable agents
<a href="/tag/jvmti/" target="_blank" class="keywords">jvmti</a>EnvI<a href="/tag/tera/" target="_blank" class="keywords">tera</a>tor it<span class="token punctuation">;
<span class="token keyword">for <span class="token punctuation">(<a href="/tag/jvmti/" target="_blank" class="keywords">jvmti</a>Env<span class="token operator">* env <span class="token operator">= it<span class="token punctuation">.<span class="token function">f<a href="/tag/irs/" target="_blank" class="keywords">irs</a>t<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">; env <span class="token operator">!= NULL<span class="token punctuation">; env <span class="token operator">= it<span class="token punctuation">.<span class="token function">next<span class="token punctuation">(env<span class="token punctuation">)<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(<span class="token operator">!env<span class="token operator">-<span class="token operator">><span class="token function">is_retransformable<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">&& env<span class="token operator">-<span class="token operator">><span class="token function">is_enabled<span class="token punctuation">(<a href="/tag/jvmti/" target="_blank" class="keywords">jvmti</a>_EVENT_CLASS_FILE_LOAD_HOOK<span class="token punctuation">)<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// non-retransformable agents cannot retransform back,<span class="token comment">// so no need to cache the original class file bytes
<span class="token function">post_to_env<span class="token punctuation">(env<span class="token punctuation">,<span class="token boolean">false<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token punctuation">}
<a href="/tag/jvmti/" target="_blank" class="keywords">jvmti</a>EnvI<a href="/tag/tera/" target="_blank" class="keywords">tera</a>tor it<span class="token punctuation">;
<span class="token keyword">for <span class="token punctuation">(<a href="/tag/jvmti/" target="_blank" class="keywords">jvmti</a>Env<span class="token operator">* env <span class="token operator">= it<span class="token punctuation">.<span class="token function">f<a href="/tag/irs/" target="_blank" class="keywords">irs</a>t<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">; env <span class="token operator">!= NULL<span class="token punctuation">; env <span class="token operator">= it<span class="token punctuation">.<span class="token function">next<span class="token punctuation">(env<span class="token punctuation">)<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// retransformable agents get all events
<span class="token keyword">if <span class="token punctuation">(env<span class="token operator">-<span class="token operator">><span class="token function">is_retransformable<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">&& env<span class="token operator">-<span class="token operator">><span class="token function">is_enabled<span class="token punctuation">(<a href="/tag/jvmti/" target="_blank" class="keywords">jvmti</a>_EVENT_CLASS_FILE_LOAD_HOOK<span class="token punctuation">)<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// retransformable agents need to cache the original class file
<span class="token comment">// bytes if changes are made via the ClassFileLoadHook
<span class="token function">post_to_env<span class="token punctuation">(env<span class="token punctuation">,<span class="token boolean">true<span class="token punctuation">)<span class="token punctuation">;
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token punctuation">}
<h1 class="p3">javaagent的其他小众功能
<p class="p2"><span class="s1">javaagent除了做字节码上面的修改之外,其实还有一些小功能,有时候还是挺有用的
<ul class="ul1">
<li class="li6"><span class="s7">获取所有已经被加载的类:<span class="s8">Class[] getAllLoadedClasses();
<li class="li6"><span class="s7">获取所有已经初始化了的类: <span class="s8">Class[] getinitiatedClasses(ClassLoader loader);
<li class="li6"><span class="s7">获取某个对象的大小: <span class="s8">long getobjectsize(Object objectToSize);
<li class="li6"><span class="s7">将某个jar加入到bootstrap classpath里优先其他jar被加载: <span class="s8">void appendToBootstrapClassLoaderSearch(JarFile jarfile);
<li class="li6"><span class="s7">将某个jar加入到classpath里供appclassloard去加载:<span class="s8">void appendToSystemClassLoaderSearch(JarFile jarfile);
<li class="li6"><span class="s7">设置某些native方法的前缀,主要在找native方法的时候做规则匹配: <span class="s8">void setNativeMethodPrefix(ClassFileTransformer transformer,String prefix)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。