崩溃了!我说用attach进行问题定位,面试官问我原理

Arthas(阿尔萨斯)是一款开源的Java诊断和监控工具,可以在生产环境中进行实时的应用程序分析和故障排查。Arthas的实现原理主要基于Java Instrumentation API和Java Agent技术。

Java Agent 是 Java 编程语言提供的一种特殊机制,允许你在程序运行过程中对字节码进行转换和增强。它是通过 Java 的 Instrumentation API 来实现的,可以用于在应用程序加载类时进行监测、修改和增强。Java Agent 通常被用于实现性能监测、代码分析、方法耗时统计、字节码增强等功能。

Java Agent 的主要特点

  1. 动态性: Java Agent 允许你在程序运行时加载代理类,对类进行转换和增强,从而实现动态修改已编译类的功能。

  2. 无侵入性: 使用 Java Agent 不需要修改源代码,也不需要重新编译。这使得你可以在不改变程序结构的情况下,实现一些横切关注点的逻辑。

  3. 全局性: Java Agent 可以在整个 JVM 中生效,对加载的所有类都可以进行转换和增强。这使得你可以监测、分析和增强全局的应用行为。

Java Agent 的使用步骤

开发 Java Agent 的涉及的要点如下图所示:

图片

image.png

要使用 Java Agent,通常需要遵循以下步骤:

  1. 创建代理类: 创建一个代理类,实现 java.lang.instrument.Instrumentation 接口的 premain 方法。

  2. 注册代理类: 在代理类的 premain 方法中注册代理类。这将使代理类在 JVM 启动时加载并执行。

  3. 定义转换规则: 在代理类中,你可以使用 Instrumentation API 来定义转换规则,即如何对类的字节码进行转换。

示例:方法计时器 Java Agent

以下是一个简单的 Java Agent 示例,实现对所有方法的计时统计:

import java.lang.instrument.Instrumentation;

public class MethodTimerAgent {

    public static void premain(String agentArgs, Instrumentation instrumentation) {
        System.out.println("Agent premain called");
        instrumentation.addTransformer(new MethodTimerTransformer());
    }
}

增加创建一个 ClassFileTransformer 接口的实现类 MethodTimerTransformer

public class MethodTimerTransformer implements ClassFileTransformer {  
    private final static String prefix = "\nlong startTime = System.currentTimeMillis();\n";  
    private final static String postfix = "\nlong endTime = System.currentTimeMillis();\n";  

    // 被处理的方法列表  
    final static Map<String, List<String>> methodMap = new HashMap<String, List<String>>();  

    public MethodTimerTransformer() {  
        add("com.example.TimeTest.sayHello");  
    }  

    private void add(String methodString) {  
        String className = methodString.substring(0, methodString.lastIndexOf("."));  
        String methodName = methodString.substring(methodString.lastIndexOf(".") + 1);  
        List<String> list = methodMap.get(className);  
        if (list == null) {  
            list = new ArrayList<String>();  
            methodMap.put(className, list);  
        }  
        list.add(methodName);  
    }  

    @Override  
    public byte[] transform(  
            ClassLoader loader,  
            String className,  
            Class<?> classBeingRedefined,  
            ProtectionDomain protectionDomain,  
            byte[] classfileBuffer) throws IllegalClassFormatException {  

        if (className.startsWith("com/example/")) {  
            className = className.replace("/", ".");  
            System.out.println("Transforming class: " + className);  

            // 在方法前后添加计时逻辑  
            CtClass ctclass = null;  

            try {  
                ctclass = ClassPool.getDefault().get(className);// 使用全称,用于取得字节码类<使用javassist>  
                System.out.println(ctclass.getName());  
                for (String methodName : methodMap.get(className)) {  
                    System.out.println(methodName);  
                    String outputStr = "\nSystem.out.println(\"this method " + methodName  
                    + " cost:\" +(endTime - startTime) +\"ms.\");";  

                    CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到这方法实例  
                    String newMethodName = methodName + "$old";// 新定义一个方法叫做比如sayHello$old  
                    ctmethod.setName(newMethodName);// 将原来的方法名字修改  

                    // 创建新的方法,复制原来的方法,名字为原来的名字  
                    CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null);  

                    // 构建新的方法体  
                    StringBuilder bodyStr = new StringBuilder();  
                    bodyStr.append("{");  
                    bodyStr.append(prefix);  
                    bodyStr.append(newMethodName + "($$);\n");// 调用原有代码,类似于method();($$)表示所有的参数  
                    bodyStr.append(postfix);  
                    bodyStr.append(outputStr);  
                    bodyStr.append("}");  

                    newMethod.setBody(bodyStr.toString());// 替换新方法  
                    ctclass.addMethod(newMethod);// 增加新方法  
                }  
                return ctclass.toBytecode();  
            } catch (Exception e) {  
                System.out.println(e.getMessage());  
                e.printStackTrace();  
            }  
        }  

