1 jvmti 构成、介绍
JVMTI(JVM Tool Interface) 位于jpda 最底层, 是Java 虚拟机所提供的native编程接口。 JVMTI可以提供性能分析、debug、内存管理、线程分析等功能。
2 如何使用jvmti
JVMTI是一套本地编程接口,因此使用JVMTI,需要与c/c++ 以及JNI打交道。事实上,开发时一般采用建立一个Agent的方式来使用JVMTI,Agent使用jvmti函数,设置一些回调函数,并从Java虚拟机中得到当前的运行态信息,并作出自己的判断, 最后还可能操作虚拟机的运行态。把Agent编译成一个动态链接库之后,可以再Java程序启动时来加载它(比如IDE调试时使用的libjdwp.so 就是采用这种方式),当然也可以通过Attach方式,中途加入(比如jmap, jps,jstack 等等)。
3 Agent的加载、设置回调、卸载流程
具体可以参考我之前的文章 jvmti agent的加载与回调函数的执行分析
4 JVMTI的环境
使用 JVMTI 的过程,主要是设置 JVMTI 环境,监听虚拟机所产生的事件,以及在某些事件上加上我们所希望的回调函数。
可以通过操作 jvmtiCapabilities 来查询、增加、修改 JVMTI 的环境参数。
另外,虚拟机有自己的一些功能,一开始并未被启动,那么增加或修改 jvmtiCapabilities 也是可能的,但不同的虚拟机对这个功能的处理也不太一样,多数的虚拟机允许增改,但是有一定的限制,比如仅支持在 Agent_OnLoad 时,即虚拟机启动时作出,它某种程度上反映了虚拟机本身的构架。
5 jvmti 基本能力
JVMTI 的功能非常丰富,包含了虚拟机中线程、内存 / 堆 / 栈,类 / 方法 / 变量,事件 / 定时器处理等等 20 多类功能。比如: 事件处理和回调函数、内存控制和对象获取、线程和锁、调试功能。具体的使用方法可以见参考文章 JVMTI 和 Agent 实现
6 event 机制介绍
jvmti 的event管理后续研究一下,再补上。
7 一个agent demo
一个agent 会经历环境初始化、参数解析、注册功能、注册事件响应这几个步骤。运行过程如下:
agent时序图代码如下:
7.1 Main.cpp
#include <iostream>
#include "MethodTraceAgent.h"
#include "jvmti.h"
using namespace std;
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
{
cout << "Agent_OnLoad(" << vm << ")" << endl;
try{
MethodTraceAgent* agent = new MethodTraceAgent();
agent->Init(vm);
agent->ParseOptions(options);
agent->AddCapability();
agent->RegisterEvent();
} catch (AgentException& e) {
cout << "Error when enter HandleMethodEntry: " << e.what() << " [" << e.ErrCode() << "]";
return JNI_ERR;
}
return JNI_OK;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
cout << "Agent_OnUnload(" << vm << ")" << endl;
}
7.2 MethodTraceAgent.cpp
#include <iostream>
#include "MethodTraceAgent.h"
#include "jvmti.h"
using namespace std;
jvmtiEnv* MethodTraceAgent::m_jvmti = 0;
char* MethodTraceAgent::m_filter = 0;
MethodTraceAgent::~MethodTraceAgent() throw(AgentException)
{
// 必须释放内存,防止内存泄露
m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(m_filter));
}
void MethodTraceAgent::Init(JavaVM *vm) const throw(AgentException){
jvmtiEnv *jvmti = 0;
jint ret = (vm)->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_0);
if (ret != JNI_OK || jvmti == 0) {
throw AgentException(JVMTI_ERROR_INTERNAL);
}
m_jvmti = jvmti;
}
void MethodTraceAgent::ParseOptions(const char* str) const throw(AgentException)
{
if (str == 0)
return;
const size_t len = strlen(str);
if (len == 0)
return;
// 必须做好内存复制工作
jvmtiError error;
error = m_jvmti->Allocate(len + 1,reinterpret_cast<unsigned char**>(&m_filter));
CheckException(error);
strcpy(m_filter, str);
// 可以在这里进行参数解析的工作
// ...
}
void MethodTraceAgent::AddCapability() const throw(AgentException)
{
// 创建一个新的环境
jvmtiCapabilities caps;
memset(&caps, 0, sizeof(caps));
caps.can_generate_method_entry_events = 1;
// 设置当前环境
jvmtiError error = m_jvmti->AddCapabilities(&caps);
CheckException(error);
}
void MethodTraceAgent::RegisterEvent() const throw(AgentException)
{
// 创建一个新的回调函数
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.MethodEntry = &MethodTraceAgent::HandleMethodEntry;
// 设置回调函数
jvmtiError error;
error = m_jvmti->SetEventCallbacks(&callbacks, static_cast<jint>(sizeof(callbacks)));
CheckException(error);
// 开启事件监听
error = m_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_METHOD_ENTRY, 0);
CheckException(error);
}
void JNICALL MethodTraceAgent::HandleMethodEntry(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID method)
{
try {
jvmtiError error;
jclass clazz;
char* name;
char* signature;
// 获得方法对应的类
error = m_jvmti->GetMethodDeclaringClass(method, &clazz);
CheckException(error);
// 获得类的签名
error = m_jvmti->GetClassSignature(clazz, &signature, 0);
CheckException(error);
// 获得方法名字
error = m_jvmti->GetMethodName(method, &name, NULL, NULL);
CheckException(error);
// 根据参数过滤不必要的方法
if(m_filter != 0){
if (strcmp(m_filter, name) != 0)
return;
}
cout << signature<< " -> " << name << "(..)"<< endl;
// 必须释放内存,避免内存泄露
error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(name));
CheckException(error);
error = m_jvmti->Deallocate(reinterpret_cast<unsigned char*>(signature));
CheckException(error);
} catch (AgentException& e) {
cout << "Error when enter HandleMethodEntry: " << e.what() << " [" << e.ErrCode() << "]";
}
}
7.3 MethodTraceAgent.h
#include "jvmti.h"
class AgentException
{
public:
AgentException(jvmtiError err) {
m_error = err;
}
char* what() const throw() {
return "AgentException";
}
jvmtiError ErrCode() const throw() {
return m_error;
}
private:
jvmtiError m_error;
};
class MethodTraceAgent
{
public:
MethodTraceAgent() throw(AgentException){}
~MethodTraceAgent() throw(AgentException);
void Init(JavaVM *vm) const throw(AgentException);
void ParseOptions(const char* str) const throw(AgentException);
void AddCapability() const throw(AgentException);
void RegisterEvent() const throw(AgentException);
static void JNICALL HandleMethodEntry(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jmethodID method);
private:
static void CheckException(jvmtiError error) throw(AgentException)
{
// 可以根据错误类型扩展对应的异常,这里只做简单处理
if (error != JVMTI_ERROR_NONE) {
throw AgentException(error);
}
}
static jvmtiEnv * m_jvmti;
static char* m_filter;
};
7.4 MethodTraceTest.java
public class MethodTraceTest{
public static void main(String[] args){
MethodTraceTest test = new MethodTraceTest();
test.first();
test.second();
}
public void first(){
System.out.println("=> Call first()");
}
public void second(){
System.out.println("=> Call second()");
}
}
7.6 编译
g++ -w -I${JAVA_HOME}/include/ -I${JAVA_HOME}/include/linux
MethodTraceAgent.cpp Main.cpp -fPIC -shared -o libagent.so
7.7 默认运行java
javac MethodTraceTest.java
java MethodTraceTest
结果如下:
默认运行
7.8 加入我们的agent
java -agentpath:/home/xxx/libagent.so=first MethodTraceTest” 。 //其中的first是个参数,可以透传给jvmti。
运行结果如下:
加入agent之后的结果
网友评论