日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

从javaagent迁移到JVMTI:我们的经验

發(fā)布時(shí)間:2023/12/3 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从javaagent迁移到JVMTI:我们的经验 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

當(dāng)您需要從JVM內(nèi)部收集數(shù)據(jù)時(shí),您會(huì)發(fā)現(xiàn)自己很危險(xiǎn)地接近Java虛擬機(jī)內(nèi)部進(jìn)行工作。 幸運(yùn)的是,有一些方法可以避免被JVM實(shí)現(xiàn)細(xì)節(jié)所困擾。 Java之父沒(méi)有給您提供過(guò)兩個(gè)漂亮的工具供您使用。

在這篇文章中,我們將說(shuō)明兩種方法之間的差異,并說(shuō)明為什么我們最近移植了算法的重要部分。

Java代理

第一種選擇是使用java.lang.instrument接口。 這種方法使用-javaagent啟動(dòng)參數(shù)將監(jiān)視代碼加載到JVM本身。 作為Java的全部選擇,如果您的背景是Java開(kāi)發(fā),那么javaagents往往是首選的方法。 說(shuō)明您如何從該方法中受益的最佳方法是通過(guò)示例。

讓我們創(chuàng)建一個(gè)真正簡(jiǎn)單的代理,該代理將負(fù)責(zé)監(jiān)視代碼中的所有方法調(diào)用。 當(dāng)代理面對(duì)方法調(diào)用時(shí),它將調(diào)用記錄到標(biāo)準(zhǔn)輸出流中:

import org.objectweb.asm.*;public class MethodVisitorNotifyOnMethodEntry extends MethodVisitor {public MethodVisitorNotifyOnMethodEntry(MethodVisitor mv) {super(Opcodes.ASM4, mv);mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(MethodVisitorNotifyOnMethodEntry.class), "callback", "()V");}public static void callback() {System.out.println("Method called!"); } }

您可以使用上面的示例,將其打包為javaagent(本質(zhì)上是一個(gè)帶有特殊MANIFEST.MF的小JAR文件),然后使用類似于以下內(nèi)容的代理的premain()方法啟動(dòng)它:

java -javaagent:path-to/your-agent.jar com.yourcompany.YourClass

啟動(dòng)后,您會(huì)看到一堆“方法調(diào)用!” 日志文件中的消息。 就我們而言,僅此而已。 但是這個(gè)概念很強(qiáng)大,尤其是與上面的示例中的字節(jié)碼檢測(cè)工具(例如ASM或cgLib)結(jié)合使用時(shí)。

為了使示例易于理解,我們跳過(guò)了一些細(xì)節(jié)。 但這是相對(duì)簡(jiǎn)單的-使用java.lang.instrument包時(shí),您首先編寫自己的代理類,并實(shí)現(xiàn)public static void premain(String agentArgs,Instrumentation inst) 。 然后,您需要向inst.addTransformer注冊(cè)您的ClassTransformer 。 您很可能希望避免直接對(duì)類字節(jié)碼進(jìn)行操作,因此可以使用一些字節(jié)碼操作庫(kù),例如我們所使用的示例中的ASM。 有了它,您只需要實(shí)現(xiàn)幾個(gè)接口– ClassVisitor (為簡(jiǎn)便起見(jiàn),略過(guò))和MethodVisitor。

JVMTI

第二種方法最終將帶您進(jìn)入JVMTI。 JVM工具接口( JVM TI )是標(biāo)準(zhǔn)的本機(jī)API,允許本機(jī)庫(kù)捕獲事件并控制Java虛擬機(jī)。 對(duì)JVMTI的訪問(wèn)通常打包在稱為代理的特定庫(kù)中。

下面的示例演示了與javaagent部分相同的回調(diào)注冊(cè),但是這次將其實(shí)現(xiàn)為JVMTI調(diào)用:

