本文介绍了Java Agent的基本概念和核心工作流程,包括启动时加载和运行时加载两种方式。通过实例演示了如何实现字节码转换器,并注册Instrumentation接口实例进行类字节码转换。文章还探讨了Agent的参数配置、重载方法等实用技巧,适合想要学习大模型和小白的程序员参考,建议收藏以便后续学习。

每天进步一点点,大家好,我是大龄码农。

下雪啦,四九就该有模有样。今天宅家看雪。

在 [Spring——AOP之AspectJ](https://mp.weixin.qq.com/s?__biz=MzIzODE2NTMwMQ==&mid=2247486332&idx=1&sn=4379c98b0c8fcd67493b9e0c3f67aefe&scene=21#wechat_redirect) 的实践中,用到了 javaagent 。那今天我们就聊聊Java Agent,在开始之前,大家需要具备[JVM](https://mp.weixin.qq.com/s?__biz=MzIzODE2NTMwMQ==&mid=2247485237&idx=1&sn=e2ec5403617de9e6be4feab4ab9d35ac&scene=21#wechat_redirect)的相关知识。

Java Agent是Java提供的一项强大功能,是基于Instrumentation的一种特殊机制,允许开发者在目标程序运行前或运行时操作其字节码,从而实现对程序行为的修改和增强功能,但无需修改目标程序的源代码。主打一个“无侵入式”。

核心工作流程

一、加载Agent

JVM识别并加载Agent Jar包,执行入口方法。有两种加载方式:启动时加载和运行时加载
  • 启动时加载

    这种模式的入口方法是premain,Agent在目标程序的main()方法执行前就完成初始化,适合在程序启动时就需要增强的场景。基本步骤如下:

    1、JVM识别Agent

    当使用 -javaagent:XXXAgent.jar=参数 启动程序时,JVM 会先解析 -javaagent 参数,识别出Agent Jar包的路径和参数。同时校验Agent Jar包的合法性:即是否在META-INF/MANIFEST.MF中声明了Premain-Class

    2、加载Agent类

    JVM使用系统类加载器(AppClassLoader)加载Premain-Class指定的Agent主类,然后调用premain()方法,它有两个重载方法:

    1)premain(String agentArgs, Instrumentation inst)

    2)premain(String agentArgs)

    参数说明:

    agentArgs:启动时指定的Agent参数

    inst:Instrumentation 接口实例

    JVM会优先调用带有Instrumentation参数的方法

    3、Agent注册ClassFileTransformer

    在premain()方法中,Agent会通过inst.addTransformer(transformer)注册ClassFileTransformer的实现类

    4、类加载时触发转换

    目标程序main()方法执行,premain()方法执行完毕后,JVM会调用目标程序的main()方法。当目标程序加载类时,JVM会先检查是否有注册的ClassFileTransformer。如果有,JVM将类的原始字节码传入转换器的 transform()方法,转换器根据规则修改字节码,返回修改后的字节码。JVM 使用修改后的字节码完成类的加载,后续程序运行时使用的就是增强后的类。

  • 运行时加载

    这种模式的入口方法是agentmain,允许在目标程序启动后,通过Attach API动态加载Agent,适合动态诊断。基本步骤如下:

    1、获取目标JVM进程的ID(PID)

    通过jps命令查看目标程序的PID

    2、通过Attach API连接目标JVM

    3、目标JVM加载Agent Jar包

    向目标JVM发送加载Agent的指令。目标JVM接收到指令后,解析Agent Jar包,并校验MANIFEST.MF中是否指定含agentmain()方法的类。全部通过后,目标JVM使用系统类加载器加载Agent-Class 指定的类。

    4、执行agentmain方法,注册转换器

    与premain()方法一样,agentmain()也有两个重载方法,目标JVM会优先调用带有Instrumentation参数的方法。同样,Agent通过inst.addTransformer(transformer)注册字节码转换器。

    5、对已加载类执行重转换(非必须)

    此时目标程序可能已加载了很多类,Agent需主动触发这些类的重转换。

重转换时,JVM会将类在加载时的原始字节码传入转换器,用新生成的字节码替换原有的类定义

二、获取Instrumentation实例