        return classfileBuffer;  
    }  
}

在上述示例中,MethodTimerAgent 是 Java Agent 的代理类,通过 premain 方法注册了 MethodTimerTransformer 类。MethodTimerTransformer 是转换器类,实现了 ClassFileTransformer 接口,允许你在 transform 方法中修改目标类的字节码。

这里的对字节码的修改用到了javassistjavassist介绍可以参考字节码增强技术-Javassist

想要其他项目使用,我们还需完成以下几步:

创建文件resources/META-INF/MANIFEST.MF,内容如下:

Manifest-Version: 1.0  
Can-Redefine-Classes: true  
Can-Retransform-Classes: true  
Premain-Class: com.example.MethodTimerAgent  
Boot-Class-Path: javassist-3.28.0-GA.jar

pom文件增加下面配置

<build>  
    <plugins>  
        <plugin>  
            <groupId>org.apache.maven.plugins</groupId>  
            <artifactId>maven-jar-plugin</artifactId>  
            <version>2.4</version>  
            <configuration>  
                <archive>  
                <!-- 就是把前面的配置的MANIFEST.MF打入jar中,以指定premain的位置,否则会报:  
                Failed to find Premain-Class manifest attribute in  
                -->  
                <manifestFile>${maven.configuration.manifestFile}</manifestFile>  
                </archive>  
            </configuration>  
        </plugin>  
    </plugins>  
</build>

使用 Java Agent

我们新建一个项目,增加一个测试类:

public class TimeTest {  
    public static void main(String[] args) {  
        sayHello();  
    }  

