Java Agent(四)OpenJdk/JDK Attach部分源码分析

总的来说,JDK端的代码,目的就是:

  • 给JVM发送SIGBREAK信号,触发Attach Listener线程的创建;
  • 给JVM发送load命令,让JVM执行加载目标agent的操作;
  • 接收JVM返回的执行结果,判断是否加载agent成功;

流程图:
JVM+JDK源码流程
我们在外部程序里只需要两行编码:

VirtualMachine virtualMachine = VirtualMachine.attach(String.valueOf(pid));
virtualMachine.loadAgent("agent.jar=args");
  1. attach方法
public static VirtualMachine attach(String id)
    throws AttachNotSupportedException, IOException
{
   
   
    if (id == null) {
   
   
        throw new NullPointerException("id cannot be null");
    }
    List<AttachProvider> providers = AttachProvider.providers();
    if (providers.size() == 0) {
   
   
        throw new AttachNotSupportedException("no providers installed");
    }
    AttachNotSupportedException lastExc = null;
    for (AttachProvider provider: providers) {
   
   
        try {
   
   
            return provider.attachVirtualMachine(id);
        } catch (AttachNotSupportedException x) {
   
   
            lastExc = x;
        }
    }
    throw lastExc;
}

此方法通过AttachProvider.providers()得到所有的provider,并逐个调用它们的attachVirtualMachine方法,只要有一个成功就返回。
provider.attachVirtualMachine是个抽象方法,以Linux中的实现为例:

public VirtualMachine attachVirtualMachine(String vmid)
    throws AttachNotSupportedException, IOException
{
   
   
    checkAttachPermission();

    // AttachNotSupportedException will be thrown if the target VM can be determined
    // to be not attachable.
    testAttachable(vmid);

    return new LinuxVirtualMachine(this, vmid);
}

/**
 * Attaches to the target VM
 */
LinuxVirtualMachine(AttachProvider provider, String vmid)
    throws AttachNotSupportedException, IOException
{
   
   
    super(provider, vmid);

    // This provider only understands pids
    int pid;
    try {
   
   
        pid = Integer.parseInt(vmid);
    } catch (NumberFormatException x) {
   
   
        throw new 
<think>我们遇到了一个编译错误:程序包com.sun.tools.attach不存在。这个包属于JDK的tools.jar,通常用于动态附加到Java进程(如Java Agent场景)。以下是解决方案: ### 原因分析 1. **JDK版本问题**:`com.sun.tools.attach` 是Oracle JDKOpenJDK的一部分,但可能未包含在某些精简版JDK中 2. **构建配置缺失**:Maven/Gradle未正确引入`tools.jar`(该jar位于JDK安装目录下,非Maven中央仓库) 3. **模块化限制(Java 9+)**:在模块化项目中未声明对`jdk.attach`模块的依赖 ### 解决方案 #### 方案1:手动引入tools.jar(Java 8) ```xml <!-- Maven 配置 --> <project> ... <dependencies> <dependency> <groupId>com.sun</groupId> <artifactId>tools</artifactId> <version>1.8</version> <scope>system</scope> <systemPath>${java.home}/../lib/tools.jar</systemPath> </dependency> </dependencies> </project> ``` > **注意**:`${java.home}` 指向JRE目录,因此需要 `../lib` 回溯到JDK目录 #### 方案2:Java 9+ 模块化配置 在 `module-info.java` 中添加: ```java module your.module { requires jdk.attach; } ``` #### 方案3:Gradle 配置(Java 8) ```gradle dependencies { compileOnly files("${System.getProperty('java.home')}/../lib/tools.jar") } ``` ### 验证步骤 1. 确认JDK安装路径包含 `lib/tools.jar` 2. 检查IDE是否使用了正确的JDK(非JRE) 3. 对于Java 9+,运行 `java --list-modules` 确认存在 `jdk.attach` ### 替代方案(避免使用内部API) ```java // 使用标准API实现动态附加(需要Java 9+) import java.lang.management.ManagementFactory; import com.sun.tools.attach.VirtualMachine; // 替换为标准API public class SafeAttach { public static void attach(String pid, String agentPath) throws Exception { // 使用java.lang.instrument包的标准方法 } } ``` ### 常见错误处理 - **"javac: invalid flag: --release"**:确保使用JDK 9+编译 - **"package jdk.internal.jvmstat is not visible"**:添加 `opens` 指令到模块配置 - **NoClassDefFoundError**:运行时需确保 `tools.jar` 在类路径中 §§相关问题§§ 1. 如何在Maven项目中正确引入JDK内部的tools.jar依赖? 2. Java 9模块化系统中如何解决`jdk.attach`模块不可见的问题? 3. 使用`com.sun.tools.attach`有哪些替代方案可以避免依赖内部API? 4. 在Gradle构建中如何为不同操作系统配置tools.jar路径? 5. 为什么在Spring Boot应用中直接使用tools.jar会导致打包失败?</think>### 解决 "程序包com.sun.tools.attach不存在" 错误 这个错误通常发生在尝试使用 Java Attach API 时,主要原因是 `tools.jar` 未包含在构建路径中。以下是完整的解决方案: #### 1. **确认 JDK 安装位置** ```bash # 查看 JDK 安装路径 echo $JAVA_HOME # Linux/Mac echo %JAVA_HOME% # Windows ``` #### 2. **Maven 解决方案(推荐)** 在 `pom.xml` 中添加依赖: ```xml <dependency> <groupId>com.sun</groupId> <artifactId>tools</artifactId> <version>1.8.0</version> <scope>system</scope> <systemPath>${java.home}/../lib/tools.jar</systemPath> </dependency> ``` #### 3. **Gradle 解决方案** 在 `build.gradle` 中添加: ```gradle dependencies { implementation files("${System.getProperty('java.home')}/../lib/tools.jar") } ``` #### 4. **手动添加 JAR 路径** 编译时指定路径: ```bash # Linux/Mac javac -cp $JAVA_HOME/lib/tools.jar YourClass.java # Windows javac -cp "%JAVA_HOME%\lib\tools.jar" YourClass.java ``` #### 5. **模块化项目 (Java 9+) 配置** 在 `module-info.java` 中添加: ```java module your.module.name { requires jdk.attach; requires jdk.internal.jvmstat; // 可选,用于进程操作 } ``` ### 常见问题排查 1. **JDK 与 JRE 混淆**: - 确保使用 JDK 而非 JRE 进行编译 - 检查 IDE 配置:File > Project Structure > SDK Location 2. **路径验证**: ```java // 验证 tools.jar 是否存在 File toolsJar = new File(System.getProperty("java.home") + "/../lib/tools.jar"); System.out.println("Exists: " + toolsJar.exists()); ``` 3. **替代方案(避免依赖 tools.jar)**: ```java // 使用 Java 9+ 的 ProcessHandle API ProcessHandle.allProcesses() .filter(p -> p.info().command().isPresent()) .forEach(p -> System.out.println(p.pid() + " " + p.info().command().get())); ``` ### 注意事项 1. `tools.jar` 是 Oracle JDK/OpenJDK 专属,不适用于: - IBM J9 VM - Android SDK - 精简版 JDK(如 jlink 生成) 2. Java 16+ 需要添加 JVM 参数: ``` --add-opens=jdk.attach/sun.tools.attach=ALL-UNNAMED ``` 3. 生产环境建议使用更稳定的方案: ```java try { Class<?> vmClass = Class.forName("com.sun.tools.attach.VirtualMachine"); // ... } catch (ClassNotFoundException e) { // 回退方案 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值