字节码增强常见问题系列(二)| 兼容性难题:如何让不同字节码增强框架和谐共存?

Sermant源码(二)如何无侵入接入配置中心 本章基于Sermant2.0.0,分析dynamic-config插件为springboot宿主应用无侵入接入配置中心。过程中涉及Sermant的两个核心通用能力(core):1. 阅读详情

往期回顾:

字节码增强常见问题系列(一)| 记一次多个JavaAgent同时使用的类增强冲突问题及分析

一、前言

当前市面上JavaAgent广泛被用于解决各种场景的问题,包括服务治理、链路追踪等。各大厂商和开源社区也都推出了自己的JavaAgent产品,例如Skywalking、阿里的JVM-Sandbox、Sermant等。用户在真实生产环境中可能会采用多个JavaAgent产品,不同的JavaAgent产品可能采用不同的字节码增强框架(ASM、Javassist、ByteBuddy、CGLIB),而在使用不同的字节码增强框架时,可能会出现各种冲突问题,这些冲突可能导致字节码增强失效、应用程序无法启动等问题。即使是使用相同的字节码增强框架也可能会出现冲突问题。对用户而言,在生产环境引入多个不同或者相同的字节码增强框架而不出现兼容性问题尤为重要。在上一期中,我们从字节码增强的底层逻辑角度分析了多个Java Agent加载冲突的问题。本期我们将重点介绍在使用多个字节码增强框架时可能遇到的兼容性问题,并结合我们的经验给出相应的解决方式以及合理的规避手段。

二、常见的字节码增强框架

字节码增强框架-ASM

ASM是基于访问者模式封装的字节码增强框架,可以生成二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。由于AMS是在虚拟机指令层面进行字节码操作,需要开发者熟悉JVM的各种指令集。

字节码增强框架-Javassist

Javassist是基于ASM进行二次开发的框架,在AMS的基础上屏蔽了JVM指令集的概念,提供了易于使用的高级 API,使开发者能够动态地创建、修改、分析Java类。但Javassist的增强逻辑采用的是硬编码形式,在开发过程中无法进行debug。

字节码增强框架-ByteBuddy

ByteBuddy 是一个简单而强大的字节码操作库,可以与 Java Agent 结合使用来实现动态修改和增强类的字节码。它提供了易于使用的 API,并支持在运行时生成代理对象、修改方法的行为、实现 AOP 和动态类创建等功能。

字节码增强框架-CGLIB 

CGLIB(Code Generation Library)是一个基于 ASM 的高性能字节码生成库,它用于在运行时动态生成子类来扩展现有类。CGLIB 可以对类进行拦截和代理,实现方法增强、AOP 功能等。

三、兼容性问题案例

在社区的场景案例中,遇到了以下由于使用不同字节码增强框架而导致的兼容性问题:

案例一:Javassist和ByteBuddy增强冲突

  • 背景描述:

用户在使用Sermant进行字节码增强的基础上再使用基于Javassist的JavaAgent产品进行增强时发现Sermant的功能都失效了。而切换两者的加载顺序之后发现功能都正常生效。

  • 原因分析:

通过对字节码增强框架的源码进行分析发现:ByteBuddy进行字节码增强时是基于JVM的Java Class元数据来进行的,如果在ByteBuddy之前已经加载了Javassist增强框架,则获取到的Class元数据是Javassist增强之后的,ByteBuddy会在原有增强的基础上再进行增强,进而保证两个增强效果都可以保留。

Javassist进行字节码增强时,是从ClassPool 里面去拿Class元信息,如果在Javassist之前已经加载了其他字节码增强框架,通过这种方式无法获取到增强之后的Class信息,Javassist在此基础上增强的话就会把之前增强的Class信息覆盖掉,导致前面的增强失效。

  • 解决方案:

由于Javassist无法在ByteBuddy增强的基础上进行增强,因此在同时使用Javassist和ByteBuddy时,一定要注意两者的加载顺序,需要将基于Javassist的JavaAgent产品在基于ByteBuddy的JavaAgent产品之前进行加载。

案例二:基于ByteBuddy的不同组件之间增强冲突

  • 背景描述:

用户在使用SkyWalking进行链路采集的基础上想通过Sermant实现微服务的治理功能(SkyWalking和Sermant都是基于ByteBuddy的),但在挂载SkyWalking的基础上再挂载Sermant之后发现,服务启动耗时大幅增加,而单独挂载Sermant服务启动耗时无明显增加。

  • 原因分析:

通过分析服务启动的CPU火焰图(见下图,紫色是Skywalking中ByteBuddy对Sermant进行增强前的类型扫描和字节码增强的CPU占用情况),发现SkyWalking使用的ByteBuddy对Sermant的类进行了字节码增强前的类型扫描,并进行了字节码增强,由于引入额外的JavaAgent组件,导致需要JVM加载的类增多,ByteBuddy在进行增强前的类型扫描时需要扫描的类也增多,最终导致服务启动耗时增加。并且在分析过程中我们也发现,ByteBuddy在进行扫描并且查找插桩点的过程中,基于模糊匹配(比如,通过类的父类,或者类所实现的接口来匹配)时,启动时的ByteBuddy所占用CPU时间片占用明显增多。

  • 解决方案:

方案一:ByteBuddy可以通过ignore方法取消对于某些类进行增强前的类型扫描。Ignore方法可以取消对于指定前缀的类的扫描。因此在使用多字节码框架时,可以通过配置需要取消扫描的类的前缀信息,减少进行增强前需要扫描的类,进而减少服务启动时间。

方案二:ByteBuddy也可以通过定义匹配器Ignored的方式取消对于某些类进行增强前的类型扫描。Ignored支持对于指定类加载器加载的类进行匹配。因此在使用多字节码框架时,可以通过配置需要取消扫描的类的类加载器信息,减少进行增强前需要扫描的类,进而减少服务启动时间。

方案三:

我们可以在使用ByteBuddy过程中,尽量减少使用模糊匹配来匹配我们想要增强的类,在程序规模较大时,通过精确匹配可以有效的降低字节码增强产品对启动耗时的影响。

下图为采用上述方案优化后的火焰图,可以明显看出,在启动过程中Skywalking中ByteBuddy所占用的CPU时间片减少:

另外,针对方案一和方案二,当前Sermant已支持通过配置的方式,来实施针对特定类的忽略,我们也在Skywalking社区提交了issue-11160,一起讨论该方案是否也在Skywalking中可行,来帮助社区用户更好的使用字节码增强的工具。

案例三:ByteBuddy和与CGLIB增强冲突

  • 背景描述:

用户在业务服务使用Spring的Cglib实现服务负载均衡的基础上,又想使用Sermant(基于ByteBuddy框架)进行流量管理,但是挂载Sermant之后出现了服务启动失败的问题,问题详细情况见下图:

  • 原因分析:

由于Spring使用CGLIB对LoadBalancerFeignClient进行增强时会创建代理类并修改方法参数和局部变量等字节码信息,因此当Sermant使用ByteBuddy对LoadBalancerFeignClient的代理类进行增强时,会出现字节码校验错误(由于该问题成因复杂,我们将在后续文章中对其进行深入分析) 。

  • 解决方案:

在CGLIB增强逻辑中,往往都是继承被增强类的方式创建代理类,所以我们只需要将插桩位置精确选择到需要增强的类即可,因为是继承关系,即使程序使用的是代理类的方法,最终也会触发我们在其父类同方法中织入的逻辑。

四、总结

当前市面上JavaAgent广泛被用于解决各种场景的问题,各大厂商和开源社区也针对字节码增强技术推出了自己的JavaAgent产品,用户在真实生产环境中可能会采用多个JavaAgent产品,结合上文中提到的案例,这里针对用户使用不同字节码增强框架时可能出现的兼容问题给出几条建议。

  • 合理安排字节码增强框架的加载顺序

在案例一中我们提到Javassist在ByteBuddy之后加载时,通过ByteBuddy增强的功能都失效了,而Javassist在ByteBuddy之前加载时两者功能都正常,因此在使用不同的字节码增强框架时,应该合理安排加载顺序,把兼容性更高的框架放在后面进行加载。

  • 减少字节码增强框架之间的相互影响

在进行类匹配时,可以通过忽略对于指定类(一些无需被增强的类或者其他字节码增强框架的类)的扫描来减少字节码增强框架的启动耗时,尤其在多字节码增强框架时,效果更加明显。与此同时还可以避免不同字节码增强框架之间相互增强可能导致的逻辑异常。

  • 遵守字节码增强的使用要求和限制

在进行字节码增强时应该按照官方接口的设计理念,增强类时不要新增、删除或者重命名字段和方法,也不要更改方法的签名,更不要更改类的继承关系。相关案例和原理分析可以参考:记一次多个JavaAgent同时使用的类增强冲突问题及分析

