做者:小傅哥
博客: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));
以及相应事件的添加。