FantasticMao 技术笔记
BlogGitHub
  • README
  • C & Unix
    • C
      • 《C 程序设计语言》笔记
      • C 语言中的陷阱
      • CMake 示例
      • GNU make
      • LLVM Clang
      • Nginx 常用模块
      • Vim 常用命令
    • Unix-like
      • 《深入理解计算机系统》笔记
      • 《UNIX 环境高级编程》笔记 - UNIX 基础知识
      • 《UNIX 环境高级编程》笔记 - 文件 IO
      • 《UNIX 环境高级编程》笔记 - 标准 IO 库
      • 《鳥哥的 Linux 私房菜》笔记 - 目录配置
      • 《鳥哥的 Linux 私房菜》笔记 - 认识与学习 bash
      • 《鳥哥的 Linux 私房菜》笔记 - 任务管理
      • OpenWrt 中的陷阱
      • iptables 工作机制
  • Go
    • 《A Tour of Go》笔记
    • Go vs C vsJava
    • Go 常用命令
    • Go 语言中的陷阱
  • Java
    • JDK
      • 《Java 并发编程实战》笔记 - 线程池的使用
      • 设计模式概览
      • 集合概览
      • HashMap 内部算法
      • ThreadLocal 工作机制
      • Java Agent
    • JVM
      • 《深入理解 Java 虚拟机》笔记 - Java 内存模型与线程
      • JVM 运行时数据区
      • 类加载机制
      • 垃圾回收算法
      • 引用类型
      • 垃圾收集算法
      • 垃圾收集器
    • Spring
      • Spring IoC 容器扩展点
      • Spring Transaction 声明式事务管理
      • Spring Web MVC DispatcherServlet 工作机制
      • Spring Security Servlet 实现原理
    • 其它
      • 《Netty - One Framework to rule them all》演讲笔记
      • Hystrix 设计与实现
  • JavaScript
    • 《写给大家看的设计书》笔记 - 设计原则
    • 《JavaScript 权威指南》笔记 - jQuery 类库
  • 数据库
    • ElasticSearch
      • ElasticSearch 概览
    • HBase
      • HBase 数据模型
    • Prometheus
      • Prometheus 概览
      • Prometheus 数据模型和指标类型
      • Prometheus 查询语法
      • Prometheus 存储原理
      • Prometheus vs InfluxDB
    • Redis
      • 《Redis 设计与实现》笔记 - 简单动态字符串
      • 《Redis 设计与实现》笔记 - 链表
      • 《Redis 设计与实现》笔记 - 字典
      • 《Redis 设计与实现》笔记 - 跳跃表
      • 《Redis 设计与实现》笔记 - 整数集合
      • 《Redis 设计与实现》笔记 - 压缩列表
      • 《Redis 设计与实现》笔记 - 对象
      • Redis 内存回收策略
      • Redis 实现分布式锁
      • Redis 持久化机制
      • Redis 数据分片方案
      • 使用缓存的常见问题
    • MySQL
      • 《高性能 MySQL》笔记 - Schema 与数据类型优化
      • 《高性能 MySQL》笔记 - 创建高性能的索引
      • 《MySQL Reference Manual》笔记 - InnoDB 和 ACID 模型
      • 《MySQL Reference Manual》笔记 - InnoDB 多版本
      • 《MySQL Reference Manual》笔记 - InnoDB 锁
      • 《MySQL Reference Manual》笔记 - InnoDB 事务模型
      • B-Tree 简述
      • 理解查询执行计划
  • 中间件
    • gRPC
      • gRPC 负载均衡
    • ZooKeeper
      • ZooKeeper 数据模型
    • 消息队列
      • 消息积压解决策略
      • RocketMQ 架构设计
      • RocketMQ 功能特性
      • RocketMQ 消息存储
  • 分布式系统
    • 《凤凰架构》笔记
    • 系统设计思路
    • 系统优化思路
    • 分布式事务协议:二阶段提交和三阶段提交
    • 分布式系统的技术栈
    • 分布式系统的弹性设计
    • 单点登录解决方案
    • 容错,高可用和灾备
  • 数据结构和算法
    • 一致性哈希
    • 布隆过滤器
    • 散列表
  • 网络协议
    • 诊断工具
    • TCP 协议
      • TCP 报文结构
      • TCP 连接管理
由 GitBook 提供支持
在本页
  • 加载 Java Agent
  • 通过 Command-Line
  • 在 JVM 启动之后加载
  • 使用案例
  • 代码示例
  1. Java
  2. JDK

