zoukankan      html  css  js  c++  java
  • Java agent demo

    参考:https://www.jianshu.com/p/efbc60dc530d

    新建一个SpringBoot 工程,使其一直运行

    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) throws InterruptedException {
            SpringApplication.run(DemoApplication.class, args);
            for(;;){
                Thread.sleep(1000);
                System.out.println(System.currentTimeMillis());
            }
        }
    
    }
    

    新建一个maven工程,打包为一个jar作为agent

    public class PreMainTraceAgent {
        public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {
            System.out.println("agent begin agentArgs=" + agentArgs);
            Class[] allLoadedClasses = inst.getAllLoadedClasses();
            int i = 0;
            for (Class clazz : allLoadedClasses) {
                CodeSource cs = clazz.getProtectionDomain() == null ? null : clazz.getProtectionDomain().getCodeSource();
                String codeSource = "";
                if (null == cs || null == cs.getLocation() || null == cs.getLocation().getFile()) {
                    codeSource = "";
                } else {
                    codeSource = cs.getLocation().getFile();
                }
    
                if (i < 10) {
                    System.out.println(clazz.getName());
                    System.out.println(codeSource);
                    System.out.println(clazz.getClassLoader());
                }
                i++;
            }
        }
    }
    

    pom: 加入manifestEntries

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.example</groupId>
        <artifactId>demo-agent</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <configuration>
                        <descriptorRefs>
                            <descriptorRef>jar-with-dependencies</descriptorRef>
                        </descriptorRefs>
                        <archive>
                            <manifestEntries>
                                <Premain-Class>com.xh.arthas.agent.PreMainTraceAgent</Premain-Class>
                                <Agent-Class>com.xh.arthas.agent.PreMainTraceAgent</Agent-Class>
                                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                <Can-Retransform-Classes>true</Can-Retransform-Classes>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        <source>${java.version}</source>
                        <target>${java.version}</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    mvn打包:

    mvn clean assembly:assembly
    [INFO] --- maven-assembly-plugin:2.2-beta-5:assembly (default-cli) @ demo-agent ---
    [INFO] Building jar: F:codesarthasLearndemo-agent argetdemo-agent-1.0-SNAPSHOT-jar-with-dependencies.jar
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS

    新建maven一个工程,作为attach入口:

    public class Start {
        public static void main(String[] args) {
            System.out.println("running JVM start ");
            String jarUrl = "F:/codes/arthasLearn/demo-agent/target/demo-agent-1.0-SNAPSHOT-jar-with-dependencies.jar";
            List<VirtualMachineDescriptor> list = VirtualMachine.list();
            try {
                for (VirtualMachineDescriptor vmd : list) {
                    if (vmd.displayName().contains("demo-0.0.1-SNAPSHOT")) {
                        VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
                        virtualMachine.loadAgent(jarUrl,"**********************");
                        virtualMachine.detach();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    最后再SpringBoot工程中看到如下日志:

    agent begin agentArgs=**********************
    java.util.regex.Pattern$CharPredicate$$Lambda$394/0x0000000800e5d040
    
    null
    java.util.regex.CharPredicates$$Lambda$393/0x0000000800e5dc40
    
    null
    java.util.regex.Pattern$$Lambda$392/0x0000000800e5d840
    
    null
    java.util.regex.CharPredicates$$Lambda$391/0x0000000800e5e440
    
    

    premain 和 agentmain 的区别
    参考:https://zhuanlan.zhihu.com/p/96806300

    Instrument Agent两种加载方式
    在官方API文档[1]中提到,有两种获取Instrumentation接口实例的方法 :
    
    JVM在指定代理的方式下启动,此时Instrumentation实例会传递到代理类的premain方法。
    JVM提供一种在启动之后的某个时刻启动代理的机制,此时Instrumentation实例会传递到代理类代码的agentmain方法。
    premain对应的就是VM启动时的Instrument Agent加载,即agent on load,agentmain对应的是VM运行时的Instrument Agent加载,
    即agent on attach。两种加载形式所加载的Instrument Agent都关注同一个JVMTI事件 – ClassFileLoadHook事件,
    这个事件是在读取字节码文件之后回调时用,也就是说premain和agentmain方式的回调时机都是类文件字节码读取之后(或者说是类加载之后),
    之后对字节码进行重定义或重转换,不过修改的字节码也需要满足一些要求,在最后的局限性有说明。
    
    premain与agentmain的区别:
    
    premain和agentmain两种方式最终的目的都是为了回调Instrumentation实例并激活sun.instrument.InstrumentationImpl#transform()
    (InstrumentationImpl是Instrumentation的实现类)从而回调注册到Instrumentation中的ClassFileTransformer实现字节码修改,本质功能上没有很大区别。
    
    两者的非本质功能的区别如下:
    premain方式是JDK1.5引入的,agentmain方式是JDK1.6引入的,JDK1.6之后可以自行选择使用premain或者agentmain。
    premain需要通过命令行使用外部代理jar包,即-javaagent:代理jar包路径;agentmain则可以通过attach机制直接附着到目标VM中加载代理,
    也就是使用agentmain方式下,操作attach的程序和被代理的程序可以是完全不同的两个程序。
    premain方式回调到ClassFileTransformer中的类是虚拟机加载的所有类,这个是由于代理加载的顺序比较靠前决定的,在开发者逻辑看来就是:
    所有类首次加载并且进入程序main()方法之前,premain方法会被激活,然后所有被加载的类都会执行ClassFileTransformer列表中的回调。
    agentmain方式由于是采用attach机制,被代理的目标程序VM有可能很早之前已经启动,当然其所有类已经被加载完成,
    这个时候需要借助Instrumentation#retransformClasses(Class<?>... classes)让对应的类可以重新转换,从而激活重新转换的类执行ClassFileTransformer列表中的回调。
    通过premain方式的代理Jar包进行了更新的话,需要重启服务器,而agentmain方式的Jar包如果进行了更新的话,
    需要重新attach,但是agentmain重新attach还会导致重复的字节码插入问题,不过也有Hotswap和DCE VM方式来避免。
    
    
  • 相关阅读:
    趣谈多线程
    使用application manifest file
    参加一个.NET培训后的若干笔记
    遍历sql server数据库的简单方法
    为什么对继承的方法签名检查这么严格?
    Serialize Dictionary to xml using DataContractSerializer
    如何在sql中查询xml字符串
    从PowerDesigner概念设计模型(CDM)中的3种实体关系说起
    using "code coverage coloring" to help debuging
    日常工作中用到过的PowerShell指令
  • 原文地址:https://www.cnblogs.com/lanqie/p/14117108.html
Copyright © 2011-2022 走看看