void JNICALL notifyOnMethodEntry(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method) {fputs("method was called!\n", stdout); }int prepareNotifyOnMethodEntry(jvmtiEnv *jvmti) {jvmtiError error;jvmtiCapabilities requestedCapabilities, potentialCapabilities;memset(&requestedCapabilities, 0, sizeof(requestedCapabilities));if((error = (*jvmti)->GetPotentialCapabilities(jvmti, &potentialCapabilities)) != JVMTI_ERROR_NONE) return 0;if(potentialCapabilities.can_generate_method_entry_events) {requestedCapabilities.can_generate_method_entry_events = 1;}else {//not possible on this JVMreturn 0;}if((error = (*jvmti)->AddCapabilities(jvmti, &requestedCapabilities)) != JVMTI_ERROR_NONE) return 0;jvmtiEventCallbacks callbacks;memset(&callbacks, 0, sizeof(callbacks));callbacks.MethodEntry = notifyOnMethodEntry;if((error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, sizeof(callbacks))) != JVMTI_ERROR_NONE) return 0;if((error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, (jthread)NULL)) != JVMTI_ERROR_NONE) return 0;return 1; }

兩種方法之間存在一些差異。 例如,通過(guò)JVMTI,您可以獲得比代理更多的信息。 但是,兩者之間最關(guān)鍵的區(qū)別在于加載機(jī)制。 在將Instrumentation代理加載到堆內(nèi)部時(shí),它們由同一JVM進(jìn)行管理。 JVMTI代理不受JVM規(guī)則支配,因此不受JVM內(nèi)部組件(如GC或運(yùn)行時(shí)錯(cuò)誤處理)的影響。 這意味著什么,最好通過(guò)我們自己的經(jīng)驗(yàn)來(lái)解釋。

從-javaagent遷移到JVMTI

三年前,當(dāng)我們開(kāi)始構(gòu)建內(nèi)存泄漏檢測(cè)器時(shí),我們并沒(méi)有過(guò)多地關(guān)注這些方法的優(yōu)缺點(diǎn)。 我們毫不猶豫地將解決方案實(shí)現(xiàn)為-javaagent 。

多年來(lái),我們已經(jīng)開(kāi)始理解含義。 其中一些不太令人滿意,因此在我們的最新版本中,我們將內(nèi)存泄漏檢測(cè)機(jī)制的重要部分移植到了本機(jī)代碼。 是什么使我們跳到這樣的結(jié)論?

首先-當(dāng)駐留在堆中時(shí),您需要將自己容納在應(yīng)用程序自己的內(nèi)存結(jié)構(gòu)旁邊。 通過(guò)痛苦的經(jīng)驗(yàn)中學(xué)到的東西本身就可能導(dǎo)致問(wèn)題。 當(dāng)您的應(yīng)用程序已將堆填滿到最大程度時(shí),您最后需要做的是一個(gè)內(nèi)存泄漏檢測(cè)器,它似乎只會(huì)加快OutOfMemoryError的到達(dá)速度。

但是增加的堆空間減少了困擾我們的弊端。 真正的問(wèn)題與以下事實(shí)有關(guān):使用受監(jiān)視的應(yīng)用程序本身正在使用的同一垃圾收集器清理了我們的數(shù)據(jù)結(jié)構(gòu)。 這導(dǎo)致更長(zhǎng)或更頻繁的GC暫停。

盡管大多數(shù)應(yīng)用程序不介意我們?yōu)槎严奶砑拥膸讉€(gè)額外的百分點(diǎn),但我們了解到,需要消除對(duì)Full GC暫停產(chǎn)生不可預(yù)測(cè)的影響。

更糟糕的是– Plumbr的工作方式是監(jiān)視所有對(duì)象的創(chuàng)建和集合。 監(jiān)視某物時(shí),您需要保持跟蹤。 跟蹤往往會(huì)創(chuàng)建對(duì)象。 創(chuàng)建的對(duì)象將有資格使用GC。 現(xiàn)在,當(dāng)您監(jiān)視的是GC時(shí),您剛剛創(chuàng)建了一個(gè)惡性循環(huán)-收集到更多的對(duì)象的垃圾,創(chuàng)建的監(jiān)視器越多,觸發(fā)的GC運(yùn)行頻率越高,等等。

跟蹤對(duì)象時(shí),JVMTI會(huì)通知我們有關(guān)對(duì)象死亡的信息。 但是,JVMTI不允許在這些回調(diào)期間使用JNI。 因此,如果我們使用Java保留有關(guān)跟蹤對(duì)象的統(tǒng)計(jì)信息,則當(dāng)我們收到更改通知時(shí),不可能立即更新統(tǒng)計(jì)信息。 相反,當(dāng)我們知道JVM處于正確狀態(tài)時(shí),需要將更改緩存并應(yīng)用。 這造成了不必要的復(fù)雜性和更新實(shí)際統(tǒng)計(jì)數(shù)據(jù)的延遲。

翻譯自: https://www.javacodegeeks.com/2014/03/migrating-from-javaagent-to-jvmti-our-experience.html

總結(jié)

以上是生活随笔為你收集整理的从javaagent迁移到JVMTI:我们的经验的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。