    public static void sayHello() {  
        try {  
            Thread.sleep(2000);  
            System.out.println("hello world!!");  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  
}

要使用 Java Agent,你需要将代理类打包成 JAR 文件,并在启动 JVM 时使用 -javaagent 参数指定该 JAR 文件的路径。例如:

java -javaagent:path/to/agent.jar -jar your-application.jar

我们这里直接在idea上测试:

增加VM Options参数

图片


由于我们使用到了javassist,需要javassist的jar与java-agent的jar放在一起

图片

最后我们运行我们的测试类TimeTest,结果如下:

图片

 

可以看到方法执行耗时被打印出来了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/338494.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Qt】—— Qt的基本介绍

目录 &#xff08;一&#xff09;什么是Qt &#xff08;二&#xff09; Qt的发展史 &#xff08;三&#xff09;Qt⽀持的平台 &#xff08;四&#xff09; Qt版本 &#xff08;五&#xff09;Qt的优点 &#xff08;六&#xff09;Qt的应⽤场景 &#xff08;七&#xff09…

77.网游逆向分析与插件开发-背包的获取-物品类的C++还原

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;76.网游逆向分析与插件开发-背包的获取-背包地址的逆向分析-CSDN博客 码云地址&#xff08;ui显示角色数据 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本…

【2020】百度校招Java研发工程师笔试卷(第二批)算法题

贴一下我去年9月份写的博客 三道编程题&#xff0c;一道数学题&#xff0c;两道图论&#xff0c;哎嘿嘿&#xff0c;我就是不会做&#xff0c;哎嘿嘿&#xff0c;哭了。。。 一.最小值 牛牛给度度熊出了一个数学题&#xff0c;牛牛给定数字n,m,k&#xff0c;希望度度熊能找到…

文件操作与IO(2)

Java中通过java.io.File类来对一个文件(包括目录)进行抽象的描述.注意,有File对象,并不代表真实存在该文件. File概述 我们先来看看File类中的常见属性,构造方法和方法. 属性 修饰符及类型属性说明static StringpathSeparator依赖系统的路径分隔符,String类型的表示static …

VM使用教程--SDK取图 视频笔记

本笔记均由海康机器人官网的V学院视频中记录所得&#xff0c;属于省流大师了[doge] 图像采集 图像采集包括1图像源&#xff0c;2多图采集&#xff0c;3输出图像&#xff0c;4缓存图像&#xff0c;5光源 1图像源 图像源包括本地图像&#xff0c;相机采图&#xff0c;SDK 本…

MySQL主从集群

MySQL主从集群 主从模式、集群模式&#xff0c;都是在一个项目中使用多个mysql节点进行存储和读取数据。 当单机模式部署&#xff0c;不满足安全性、高可用、高并发等需求的时候&#xff0c;就需要考虑主从模式或者集群模式部署。 什么是主从模式&#xff1f; 主从模式&…

结构体大揭秘:代码中的时尚之选(上)

目录 结构结构的声明结构成员的类型结构体变量的定义和初始化结构体成员的访问结构体传参 结构 结构是一些值的集合&#xff0c;这些值被称为成员变量。之前说过数组是相同类型元素的集合。结构的每个成员可以是不同类型的变量&#xff0c;当然也可以是相同类型的。 我们在生活…

【数据库原理】(38)数据仓库

数据仓库&#xff08;Data Warehouse, DW&#xff09;是为了满足企业决策分析需求而设计的数据环境&#xff0c;它与传统数据库有明显的不同。 一.数据库仓库概述 定义: 数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合&#xff0c;用于支持企业管理和…

DAY06_SpringBoot—入门properties/YML文件lombok插件及使用

目录 1 SpringBoot1.1 SpringBoot介绍1.2 SpringBoot入门案例1.2.1 安装SpringBoot插件1.2.2 创建SpringBoot项目 1.3 关于SpringBoot项目说明1.3.1 关于POM.xml文件说明1.3.2 依赖配置项1.3.3 build标签 1.4 SpringBoot Maven操作1.4.1 项目打包1.4.2 java命令运行项目 1.5 关…

数据脱敏(二)脱敏算法-哈希脱敏

脱敏算法篇使用阿里云数据脱敏算法为模板,使用算子平台快速搭建流程来展示数据 哈希脱敏是一种数据安全处理技术&#xff0c;主要用于保护敏感信息。它将原始数据&#xff08;如密码、身份证号等&#xff09;通过哈希算法转换成固定长度的哈希值&#xff0c;即使哈希值被泄露&a…

【GitHub项目推荐--老照片变清晰】【转载】

先来看一个效果图&#xff0c;这个开源项目能把模糊爆浆的老照片 1 s 内变成清晰、高清的有色照片。 而以上这些效果&#xff0c;无需专业 PS 技能&#xff0c;只用一个网页端的 Demo、点点鼠标上传图片就能搞定。 这个修复神器&#xff0c;由腾讯 PCG ARC 实验室研发&#xf…

Java 面向对象 06 对象内存图(黑马)

之前设计的如下图&#xff1a; 方法区和内存在物理上是一块的&#xff0c;但是有不好的地方&#xff0c;所以变成了这种形式&#xff1a; 一个对象的内存图&#xff1a; 在创建对象时虚拟机至少做了以下七步&#xff1a; 解释&#xff1a; 第一步&#xff1a; 第二步&#x…

使用golang对接微软Azure AI翻译

文章目录 一、官方地址二、准备工作三、代码示例 一、官方地址 https://learn.microsoft.com/zh-CN/azure/ai-services/translator/translator-text-apis?tabsgo 二、准备工作 创建服务 创建服务连接地址&#xff1a;https://portal.azure.com/#create/Microsoft.CognitiveS…

如何本地部署虚拟数字克隆人 SadTalker

环境&#xff1a; Win10 SadTalker 问题描述&#xff1a; 如何本地部署虚拟数字克隆人 SadTalker 解决方案&#xff1a; SadTalker&#xff1a;学习逼真的3D运动系数&#xff0c;用于风格化的音频驱动的单图像说话人脸动画 单张人像图像&#x1f64e; ♂️音频&#x1f3…

数据结构:顺序循环队列

队列是限制在两端操作进行插入操作与删除操作的线性表&#xff0c;允许进行插入操作的一端称为"队尾"&#xff0c;允许进行删除操作的一端称为“队头”。当线性表中没有元素时&#xff0c;称为“空队”。队列的特点是先进先出。 队列两种规定&#xff1a; 1、front…

算法第二十一天-丑数

丑数 题目要求 解题思路 首先判断数字是不是为0或者负数&#xff0c;两者均不可能成为丑数&#xff1b; 之后对n进行不断整除&#xff0c;直到无法除尽为止。 简单判断最后的数是不是1即可。 代码 class Solution:def isUgly(self, n: int) -> bool:if n<0:return Fa…

1.redhat网卡配置

想要通过cmd ping通redhat 1.在redhat输入:ifconfig 将自己主机网络适配器VMware Network Adapter VMnet1的IPv4配置在同一网段,掩码是255.255.255.0,所以最后一位不同就可以 推荐用FileZilla远程上传文件

【手撕C语言 第六集】函数(上)

文章目录 一、函数是什么&#xff1f;二、C语言中函数的分类&#xff1a;1.库函数1.1 如何学会使用库函数&#xff1f; 2. 自定义函数 三、函数的参数1.实际参数&#xff08;实参&#xff09;&#xff1a;2.形式参数&#xff08;形参&#xff09;&#xff1a; 四、函数的调用&a…

Java研学-spring框架(一)

一 概述 1 介绍 Spring框架是一个开源的Java EE应用程序框架&#xff0c;旨在简化Java企业级应用的开发难度和开发周期&#xff0c;主要通过控制反转&#xff08;IoC&#xff09;和面向切面编程&#xff08;AOP&#xff09;等技术实现。   容器&#xff08;Container&#x…

会计六要素

目录 会计六要素(一&#xff09;资产(二&#xff09;负债(三&#xff09;所有者权益(四&#xff09;收入、费用和利润一、收入二、费用三、利润 \quad 会计六要素 我国《企业会计准则》将企业会计要素分为 资产 负债 所有者权益 收入 费用 利润 \quad (一&#xff09;资产 定…