JVM只能通过premain()方法或者agentmain()方法,将Instrumentation接口实例传入,作为Agent与JVM的交互工具,开发者无法手动实例化。

Instrumentation是Java Agent技术的核心接口,它允许开发者在不修改目标程序源码的情况下,通过“插桩”的方式增强或者监控程序,主要的核心作用有:
  • 类字节码转换

    在类加载时或运行时修改类的字节码(比如:添加日志、性能统计)。

  • 类重定义与重转换

    动态替换已加载类的字节码,实现 “热更新” 或动态诊断。

  • JVM 信息获取

    获取已加载的类、类加载器、模块(JDK 9+)等信息,辅助监控与诊断。

三、注册字节码转换器

字节码转换器(ClassFileTransformer)通过定义字节码转换规则,在类加载或运行时对字节码进行修改。Agent通过Instrumentation注册字节码转换器

inst.addTransformer(new ClassFileTransformer的实现类);

四、字节码转换

字节码转换器的transform()方法是字节码转换的“执行入口”,JVM 在类加载或者重转换时自动调用该方法,传入原始字节码,通过返回修改后的字节码实现类的增强。

实践

新建一个maven工程,名为AgentTool

注:在今天的实践中,我们先熟悉整个流程,不做字节码修改的操作。

一、启动时加载

1、实现字节码转换器

图片

说明:返回原始字节码

2、编写Agent主类

图片

3、配置MANIFEST.MF

图片

说明1:文件放在resources目录下

说明2:文件中最后一行的空行是必须的

4、配置pom.xml

图片

说明:需要生成jar包

图片

图片

说明:这是一个关键配置,可以手动创建。为了方便,我们将其配置在pom文件中

5、打包

图片

说明:也可以使用mvn install的命令,效果一样

6、测试类

图片

说明:在其他工程中新建一个测试类并使用javac编译

7、测试过程

为了写命令方便,将生成的Agent的jar包,拷贝到其他工程的目录中

图片

说明1:注意命令行的写法,要指定对应的Agent的jar包

说明2:使用启动时加载的Agent,premain()方法确实在测试类的main()方法之前执行了

问题:为什么出现了这么多的“执行自定义的类字节码转换”,也就是说,为什么MyCustomTransformer被调用了多次?

下面我们修改一下MyCustomTransformer类

图片

再次运行上面的步骤,看下执行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明:字节码转换器应用到了所有的类,能否用到指定的类上,当然可以。但是由于事先并不知道用于哪个类,那要怎么做呢?这个留到下次再说

二、运行时加载

1、实现字节码转换器

图片 说明:复用加载时的字节码转换器

2、编写Agent主类

图片

3、配置MANIFEST.MF

图片

4、配置pom.xml

图片

5、打包

此处略过

6、测试类

图片

说明:因为需要连接到目标JVM,所以需要目标执行一段时间

7、测试过程

这里有两种测试方法:
  • 编写代码

    创建VirtualMachine实例,连接到目标PID,调用loadAgent(“/路径/XXXAgent.jar”, “参数”)

  • 使用jattach工具

    下载地址:https://github.com/jattach/jattach/releases

    这里使用jattach工具(有这么好用的工具,为啥要写代码呢?)

    命令格式:jattach PID load instrument false XXXAgent.jar

    注:关于jattach命令的更多说明,请参考官方文档

图片

说明:先运行目标程序

图片

说明:获取目标程序的PID,并运行jattach工具

图片

说明:成功加载

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明:目标程序结束

三、其他

为了方便,以下使用启动时加载的Agent验证

1、Agent的参数

图片

说明:打印一下参数信息

图片

说明1:从定义看,方法只支持一个参数,在命令行中识别第一个等号位置

说明2:如果想传入多个参数,那就使用特殊的字符拼接后,在后台处理吧

2、重载方法

图片

说明:增加只有一个入参的重载方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明:优先执行带有两个入参的重载方法

3、将启动时加载与运行时加载的配置合二为一

这个是可以的,毕竟加载的时机不同,大家可以验证一下

总结:

1、介绍了Java Agent的基本定义

2、介绍了Java Agent的核心工作流程

3、用程序讲解了今天的知识

小白/程序员如何系统学习大模型LLM?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

更多推荐