做者:小傅哥
博客:https://bugstack.cnhtml
沉澱、分享、成長,讓本身和他人都能有所收穫!😄java
五常大米好吃!
ios
哈哈哈,是不你總買五常大米,其實五常和榆樹是挨着的,榆樹大米也好吃,榆樹仍是天下第一糧倉呢!可是五常出名,因此只認識五常。c++
爲何提這個呢,由於阿里不容許使用 Executors 建立線程池!其餘不少大廠也不容許,這麼建立的話,控制很差會出現OOM。git
好,本篇就帶你學習四種線程池的不一樣使用方式、業務場景應用以及如何監控線程。github
謝飛機,小記!
,上次從面試官那逃跑後,惡補了多線程,本身好像也內捲了,因此出門逛逛!面試
面試官:嗨,飛機,飛機,這邊!spring
謝飛機:嗯?!哎呀,面試官你咋來南海子公園了?編程
面試官:我家就附近,跑步來了。最近你咋樣,上次問你的多線程學了嗎?緩存
謝飛機:哎,看了是看了,記不住鴨!
面試官:嗯,不經常使用確實記不住。不過你能夠選擇跳槽,來大廠,大廠的業務體量較大!
謝飛機:我就糾結呢,想回家考教師資格證了,咱們村小學要教java了!
面試官:哈哈哈哈哈,一塊兒!
Executors
是建立線程池的工具類,比較典型常見的四種線程池包括:newFixedThreadPool
、newSingleThreadExecutor
、newCachedThreadPool
、newScheduledThreadPool
。每一種都有本身特定的典型例子,能夠按照每種的特性用在不一樣的業務場景,也能夠作爲參照精細化建立線程池。
public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 1; i < 5; i++) { int groupId = i; executorService.execute(() -> { for (int j = 1; j < 5; j++) { try { Thread.sleep(1000); } catch (InterruptedException ignored) { } logger.info("第 {} 組任務,第 {} 次執行完成", groupId, j); } }); } executorService.shutdown(); } // 測試結果 23:48:24.628 [pool-2-thread-1] INFO o.i.i.test.Test_newFixedThreadPool - 第 1 組任務,第 1 次執行完成 23:48:24.628 [pool-2-thread-2] INFO o.i.i.test.Test_newFixedThreadPool - 第 2 組任務,第 1 次執行完成 23:48:24.628 [pool-2-thread-3] INFO o.i.i.test.Test_newFixedThreadPool - 第 3 組任務,第 1 次執行完成 23:48:25.633 [pool-2-thread-3] INFO o.i.i.test.Test_newFixedThreadPool - 第 3 組任務,第 2 次執行完成 23:48:25.633 [pool-2-thread-1] INFO o.i.i.test.Test_newFixedThreadPool - 第 1 組任務,第 2 次執行完成 23:48:25.633 [pool-2-thread-2] INFO o.i.i.test.Test_newFixedThreadPool - 第 2 組任務,第 2 次執行完成 23:48:26.633 [pool-2-thread-3] INFO o.i.i.test.Test_newFixedThreadPool - 第 3 組任務,第 3 次執行完成 23:48:26.633 [pool-2-thread-2] INFO o.i.i.test.Test_newFixedThreadPool - 第 2 組任務,第 3 次執行完成 23:48:26.633 [pool-2-thread-1] INFO o.i.i.test.Test_newFixedThreadPool - 第 1 組任務,第 3 次執行完成 23:48:27.634 [pool-2-thread-3] INFO o.i.i.test.Test_newFixedThreadPool - 第 3 組任務,第 4 次執行完成 23:48:27.634 [pool-2-thread-2] INFO o.i.i.test.Test_newFixedThreadPool - 第 2 組任務,第 4 次執行完成 23:48:27.634 [pool-2-thread-1] INFO o.i.i.test.Test_newFixedThreadPool - 第 1 組任務,第 4 次執行完成 23:48:28.635 [pool-2-thread-3] INFO o.i.i.test.Test_newFixedThreadPool - 第 4 組任務,第 1 次執行完成 23:48:29.635 [pool-2-thread-3] INFO o.i.i.test.Test_newFixedThreadPool - 第 4 組任務,第 2 次執行完成 23:48:30.635 [pool-2-thread-3] INFO o.i.i.test.Test_newFixedThreadPool - 第 4 組任務,第 3 次執行完成 23:48:31.636 [pool-2-thread-3] INFO o.i.i.test.Test_newFixedThreadPool - 第 4 組任務,第 4 次執行完成 Process finished with exit code 0
圖解
new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
LinkedBlockingQueue
無界阻塞隊列存放等待線程。public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 1; i < 5; i++) { int groupId = i; executorService.execute(() -> { for (int j = 1; j < 5; j++) { try { Thread.sleep(1000); } catch (InterruptedException ignored) { } logger.info("第 {} 組任務,第 {} 次執行完成", groupId, j); } }); } executorService.shutdown(); } // 測試結果 23:20:15.066 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 1 組任務,第 1 次執行完成 23:20:16.069 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 1 組任務,第 2 次執行完成 23:20:17.070 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 1 組任務,第 3 次執行完成 23:20:18.070 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 1 組任務,第 4 次執行完成 23:20:19.071 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 2 組任務,第 1 次執行完成 23:23:280.071 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 2 組任務,第 2 次執行完成 23:23:281.072 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 2 組任務,第 3 次執行完成 23:23:282.072 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 2 組任務,第 4 次執行完成 23:23:283.073 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 3 組任務,第 1 次執行完成 23:23:284.074 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 3 組任務,第 2 次執行完成 23:23:285.074 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 3 組任務,第 3 次執行完成 23:23:286.075 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 3 組任務,第 4 次執行完成 23:23:287.075 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 4 組任務,第 1 次執行完成 23:23:288.075 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 4 組任務,第 2 次執行完成 23:23:289.076 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 4 組任務,第 3 次執行完成 23:20:30.076 [pool-2-thread-1] INFO o.i.i.t.Test_newSingleThreadExecutor - 第 4 組任務,第 4 次執行完成
圖解
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 1; i < 5; i++) { int groupId = i; executorService.execute(() -> { for (int j = 1; j < 5; j++) { try { Thread.sleep(1000); } catch (InterruptedException ignored) { } logger.info("第 {} 組任務,第 {} 次執行完成", groupId, j); } }); } executorService.shutdown(); // 測試結果 23:25:59.818 [pool-2-thread-2] INFO o.i.i.test.Test_newCachedThreadPool - 第 2 組任務,第 1 次執行完成 23:25:59.818 [pool-2-thread-3] INFO o.i.i.test.Test_newCachedThreadPool - 第 3 組任務,第 1 次執行完成 23:25:59.818 [pool-2-thread-1] INFO o.i.i.test.Test_newCachedThreadPool - 第 1 組任務,第 1 次執行完成 23:25:59.818 [pool-2-thread-4] INFO o.i.i.test.Test_newCachedThreadPool - 第 4 組任務,第 1 次執行完成 23:25:00.823 [pool-2-thread-4] INFO o.i.i.test.Test_newCachedThreadPool - 第 4 組任務,第 2 次執行完成 23:25:00.823 [pool-2-thread-1] INFO o.i.i.test.Test_newCachedThreadPool - 第 1 組任務,第 2 次執行完成 23:25:00.823 [pool-2-thread-2] INFO o.i.i.test.Test_newCachedThreadPool - 第 2 組任務,第 2 次執行完成 23:25:00.823 [pool-2-thread-3] INFO o.i.i.test.Test_newCachedThreadPool - 第 3 組任務,第 2 次執行完成 23:25:01.823 [pool-2-thread-4] INFO o.i.i.test.Test_newCachedThreadPool - 第 4 組任務,第 3 次執行完成 23:25:01.823 [pool-2-thread-1] INFO o.i.i.test.Test_newCachedThreadPool - 第 1 組任務,第 3 次執行完成 23:25:01.824 [pool-2-thread-2] INFO o.i.i.test.Test_newCachedThreadPool - 第 2 組任務,第 3 次執行完成 23:25:01.824 [pool-2-thread-3] INFO o.i.i.test.Test_newCachedThreadPool - 第 3 組任務,第 3 次執行完成 23:25:02.824 [pool-2-thread-1] INFO o.i.i.test.Test_newCachedThreadPool - 第 1 組任務,第 4 次執行完成 23:25:02.824 [pool-2-thread-4] INFO o.i.i.test.Test_newCachedThreadPool - 第 4 組任務,第 4 次執行完成 23:25:02.825 [pool-2-thread-3] INFO o.i.i.test.Test_newCachedThreadPool - 第 3 組任務,第 4 次執行完成 23:25:02.825 [pool-2-thread-2] INFO o.i.i.test.Test_newCachedThreadPool - 第 2 組任務,第 4 次執行完成 }
圖解
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>())
SynchronousQueue
是一個生產消費模式的阻塞任務隊列,只要有任務就須要有線程執行,線程池中的線程能夠重複使用。public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); executorService.schedule(() -> { logger.info("3秒後開始執行"); }, 3, TimeUnit.SECONDS); executorService.scheduleAtFixedRate(() -> { logger.info("3秒後開始執行,之後每2秒執行一次"); }, 3, 2, TimeUnit.SECONDS); executorService.scheduleWithFixedDelay(() -> { logger.info("3秒後開始執行,後續延遲2秒"); }, 3, 2, TimeUnit.SECONDS); } // 測試結果 23:28:32.442 [pool-2-thread-1] INFO o.i.i.t.Test_newScheduledThreadPool - 3秒後開始執行 23:28:32.444 [pool-2-thread-1] INFO o.i.i.t.Test_newScheduledThreadPool - 3秒後開始執行,之後每2秒執行一次 23:28:32.444 [pool-2-thread-1] INFO o.i.i.t.Test_newScheduledThreadPool - 3秒後開始執行,後續延遲2秒 23:28:34.441 [pool-2-thread-1] INFO o.i.i.t.Test_newScheduledThreadPool - 3秒後開始執行,之後每2秒執行一次 23:28:34.445 [pool-2-thread-1] INFO o.i.i.t.Test_newScheduledThreadPool - 3秒後開始執行,後續延遲2秒 23:28:36.440 [pool-2-thread-1] INFO o.i.i.t.Test_newScheduledThreadPool - 3秒後開始執行,之後每2秒執行一次 23:28:36.445 [pool-2-thread-1] INFO o.i.i.t.Test_newScheduledThreadPool - 3秒後開始執行,後續延遲2秒
圖解
![圖 22-4 newScheduledThreadPool 執行過程
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new ScheduledThreadPoolExecutor.DelayedWorkQueue()); }
Integer.MAX_VALUE
。它提供的調用方法比較多,包括:scheduleAtFixedRate
、scheduleWithFixedDelay
,能夠按需選擇延遲執行方式。何時使用線程池?
說簡單是當爲了給老闆省錢的時候,由於使用線程池能夠下降服務器資源的投入,讓每臺機器儘量更大限度的使用CPU。
😄那你這麼說確定沒辦法升職加薪了!
因此若是說的高大上一點,那麼是在符合科特爾法則和阿姆達爾定律 的狀況下,引入線程池的使用最爲合理。啥意思呢,還得簡單說!
假如:咱們有一套電商服務,用戶瀏覽商品的併發訪問速率是:1000客戶/每分鐘,平均每一個客戶在服務器上的耗時0.5分鐘。根據利特爾法則,在任什麼時候刻,服務端都承擔着1000*0.5=500個客戶的業務處理量。過段時間大促了,併發訪問的用戶擴了一倍2000客戶了,那怎麼保障服務性能呢?
因此:在有些場景下會把串行的請求接口,壓縮成並行執行,如圖 22-5
可是,線程池的使用會隨着業務場景變化而不一樣,若是你的業務須要大量的使用線程池,並不是常依賴線程池,那麼就不可能用 Executors
工具類中提供的方法。由於這些線程池的建立都不夠精細化,也很是容易形成OOM風險,並且隨着業務場景邏輯不一樣,會有IO密集型和CPU密集型。
最終,你們使用的線程池都是使用 new ThreadPoolExecutor()
建立的,固然也有基於Spring的線程池配置 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
。
可你想過嗎,一樣一個接口在有活動時候怎麼辦、有大促時候怎麼辦,可能你當時設置的線程池是合理的,可是一到流量很是大的時候就很不適合了,因此若是能動態調整線程池就很是有必要了。並且使用 new ThreadPoolExecutor()
方式建立的線程池是能夠經過提供的 set 方法進行動態調整的。有了這個動態調整的方法後,就能夠把線程池包裝起來,在配合動態調整的頁面,動態更新線程池參數,就能夠很是方便的調整線程池了。
你收過報警短信嗎?
收過,半夜還有報警機器人打電話呢!崴,你的系統有個機器睡着了,快起來看看!!!
因此,若是你高頻、高依賴線程池,那麼有一個完整的監控系統,就非重要了。總不能線上掛了,你還不知道!
可監控內容
方法 | 含義 |
---|---|
getActiveCount() | 線程池中正在執行任務的線程數量 |
getCompletedTaskCount() | 線程池已完成的任務數量,該值小於等於taskCount |
getCorePoolSize() | 線程池的核心線程數量 |
getLargestPoolSize() | 線程池曾經建立過的最大線程數量。經過這個數據能夠知道線程池是否滿過,也就是達到了maximumPoolSize |
getMaximumPoolSize() | 線程池的最大線程數量 |
getPoolSize() | 線程池當前的線程數量 |
getTaskCount() | 線程池已經執行的和未執行的任務總數 |
若是咱們想監控一個線程池的方法執行動做,最簡單的方式就是繼承這個類,重寫方法,在方法中添加動做收集信息。
僞代碼
public class ThreadPoolMonitor extends ThreadPoolExecutor { @Override public void shutdown() { // 統計已執行任務、正在執行任務、未執行任務數量 super.shutdown(); } @Override public List<Runnable> shutdownNow() { // 統計已執行任務、正在執行任務、未執行任務數量 return super.shutdownNow(); } @Override protected void beforeExecute(Thread t, Runnable r) { // 記錄開始時間 } @Override protected void afterExecute(Runnable r, Throwable t) { // 記錄完成耗時 } ... }
這塊是監控的重點,由於咱們不太可能讓每個須要監控的線程池都來重寫的方式記錄,這樣的改形成本過高了。
那麼除了這個笨方法外,能夠選擇使用基於JVMTI的方式,進行開發監控組件。
JVMTI:JVMTI(JVM Tool Interface)位於jpda最底層,是Java虛擬機所提供的native編程接口。JVMTI能夠提供性能分析、debug、內存管理、線程分析等功能。
基於jvmti提供的接口服務,運用C++代碼(win32-add_library)在Agent_OnLoad裏開發監控服務,並生成dll文件。開發完成後在java代碼中加入agentpath,這樣就能夠監控到咱們須要的信息內容。
環境準備:
配置信息:(路徑相關修改成本身的)
Java工程
public class TestLocationException { public static void main(String[] args) { Logger logger = Logger.getLogger("TestLocationException"); try { PartnerEggResourceImpl resource = new PartnerEggResourceImpl(); Object obj = resource.queryUserInfoById(null); logger.info("測試結果:" + obj); } catch (Exception e) { //屏蔽異常 } } } class PartnerEggResourceImpl { Logger logger = Logger.getLogger("PartnerEggResourceImpl"); public Object queryUserInfoById(String userId) { logger.info("根據用戶Id獲取用戶信息" + userId); if (null == userId) { throw new NullPointerException("根據用戶Id獲取用戶信息,空指針異常"); } return userId; } }
c++監控
#include <iostream> #include <cstring> #include "jvmti.h" using namespace std; //異常回調函數 static void JNICALL callbackException(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thr, jmethodID methodId, jlocation location, jobject exception, jmethodID catch_method, jlocation catch_location) { // 得到方法對應的類 jclass clazz; jvmti_env->GetMethodDeclaringClass(methodId, &clazz); // 得到類的簽名 char *class_signature; jvmti_env->GetClassSignature(clazz, &class_signature, nullptr); //過濾非本工程類信息 string::size_type idx; string class_signature_str = class_signature; idx = class_signature_str.find("org/itstack"); if (idx != 1) { return; } //異常類名稱 char *exception_class_name; jclass exception_class = env->GetObjectClass(exception); jvmti_env->GetClassSignature(exception_class, &exception_class_name, nullptr); // 得到方法名稱 char *method_name_ptr, *method_signature_ptr; jvmti_env->GetMethodName(methodId, &method_name_ptr, &method_signature_ptr, nullptr); //獲取目標方法的起止地址和結束地址 jlocation start_location_ptr; //方法的起始位置 jlocation end_location_ptr; //用於方法的結束位置 jvmti_env->GetMethodLocation(methodId, &start_location_ptr, &end_location_ptr); //輸出測試結果 cout << "測試結果 - 定位類的簽名:" << class_signature << endl; cout << "測試結果 - 定位方法信息:" << method_name_ptr << " -> " << method_signature_ptr << endl; cout << "測試結果 - 定位方法位置:" << start_location_ptr << " -> " << end_location_ptr + 1 << endl; cout << "測試結果 - 異常類的名稱:" << exception_class_name << endl; cout << "測試結果-輸出異常信息(能夠分析行號):" << endl; jclass throwable_class = (*env).FindClass("java/lang/Throwable"); jmethodID print_method = (*env).GetMethodID(throwable_class, "printStackTrace", "()V"); (*env).CallVoidMethod(exception, print_method); } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { jvmtiEnv *gb_jvmti = nullptr; //初始化 vm->GetEnv(reinterpret_cast<void **>(&gb_jvmti), JVMTI_VERSION_1_0); // 建立一個新的環境 jvmtiCapabilities caps; memset(&caps, 0, sizeof(caps)); caps.can_signal_thread = 1; caps.can_get_owned_monitor_info = 1; caps.can_generate_method_entry_events = 1; caps.can_generate_exception_events = 1; caps.can_generate_vm_object_alloc_events = 1; caps.can_tag_objects = 1; // 設置當前環境 gb_jvmti->AddCapabilities(&caps); // 建立一個新的回調函數 jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); //異常回調 callbacks.Exception = &callbackException; // 設置回調函數 gb_jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)); // 開啓事件監聽(JVMTI_EVENT_EXCEPTION) gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, nullptr); return JNI_OK; } JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm) { }
測試結果
在 VM vptions 中配置:-agentpath:E:\itstack\git\github.com\itstack-jvmti\cmake-build-debug\libitstack_jvmti.dll
十二月 16, 2020 23:53:27 下午 org.itstack.demo.PartnerEggResourceImpl queryUserInfoById 信息: 根據用戶Id獲取用戶信息null java.lang.NullPointerException: 根據用戶Id獲取用戶信息,空指針異常 at org.itstack.demo.PartnerEggResourceImpl.queryUserInfoById(TestLocationException.java:26) at org.itstack.demo.TestLocationException.main(TestLocationException.java:13) 測試結果-定位類的簽名:Lorg/itstack/demo/PartnerEggResourceImpl; 測試結果-定位方法信息:queryUserInfoById -> (Ljava/lang/String;)Ljava/lang/Object; 測試結果-定位方法位置:0 -> 43 測試結果-異常類的名稱:Ljava/lang/NullPointerException; 測試結果-輸出異常信息(能夠分析行號):
其實方法差很少,都是基於C++開發DLL文件,引入使用。不過這部分代碼會監控方法信息,並採集線程的執行內容。
static void JNICALL callbackMethodEntry(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thr, jmethodID method) { // 得到方法對應的類 jclass clazz; jvmti_env->GetMethodDeclaringClass(method, &clazz); // 得到類的簽名 char *class_signature; jvmti_env->GetClassSignature(clazz, &class_signature, nullptr); //過濾非本工程類信息 string::size_type idx; string class_signature_str = class_signature; idx = class_signature_str.find("org/itstack"); gb_jvmti->RawMonitorEnter(gb_lock); { //must be deallocate char *name = NULL, *sig = NULL, *gsig = NULL; jint thr_hash_code = 0; error = gb_jvmti->GetMethodName(method, &name, &sig, &gsig); error = gb_jvmti->GetObjectHashCode(thr, &thr_hash_code); if (strcmp(name, "start") == 0 || strcmp(name, "interrupt") == 0 || strcmp(name, "join") == 0 || strcmp(name, "stop") == 0 || strcmp(name, "suspend") == 0 || strcmp(name, "resume") == 0) { //must be deallocate jobject thd_ptr = NULL; jint hash_code = 0; gb_jvmti->GetLocalObject(thr, 0, 0, &thd_ptr); gb_jvmti->GetObjectHashCode(thd_ptr, &hash_code); printf("[線程監控]: thread (%10d) %10s (%10d)\n", thr_hash_code, name, hash_code); } } gb_jvmti->RawMonitorExit(gb_lock); } JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { // 初始化 jvm->GetEnv((void **) &gb_jvmti, JVMTI_VERSION_1_0); // 建立一個新的環境 memset(&gb_capa, 0, sizeof(jvmtiCapabilities)); gb_capa.can_signal_thread = 1; gb_capa.can_get_owned_monitor_info = 1; gb_capa.can_generate_method_exit_events = 1; gb_capa.can_generate_method_entry_events = 1; gb_capa.can_generate_exception_events = 1; gb_capa.can_generate_vm_object_alloc_events = 1; gb_capa.can_tag_objects = 1; gb_capa.can_generate_all_class_hook_events = 1; gb_capa.can_generate_native_method_bind_events = 1; gb_capa.can_access_local_variables = 1; gb_capa.can_get_monitor_info = 1; // 設置當前環境 gb_jvmti->AddCapabilities(&gb_capa); // 建立一個新的回調函數 jvmtiEventCallbacks callbacks; memset(&callbacks, 0, sizeof(jvmtiEventCallbacks)); // 方法回調 callbacks.MethodEntry = &callbackMethodEntry; // 設置回調函數 gb_jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks)); gb_jvmti->CreateRawMonitor("XFG", &gb_lock); // 註冊事件監聽(JVMTI_EVENT_VM_INIT、JVMTI_EVENT_EXCEPTION、JVMTI_EVENT_NATIVE_METHOD_BIND、JVMTI_EVENT_CLASS_FILE_LOAD_HOOK、JVMTI_EVENT_METHOD_ENTRY、JVMTI_EVENT_METHOD_EXIT) error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, (jthread) NULL); error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_EXCEPTION, (jthread) NULL); error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_NATIVE_METHOD_BIND, (jthread) NULL); error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, (jthread) NULL); error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, (jthread) NULL); error = gb_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_EXIT, (jthread) NULL); return JNI_OK; }
SetEventCallbacks(&callbacks, sizeof(callbacks));
以及相應事件的添加。