Sermant 作为专注于服务治理领域的字节码增强框架,致力于提供高性能、可扩展、易接入、功能丰富的服务治理体验,并会在每个版本中做好性能、功能、体验的看护,广泛欢迎大家的加入。

【字节码】Java Instrumentation 简介 以及 ASM 组合案例 JDK从1.5版本开始引人了java.lang.instrument 包,开发者可以更方便的实现字节码增强。其核心功能由提供,这个接口的方法提供了注册类文件转换器、获取所有已加载的类等功能,允许我们在对已加载和未加载的类进行修改,实现AOP、性能监控等功能。Instrumentation接口的常用方法如下所示。它的addTransformer方法给Instrumentation注册一个类型为ClassFileTransformer的类文件转换器。 阅读详情

相关推荐

STM32F103C8T6 Bootloader开发实战:串口IAP固件升级与Keil工程配置详解

本文详细介绍了STM32F103C8T6 Bootloader开发实战,重点讲解串口IAP固件升级的实现方法与Keil uVision5工程配置。通过Flash分区管理、USART1通信协议设计及应用程序跳转机制等核心技术,帮助开发者掌握嵌入式系统固件升级方案,提升设备维护效率。

weixin_29165725的博客 436

Skywalking流程分析_5(字节码增强)

Skywalking流程分析_5(字节码增强)

guntun8987的博客 659

双臂复合机器人眼在手外标定实战:从原理到D435标定流程详解

本文详细解析了双臂复合机器人中眼在手外标定的核心原理与实战流程。通过阐述坐标系转换与AX=XB方程,重点介绍了使用Intel RealSense D435相机进行标定的完整步骤,包括环境搭建、数据采集、计算与验证,并提供了关键的避坑指南,帮助工程师实现相机与机械臂基座间的精确坐标对齐。

weixin_29100927的博客 447

字节码增强技术ByteBuddy和探针技术-JavaAgent

如何动态修改运行中的字节码,有两个步骤,1:生成新的字节码2:替换旧的字节码 谁去修改字节码,就是字节码增强框架做的事。 修改后的字节码数据是怎么生效的,主要由Java Agent技术来实现。Asm,Javassist,ByteBuddy

qq_30246135的博客 2185

基于近场动力学的二维疲劳裂纹扩展模型_近场动力学数值模拟的程序实现(1)——引言...

参考书目W. H. Gerstle. 2015. Introduction to Practical Peridynamic: Computational Solid Mechanics Without Stress and Strain.E.Madenci, A.Barut, M.Dorduncu. Peridynamic Differential Operator for Numerical ...

weixin_39764603的博客 1202

【Autosar COM】Marvell 88Q5050 以太网交换机驱动技术解析

本驱动实现了对Marvell 88Q5050交换机的全面控制,通过模块化设计满足AUTOSAR规范要求。增加DMA加速机制提升大数据量处理性能实现TSN(时间敏感网络)特性支持引入异步通知机制替代轮询操作增强安全特性,如MACsec支持该驱动架构为车载以太网交换机控制提供了可靠基础,其设计思想可推广到其他车载网络控制器开发中。随着车载网络技术的演进,驱动需持续迭代以支持新特性与更高通信速率需求。

1450

Byte Buddy官方教程(三) — 字段和方法

Byte Buddy官方文档翻译,字段和方法

CaptHua的博客 6588

Modbus测试工具ModbusPoll与Modbus Slave使用方法 小白实操记录

Modbus 下面介绍modbus标准协议从站、主站仿真工具;可用于指令下发,数据采集等;是学习、现场运维的好工具。 1 介绍 Modbus Poll :Modbus主机仿真器,用于测试和调试Modbus从设备。该软件支持ModbusRTU、ASCII、TCP/IP。用来帮助开发人员测试Modbus从设备,或者其它Modbus协议的测试和仿真。它支持多文档接口,即,可以同时监视多个从设备/数据域。每个窗口简单地设定从设备ID,功能,地址,大小和轮询间隔。你可以从任意一个窗口读写寄存器和线圈。如果你想改

码灵的博客 1万+

多JavaAgent共存场景下的字节码增强冲突分析与最佳实践