Java Agent

最后更新于1年前

java.lang.instrument 包提供了这样的一种能力:允许开发者使用 Java Agent 来探测 JVM 中正在运行的应用程序。其中,探测或修改应用程序需要通过修改程序的字节码来实现。

一个 Java Agent 程序是以 Jar 包的形式部署,Java Agent 程序打包生成的 Jar 包中的 META-INF/MANIFEST.MF 文件中的属性(Premain-Class 或 Agent-Class)指定了引导这个 Java Agent 程序的主类。Java Agent 程序可以通过添加新的 JVM 启动参数选项,来以 Command-Line 的方式加载和使用。除此之外,Java Agent 程序也支持在 JVM 启动之后再加载的方式,不过这种使用方式的具体实现是与 JVM 平台独立无关的。

加载 Java Agent

通过 Command-Line

在 Command-Line 使用方式中,一个 Java Agent 程序是通过在 JVM 的启动参数中添加 -javaagent:jarpath[=options] 选项来加载的。其中,jarpath 是 Java Agent 对应 Jar 包的路径,options 是启动 Java Agent 所需的参数。

在这种使用方式中,Java Agent 对应 Jar 包中的 META-INF/MANIFEST.MF 文件需要含有 Premain-Class 属性,Premain-Class 属性值是引导这个 Java Agent 程序启动的主类,并且这个类需要含有 public static void premain(String agentArgs, Instrumentation inst) 或者 public static void premain(String agentArgs) 签名的方法(与一般 Java 应用程序的主类需要含有 public static void main(String[] args) 签名的方法类似)。

Java Agent 程序中的主类会被应用程序的 ClassLoader#getSystemClassLoader() 类加载器所加载,这个类加载器通常也会加载应用程序的主类。如果 Java Agent 程序无法被 JVM 加载或者加载时抛出异常,那么 JVM 的启动流程将会中止。

在 JVM 启动之后加载

在 JVM 启动之后再加载 Java Agent 使用方式中,Java Agent 的初始化细节是基于应用特定实现的,但通常来说,这是发生在应用已经启动和加载主类之后的事情。

在这种使用方式中,Java Agent 对应 Jar 包中的 META-INF/MANIFEST.MF 文件需要含有 Agent-Class 属性,Agent-Class 属性值是引导这个 Java Agent 程序启动的主类,并且这个类需要含有 public static void agentmain(String agentArgs, Instrumentation inst) 或者 public static void agentmain(String agentArgs) 签名的方法。

Java Agent 程序中的主类会被应用程序的 ClassLoader#getSystemClassLoader() 类加载器所加载,并且要求这个类加载器支持将 Java Agent 对应 Jar 包添加到系统类路径的机制。如果 Java Agent 程序无法被 JVM 加载或者加载时抛出异常,JVM 则会忽略异常,并且不会中止退出。

原文链接:。

使用案例

Java Agent 可以被应用于很多场景,例如;

代码示例

Java Agent 的主类:

package com.demo;
import java.lang.instrument.Instrumentation;
public class MyAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        // 此处通常会操作字节码,修改应用程序逻辑,实现监控、热部署、AOP 等功能
        System.out.println("Hello My Java Agent");
    }
}

Java Agent 的 Maven 打包方式:

<groupId>com.demo</groupId>
<artifactId>MyAgent</artifactId>
<version>1.0</version>
......
<build>
    <plugins>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.2</version>
            <configuration>
                <archive>
                    <!-- see https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html -->
                    <manifestEntries>
                        <!-- The manifest of the agent JAR file must contain the attribute Premain-Class. -->
                        <Premain-Class>com.demo.MyAgent</Premain-Class>
                        <!-- Is the ability to redefine classes needed by this agent. -->
                        <Can-Redefine-Classes>false</Can-Redefine-Classes>
                        <!-- Is the ability to retransform classes needed by this agent. -->
                        <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        <!-- Is the ability to set native method prefix needed by this agent. -->
                        <Can-Set-Native-Method-Prefix>false</Can-Set-Native-Method-Prefix>
                    </manifestEntries>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

Java Agent 的 JVM 启动参数:

java -javaagent:.../MyAgent-1.0.jar -jar application.jar

使用 Java Agent 作为探测应用状态程序的启动类;

支持使用 Java Agent 无侵入地方式装饰 JDK 线程池实现类;

https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html
arthas
transmittable-thread-local