jvmti介绍和开发

jvmti介绍和开发

本篇文章将介绍一下JVMTI的基本功能,并开发一个简单的JVMTI工具。

JVMTI是什么

JVMTI是JVM Tool Interface的缩写,即JVM的工具接口。 通过JVMTI可以用来实现profiling性能分析、debugging、监控、线程分析、覆盖率分析等工具。 接口提供的功能分为几大类,包括了class、线程、Heap内存的查询、操作等等。 这样可以在不改动代码的情况下监控、分析java进程的状态等。 javaagent也常用来实现类似的功能,不过javaagent对应的Instrumentation接口的功能相对有限,可以通过JVMTI获取更多底层功能。

开发一个简单的JVMTI工具

JVMTI有两种启动方式。 一种是在Java进程启动的时候通过-agentpath:<path-to-agent>=<options>的方式启动,path-to-agent是对应的jvmti接口实现的so动态库文件的绝对路径,后面可以追加jvmti程序需要的参数。 另一种方式是运行时attach,然后加载jvmti实现的动态库文件。 为了学习JVMTI,我们编写一个打印当前所有已经加载的类的工具,熟悉下整个开发使用流程。

创建一个C++项目

在Clion中,选择Create C++ executable,然后修改CMakeLists.txt 增加如下几行,来加入jvmti.h文件的依赖

include_directories(${JAVA_INCLUDE_PATH})
include_directories(${JAVA_INCLUDE_PATH2})

然后创建一个cpp文件,命名为jvmti_example.cpp,并在CMakeLists.txt中添加

SET(LIB_SRC jvmti_example.cpp)
ADD_LIBRARY(jvmti_example SHARED ${LIB_SRC})

编写jvmti_example.cpp,实现Agent_OnLoad、Agent_OnAttach、Agent_OnUnload。 OnLoad会在通过命令行参数方式启动时调用,OnAttach在attach到目标进程加载时调用,OnUnload在agent被卸载时调用。 我们在attach之后打印出当前所有已经加载的类的签名

#include <iostream>
#include "jvmti.h"

jint printLoadedClasses(JavaVM *vm);

JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
    std::cout << "Agent OnLoad" << std::endl;
    return 0;
}

jint JNICALL
Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
    std::cout << "Agent OnAttach" << std::endl;
    return printLoadedClasses(vm);
}

JNIEXPORT void JNICALL
Agent_OnUnload(JavaVM *vm) {
    std::cout << "Agent OnUnload" << std::endl;
}

JNIEXPORT jint printLoadedClasses(JavaVM *vm) {
    jvmtiEnv *jvmti;

    jint result = vm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_2);
    if (result != JNI_OK) {
        std::cout << "Unable to access jvm env" << std::endl;
        return result;
    }

    jclass *classes;
    jint count;
    result = jvmti->GetLoadedClasses(&count, &classes);
    if (result != JNI_OK) {
        std::cout << "JVMTI GetLoadedClasses failed" << std::endl;
        return result;
    }

    for (int i = 0; i < count; i++) {
        char *sig;
        char *genericSig;
        jvmti->GetClassSignature(classes[i], &sig, &genericSig);
        std::cout << "class signature = " << sig << std::endl;
    }

    return 0;
}


编译写好的cpp文件

mkdir build
cd build
cmake ..
make

然后可以在build文件夹内看到生成的动态库文件,例如在osx系统下是一个libjvmti_example.dylib的文件。

测试刚才创建出来的JVMTI程序

编写一个简单的Java代码, 并通过javac Test.java生成class文件

public class Test {
    public static void main(String[] args) throws Exception {
        while (true) {
            say();
            Thread.sleep(1000);
        }
    }

    public static void say() {
        System.out.println("Hello");
    }
}

命令行启动参数方式

通过java -agentpath:动态库绝对路径

例如

java -agentpath:/Users/liuzhengyang/CLionProjects/jvmti_examples/build/libjvmti_example.dylib Test

然后可以在标准输出中看到Agent OnLoad的输出。

动态attach方式

然后使用动态attach的方式加载jvmti程序,先编写一个Java版本的attach工具,其中attach方法的第一个参数是目标进程pid,第二个参数是动态库的绝对路径,第三个参数是agent的参数,当前这个例子不需要参数。

import com.sun.tools.attach.VirtualMachine;

/**
 * @author liuzhengyang
 * Make something people want.
 * 2020/4/19
 */
public class AgentAttacher {
    public static void main(String[] args) {
        attach(pid, "/Users/liuzhengyang/CLionProjects/jvmti_examples/build/libjvmti_example.dylib", "");
    }

    public static void attach(String pid, String agentPath, String agentArgs) {
        try {
            VirtualMachine virtualMachine = VirtualMachine.attach(pid);
            virtualMachine.loadAgentPath(agentPath, agentArgs);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

运行这个attach工具,可以在标准输出中看到Agent OnAttach和当前已经加载的类的结果

完整源码在: https://github.com/liuzhengyang/jvmti_examples

相关文章

Java运行环境配置

若要在计算机上运行Java程序,需要配置Java运行环境(JRE)或Java开发工具包(JDK)。以下是在Windows操作系统上配置Java运行环境的步骤:下载Java安装程序:前往Oracle官方...

「Java后端」开发环境搭建指南

1. Java1.1 Java安装及配置统一使用Oracle Java,版本为1.8,安装完成后配置环境变量JAVA_HOME及PATH。在命令行执行java -version应显示正确版本号。2....

JVM内存溢出常用排查命令

最近,有一个项目在不超过的12小时内,一定会内存溢出(java.lang.OutOfMemoryError:Java heap space)。由于当时比较忙,没有时间去具体分析,所以暂时只是加大了JV...

JVM常用指令

目录:一.引言二.基础故障处理工具2.1 概述2.2. jps:虚拟机进程状况工具2.3. jstat:虚拟机统计信息监视工具2.3. jinfo:java配置信息工具2.5. jmap:Java...

探秘Clojure:编程世界的“隐藏高手”

为什么你该认识 Clojure在编程语言的浩瀚星空中,Clojure 宛如一颗遗世独立的小众星辰,散发着独特而迷人的光芒。尽管它的知名度远不及 Java、Python 等主流语言,但在专业开发者的圈子...

CI&amp;CD落地实践9-Sonar Scanner使用配置&amp;SonarQube项目命令行接入

前言在前面一篇《代码质量扫描工具SonarQube原理及环境搭建》中,我们介绍了Sonarqube的架构组成、工作原理以及环境搭建相关操作。本篇将会重点介绍:Sonar Scanner的使用配置;利用...