本文深入分析了多JavaAgent共存场景下字节码增强冲突的根本原因,即后加载Agent对已加载类进行违反JVM规则的重转换操作。文章通过典型故障案例,揭示了Instrumentation API的契约边界,并提供了包括启动顺序调优、Agent兼容性设计、使用编排框架等在内的多维度最佳实践,帮助开发者实现JavaAgent的稳定共存与协作。

5f4d3s2a1q的博客 710

【NLP】第十章:位置编码Positional Encoding

前面讲RNN时一般都会有一个ht的输出,这个输出就是样本与样本之间的信息,而且RNN是时序循环的,所以RNN本身的结构,在物理上,就自带词的前后顺序信息的。比如词never,不管never本身的词向量是什么,只要你在序列中排第一个位置,你的位置向量就是[0,1,01],你排第三个位置,你的位置向量就是[0.9,-0,4,0.2,1.0]。也所以,你可以理解为:embedding后的词嵌入,PE按照每个样本在sequence中的位置,把embedding后的词嵌入给扭转后,才送入attention的。

因为热爱所以坚持 1917

1.嵌入式控制器EC学习,编译环境搭建

工欲善其事,必先利其器。 在学习EC相关知识之前,首先需要完成EC代码编译环境的搭建,需要如下内容: Keil C51(用于EC中C代码的编译器环境) EC源代码,我们使用从网上可以下载到的 ITE_V12.4_Update 版的代码为例进行学习 代码编辑器,这个可以根据自己的习惯选择,VS Code或者Source Insight等 Keil C51下载地址: http://www.downcc.com/soft/307131.html 下载完成后请进行破解 EC源代码 ...

qq_30135687的博客 3917

JavaAgent多框架共存实战:SkyWalking与自研Agent加载顺序的坑与解决方案

本文深入探讨了在微服务架构中,当SkyWalking与自研JavaAgent等多框架共存时,因加载顺序不当导致的类增强冲突问题。文章从故障案例出发,剖析了JavaAgent的工作原理与多个ClassFileTransformer的执行链,指出在retransform阶段修改类结构是冲突根源,并提供了调整加载顺序、优化Agent兼容性及运行时诊断等实战解决方案。

php55的博客 419

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

字节码增强禁忌手册:从SkyWalking改造事件看JavaAgent开发红线

本文通过分析SkyWalking改造事件引发的典型冲突,深入剖析了JavaAgent开发中`retransformClasses()`方法的六大禁忌行为,包括禁止改变类继承关系、增删字段与方法等核心限制。文章旨在帮助开发者理解字节码增强的底层红线,设计出在多Agent环境下更安全、兼容的监控或增强组件,避免因违规操作导致`UnsupportedOperationException`等运行时异常。

moon9的博客 449

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

为什么不应该期待完美兼容,但可以构建混合架构:Paper服务器的插件与mod共存之道

在Minecraft服务器管理的技术争议中,一个长期存在的迷思是"插件与mod不可兼得"。这个看似合理的论断源于两大技术体系的技术债务:Bukkit API的事件驱动模型与Forge/Fabric的字节码注入机制在架构层面的根本性冲突。然而,Paper项目通过一系列巧妙的架构补丁和性能优化,正在逐步打破这一技术壁垒。 ## 技术挑战:插件与mod的架构鸿沟 ### 事件总线与字节码注入的哲学对

gitblog_00179的博客 1018

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

TestableMock:Java单元测试Mock难题的终极解决方案

在Java单元测试领域,Mock技术是隔离依赖、聚焦被测逻辑的核心手段。传统基于动态代理的Mock框架(如Mockito)通过运行时拦截方法调用来实现模拟,但在面对静态方法、私有方法、final类等场景时往往力不从心,需要借助反射或PowerMock等重型工具,导致测试代码臃肿且侵入性强。TestableMock创新性地采用编译期字节码增强原理,在代码编译阶段直接修改方法调用指令,实现了对任意方法的无侵入Mock。这一技术突破极大提升了单元测试的灵活性和可维护性,尤其适用于遗留系统重构、工具类测试等复杂场景

weixin_33976072的博客 715

PHP 8.5兼容性难题全解析,资深工程师亲授平滑过渡方案

轻松应对PHP 8.5升级挑战,全面解析兼容性处理方案。涵盖常见框架适配、废弃函数替换与类型系统变更,结合实际项目场景提供平滑迁移路径,降低升级风险,提升系统稳定性,值得收藏。

FastDebug的博客 930

SkyWalking与自研JavaAgent共存时,为什么调整加载顺序就能解决问题?

