1、JVMTI提供哪些功能
了解JVMTI看这里:JVMTI是什么
编写JVMTI程序看这里:如何编写JVMTI agent程序
JVMTI 的功能非常丰富,包含了虚拟机中线程、内存堆/栈、类/方法/变量、事件/定时器处理、代码调试等多种功能,这里我们介绍一些常用的功能。
调试功能
调试功能是JVMTI的基本功能之一,这主要包括了设置断点、调试等,在JVMTI里面,设置断点的API本身很简单:
jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location)
jlocation这个数据结构在这里代表的是对应方法方法中一个可执行代码的行数。在断点发生的时候,虚拟机会触发一个事件,我们可以使用在上文中介绍过的方式对事件进行处理。
事件处理和回调函数
使用JVMTI一个基本的方式就是设置回调函数,在某些事件发生的时候触发并作出相应的动作。
因此这一部分的功能非常基本,回调事件包括虚拟机初始化/开始运行/结束、类的加载、方法出入、线程始末等等。如果想对某些事件进行处理,我们首为该事件写一个函数,然后在jvmtiEventCallbacks这个结构中指定相应的函数指针。
比如,我们对线程启动感兴趣,并写了一个HandleThreadStart函数,那么我们需要在Agent_OnLoad函数里加入:
jvmtiEventCallbacks eventCallBacks;
memset(&ecbs, 0, sizeof(ecbs)); // 初始化
eventCallBacks.ThreadStart = &HandleThreadStart; // 设置函数指针
jvmti->SetEventCallbacks(eventCallBacks, sizeof(eventCallBacks));
在虚拟机运行过程中,一旦有线程开始,虚拟机就会回调HandleThreadStart方法。
设置回调函数的时候,需要注意:虚拟机不保证回调函数同步,比如,好几个线程同时开始运行了,这个HandleThreadStart就会被同时调用几次.
内存控制和对象获取
内存控制是一切运行态的基本功能。 JVMTI除了提供最简单的内存申请和撤销之外(这块内存不受 Java 堆管理,需要自行进行清理工作,不然会造成内存泄漏),也提供了对Java堆的操作。众所周知,Java堆中存储了Java的对象,通过对堆的操作,可以很容易的查找任意的对象,还可以强行执行垃圾收集工作。
JVMTI中没有提供一个直接获取的方式,而是使用一个迭代器(iterater)的方式遍历,由此可见,虚拟机对对象的管理并非是哈希表,而是某种树/图方式:
jvmtiError FollowReferences(jvmtiEnv* env,
jint heap_filter,
jclass klass,
jobject initial_object,// 该方式可以指定根节点
const jvmtiHeapCallbacks* callbacks,// 设置回调函数
const void* user_data)
或者
jvmtiError IterateThroughHeap(jvmtiEnv* env,
jint heap_filter,
jclass klass,
const jvmtiHeapCallbacks* callbacks,
const void* user_data)// 遍历整个 heap
在遍历的过程中,我们可以设定一定的条件,例如指定某一个类的对象,并设置一个回调函数,如果条件被满足,回调函数就会被执行。还可以在回调函数中对当前传回的指针进行打标记(tag)操作,在遍历中,只能对满足条件的对象进行tag,然后再使用GetObjectsWithTags函数获取需要的对象。如下:
jvmtiError GetObjectsWithTags(jvmtiEnv* env,
jint tag_count,
const jlong* tags, // 设定特定的 tag,即我们上面所设置的
jint* count_ptr,
jobject** object_result_ptr,
jlong** tag_result_ptr)
如果你仅仅想对特定Java对象操作,应该避免设置其他类型的回调函数,否则会影响效率。多增加一个primitive的回调函数,可能会使整个操作效率下降一个数量级。
线程和锁
在JVMTI中也提供了很多API进行线程的操作,包括查询当前线程状态、暂停、恢复或者终端线程,还可以对线程锁进行操作。我们可以获得特定线程所拥有的锁:
jvmtiError GetOwnedMonitorInfo(jvmtiEnv* env,
jthread thread,
jint* owned_monitor_count_ptr,
jobject** owned_monitors_ptr)
也可以获得当前线程正在等待的锁:
jvmtiError GetCurrentContendedMonitor(jvmtiEnv* env,
jthread thread,
jobject* monitor_ptr)
我们可以通过以上接口设计自己的算法来判断是否死锁。
JVMTI还提供了一系列的监视器(Monitor)操作,来帮助我们在native环境中实现同步,主要的操作是构建监视器(CreateRawMonitor),获取监视器(RawMonitorEnter),释放监视器(RawMonitorExit),等待和唤醒监视器 (RawMonitorWait,RawMonitorNotify) 等操作,通过这些简单锁,程序的同步操作可以得到保证。