本文深入分析了SkyWalking与自研JavaAgent共存时,因加载顺序导致功能异常的根源。核心在于Java Instrumentation机制中,类重转换(retransform)对修改类继承关系有严格限制。通过调整`-javaagent`参数的先后顺序,可以控制ClassFileTransformer的执行链,确保字节码增强在类初次加载时完成,从而规避兼容性问题。

weixin_29248653的博客 266

ModTheSpire技术解构:从入门到架构师的实践指南

在游戏模组开发领域,开发者常常面临修改原始游戏代码带来的兼容性风险和维护难题。ModTheSpire作为一款非侵入式框架,通过字节码增强和运行时注入技术,为《杀戮尖塔》模组开发提供了安全高效的解决方案。本文将深入剖析ModTheSpire的技术架构,从核心价值到实践应用,全面展示如何利用这一框架构建稳定、高效的游戏模组,帮助开发者在非侵入式框架的应用中实现从入门到精通的跨越。 ## 核心价值:重

gitblog_01113的博客 309

Agent就绪≠真正就绪!Spring Boot 4.0上线前必须验证的7项Agent兼容性指标,92%团队忽略第5项

Spring Boot 4.0上线前精准验证Agent兼容性,避免生产环境故障。基于Agent-Ready架构最佳实践,覆盖类加载、指标采集、线程上下文等7项关键指标,特别强调易被忽略的第5项(异步链路透传一致性)。适用于微服务与可观测性落地场景,值得收藏。

FuncTide的博客 349

Arthas热部署的隐形陷阱:为何你的retransform有时不生效?

本文深入探讨了Arthas热部署中retransform失效的常见原因,包括类加载器隔离、线程执行状态和字节码限制等关键因素。通过实际案例和解决方案,帮助开发者有效规避热部署陷阱,提升Java应用运维效率。特别针对类加载器哈希和多版本类共存问题提供了实用技巧。

gold8的专栏 180

推荐文章:探索ja-netfilter——强大灵活的Java Agent框架

推荐文章:探索ja-netfilter——强大灵活的Java Agent框架 项目介绍 ja-netfilter是一款高度可定制的Java Agent框架,版本号为2022.2.0。它为Java应用程序提供了深度网络过滤和插件化扩展的能力,让开发者能够在运行时对Java应用的网络交互进行精细控制和监控,而不必修改源代码。这款工具通过简单的配置文件和强大的插件系统,实现了对HTTP请求、DNS查询...

gitblog_00613的博客 1218

Spring Boot 4.0 Agent启动失败?3步精准诊断JVM Agent注入冲突、字节码增强异常与ClassLoader隔离问题

Spring Boot 4.0 Agent-Ready架构报错解决方法:精准定位JVM Agent注入冲突、字节码增强异常与ClassLoader隔离三大问题。适用于APM监控、链路追踪等Agent集成场景,提供日志分析、启动参数调优、类加载器适配三步诊断法,稳定高效,值得收藏。

CompiLume的博客 328

终极MinecraftForge访问转换器使用指南:10个修改游戏行为的核心技巧

MinecraftForge访问转换器是Minecraft模组开发中至关重要的工具,它能够修改游戏的基础行为,实现模组之间的兼容性。通过本文的完整指南,你将掌握如何利用这些转换器来改变游戏的核心机制。 ## 🎯 什么是MinecraftForge访问转换器? MinecraftForge访问转换器是专门设计用于在运行时修改Minecraft字节码的工具集。它们工作在游戏加载过程中,允许开发者

gitblog_00431的博客 921

解锁《杀戮尖塔》无限可能:ModTheSpire模组加载器完全指南

你是否曾梦想过为《杀戮尖塔》添加全新卡牌?是否希望调整游戏难度曲线以获得更具挑战性的体验?又或者想彻底改变游戏视觉风格,打造专属的地牢探险之旅?ModTheSpire作为一款强大的开源模组加载器,正是实现这些想法的钥匙。本文将带你深入探索模组加载的技术奥秘,从环境搭建到高级配置,一步步解锁游戏的无限可能。 ## 突破游戏边界:模组加载器的底层工作原理 ### 挑战:为何原生游戏难以扩展? 现代

gitblog_00075的博客 445
上一篇: 华为云第二期线下meetup·北理工站圆满落幕
下一篇: Sermant类隔离架构解析——解决JavaAgent场景类冲突的实践
华为云开源
博客等级 码龄4年 281粉丝 69原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值