制作网站软件哪个好网站系统分析报告

news/2025/10/2 11:57:54/文章来源:
制作网站软件哪个好,网站系统分析报告,上海好的高端网站建,怎样通过网址浏览自己做的网站1.字节码 1.1 什么是字节码#xff1f; Java之所以可以“一次编译#xff0c;到处运行”#xff0c;一是因为JVM针对各种操作系统、平台都进行了定制#xff0c;二是因为无论在什么平台#xff0c;都可以编译生成固定格式的字节码#xff08;.class文件#xff09;供JVM… 1.字节码 1.1 什么是字节码 Java之所以可以“一次编译到处运行”一是因为JVM针对各种操作系统、平台都进行了定制二是因为无论在什么平台都可以编译生成固定格式的字节码.class文件供JVM使用。因此也可以看出字节码对于Java生态的重要性。之所以被称之为字节码是因为字节码文件由十六进制值组成而JVM以两个十六进制值为一组即以字节为单位进行读取。在Java中一般是用javac命令编译源代码为字节码文件一个.java文件从编译到运行的示例如图1所示。 对于开发人员了解字节码可以更准确、直观地理解Java语言中更深层次的东西比如通过字节码可以很直观地看到Volatile关键字如何在字节码上生效。另外字节码增强技术在Spring AOP、各种ORM框架、热部署中的应用屡见不鲜深入理解其原理对于我们来说大有裨益。除此之外由于JVM规范的存在只要最终可以生成符合规范的字节码就可以在JVM上运行因此这就给了各种运行在JVM上的语言如Scala、Groovy、Kotlin一种契机可以扩展Java所没有的特性或者实现各种语法糖。理解字节码后再学习这些语言可以“逆流而上”从字节码视角看它的设计思路学习起来也“易如反掌”。 本文重点着眼于字节码增强技术从字节码开始逐层向上由JVM字节码操作集合到Java中操作字节码的框架再到我们熟悉的各类框架原理及应用也都会一一进行介绍。 1.2 字节码结构 .java文件通过javac编译后将得到一个.class文件比如编写一个简单的ByteCodeDemo类如下图2的左侧部分 编译后生成ByteCodeDemo.class文件打开后是一堆十六进制数按字节为单位进行分割后展示如图2右侧部分所示。上文提及过JVM对于字节码是有规范要求的那么看似杂乱的十六进制符合什么结构呢JVM规范要求每一个字节码文件都要由十部分按照固定的顺序组成整体结构如图3所示。接下来我们将一一介绍这十部分 1 魔数Magic Number 所有的.class文件的前四个字节都是魔数魔数的固定值为0xCAFEBABE。魔数放在文件开头JVM可以根据文件的开头来判断这个文件是否可能是一个.class文件如果是才会继续进行之后的操作。 有趣的是魔数的固定值是Java之父James Gosling制定的为CafeBabe咖啡宝贝而Java的图标为一杯咖啡。 2 版本号 版本号为魔数之后的4个字节前两个字节表示次版本号Minor Version后两个字节表示主版本号Major Version。上图2中版本号为“00 00 00 34”次版本号转化为十进制为0主版本号转化为十进制为52在Oracle官网中查询序号52对应的主版本号为1.8所以编译该文件的Java版本号为1.8.0。 3 常量池Constant Pool 紧接着主版本号之后的字节为常量池入口。常量池中存储两类常量字面量与符号引用。字面量为代码中声明为Final的常量值符号引用如类和接口的全局限定名、字段的名称和描述符、方法的名称和描述符。常量池整体上分为两部分常量池计数器以及常量池数据区如下图4所示。 常量池计数器constant_pool_count由于常量的数量不固定所以需要先放置两个字节来表示常量池容量计数值。图2中示例代码的字节码前10个字节如下图5所示将十六进制的24转化为十进制值为36排除掉下标“0”也就是说这个类文件中共有35个常量。 常量池数据区数据区是由constant_pool_count-1个cp_info结构组成一个cp_info结构对应一个常量。在字节码中共有14种类型的cp_info如下图6所示每种类型的结构都是固定的。 具体以CONSTANT_utf8_info为例它的结构如下图7左侧所示。首先一个字节“tag”它的值取自上图6中对应项的Tag由于它的类型是utf8_info所以值为“01”。接下来两个字节标识该字符串的长度Length然后Length个字节为这个字符串具体的值。从图2中的字节码摘取一个cp_info结构如下图7右侧所示。将它翻译过来后其含义为该常量类型为utf8字符串长度为一字节数据为“a”。 其他类型的cp_info结构在本文不再赘述整体结构大同小异都是先通过Tag来标识类型然后后续n个字节来描述长度和或数据。先知其所以然以后可以通过javap -verbose ByteCodeDemo命令查看JVM反编译后的完整常量池如下图8所示。可以看到反编译结果将每一个cp_info结构的类型和值都很明确地呈现了出来。 4 访问标志 常量池结束之后的两个字节描述该Class是类还是接口以及是否被Public、Abstract、Final等修饰符修饰。JVM规范规定了如下图9的访问标志Access_Flag。需要注意的是JVM并没有穷举所有的访问标志而是使用按位或操作来进行描述的比如某个类的修饰符为Public Final则对应的访问修饰符的值为ACC_PUBLIC | ACC_FINAL即0x0001 | 0x00100x0011。 5 当前类名 访问标志后的两个字节描述的是当前类的全限定名。这两个字节保存的值为常量池中的索引值根据索引值就能在常量池中找到这个类的全限定名。 6 父类名称 当前类名后的两个字节描述父类的全限定名同上保存的也是常量池中的索引值。 7 接口信息 父类名称后为两字节的接口计数器描述了该类或父类实现的接口数量。紧接着的n个字节是所有接口名称的字符串常量的索引值。 8 字段表 字段表用于描述类和接口中声明的变量包含类级别的变量以及实例变量但是不包含方法内部声明的局部变量。字段表也分为两部分第一部分为两个字节描述字段个数第二部分是每个字段的详细信息fields_info。字段表结构如下图所示 以图2中字节码的字段表为例如下图11所示。其中字段的访问标志查图90002对应为Private。通过索引下标在图8中常量池分别得到字段名为“a”描述符为“I”代表int。综上就可以唯一确定出一个类中声明的变量private int a。 9方法表 字段表结束后为方法表方法表也是由两部分组成第一部分为两个字节描述方法的个数第二部分为每个方法的详细信息。方法的详细信息较为复杂包括方法的访问标志、方法名、方法的描述符以及方法的属性如下图所示 方法的权限修饰符依然可以通过图9的值查询得到方法名和方法的描述符都是常量池中的索引值可以通过索引值在常量池中找到。而“方法的属性”这一部分较为复杂直接借助javap -verbose将其反编译为人可以读懂的信息进行解读如图13所示。可以看到属性中包括以下三个部分 “Code区”源代码对应的JVM指令操作码在进行字节码增强时重点操作的就是“Code区”这一部分。“LineNumberTable”行号表将Code区的操作码和源代码中的行号对应Debug时会起到作用源代码走一行需要走多少个JVM指令操作码。“LocalVariableTable”本地变量表包含This和局部变量之所以可以在每一个方法内部都可以调用This是因为JVM将This作为每一个方法的第一个参数隐式进行传入。当然这是针对非Static方法而言。 10附加属性表 字节码的最后一部分该项存放了在该文件中类或接口所定义属性的基本信息。 1.3 字节码操作集合 在上图13中Code区的红色编号017就是.java中的方法源代码编译后让JVM真正执行的操作码。为了帮助人们理解反编译后看到的是十六进制操作码所对应的助记符十六进制值操作码与助记符的对应关系以及每一个操作码的用处可以查看Oracle官方文档进行了解在需要用到时进行查阅即可。比如上图中第一个助记符为iconst_2对应到图2中的字节码为0x05用处是将int值2压入操作数栈中。以此类推对0~17的助记符理解后就是完整的add()方法的实现。 1.4 操作数栈和字节码 JVM的指令集是基于栈而不是寄存器基于栈可以具备很好的跨平台性因为寄存器指令集往往和硬件挂钩但缺点在于要完成同样的操作基于栈的实现需要更多指令才能完成因为栈只是一个FILO结构需要频繁压栈出栈。另外由于栈是在内存实现的而寄存器是在CPU的高速缓存区相较而言基于栈的速度要慢很多这也是为了跨平台性而做出的牺牲。 我们在上文所说的操作码或者操作集合其实控制的就是这个JVM的操作数栈。为了更直观地感受操作码是如何控制操作数栈的以及理解常量池、变量表的作用将add()方法的对操作数栈的操作制作为GIF如下图14所示图中仅截取了常量池中被引用的部分以指令iconst_2开始到ireturn结束与图13中Code区0~17的指令一一对应 1.5 查看字节码工具 如果每次查看反编译后的字节码都使用javap命令的话好非常繁琐。这里推荐一个Idea插件jclasslib。使用效果如图15所示代码编译后在菜单栏”View”中选择”Show Bytecode With jclasslib”可以很直观地看到当前字节码文件的类信息、常量池、方法区等信息。 2. 字节码增强 在上文中着重介绍了字节码的结构这为我们了解字节码增强技术的实现打下了基础。字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。接下来我们将从最直接操纵字节码的实现方式开始深入进行剖析。 2.1 ASM 对于需要手动操纵字节码的需求可以使用ASM它可以直接生产 .class字节码文件也可以在类被加载入JVM之前动态修改类行为如下图17所示。ASM的应用场景有AOPCglib就是基于ASM、热部署、修改其他jar包中的类等。当然涉及到如此底层的步骤实现起来也比较麻烦。接下来本文将介绍ASM的两种API并用ASM来实现一个比较粗糙的AOP。但在此之前为了让大家更快地理解ASM的处理流程强烈建议读者先对访问者模式进行了解。简单来说访问者模式主要用于修改或操作一些数据结构比较稳定的数据而通过第一章我们知道字节码文件的结构是由JVM固定的所以很适合利用访问者模式对字节码文件进行修改。 2.1.1 ASM API 2.1.1.1 核心API ASM Core API可以类比解析XML文件中的SAX方式不需要把这个类的整个结构读取进来就可以用流式的方法来处理字节码文件。好处是非常节约内存但是编程难度较大。然而出于性能考虑一般情况下编程都使用Core API。在Core API中有以下几个关键类 ClassReader用于读取已经编译好的.class文件。ClassWriter用于重新构建编译后的类如修改类名、属性以及方法也可以生成新的类的字节码文件。各种Visitor类如上所述CoreAPI根据字节码从上到下依次处理对于字节码文件中不同的区域有不同的Visitor比如用于访问方法的MethodVisitor、用于访问类变量的FieldVisitor、用于访问注解的AnnotationVisitor等。为了实现AOP重点要使用的是MethodVisitor。2.1.1.2 树形API ASM Tree API可以类比解析XML文件中的DOM方式把整个类的结构读取到内存中缺点是消耗内存多但是编程比较简单。TreeApi不同于CoreAPITreeAPI通过各种Node类来映射字节码的各个区域类比DOM节点就可以很好地理解这种编程方式。 2.1.2 直接利用ASM实现AOP 利用ASM的CoreAPI来增强类。这里不纠结于AOP的专业名词如切片、通知只实现在方法调用前、后增加逻辑通俗易懂且方便理解。首先定义需要被增强的Base类其中只包含一个process()方法方法内输出一行“process”。增强后我们期望的是方法执行前输出“start”之后输出”end”。 public class Base {public void process(){System.out.println(process);} }为了利用ASM实现AOP需要定义两个类一个是MyClassVisitor类用于对字节码的visit以及修改另一个是Generator类在这个类中定义ClassReader和ClassWriter其中的逻辑是classReader读取字节码然后交给MyClassVisitor类处理处理完成后由ClassWriter写字节码并将旧的字节码替换掉。Generator类较简单我们先看一下它的实现如下所示然后重点解释MyClassVisitor类。 import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter;public class Generator {public static void main(String[] args) throws Exception {//读取ClassReader classReader new ClassReader(meituan/bytecode/asm/Base);ClassWriter classWriter new ClassWriter(ClassWriter.COMPUTE_MAXS);//处理ClassVisitor classVisitor new MyClassVisitor(classWriter);classReader.accept(classVisitor, ClassReader.SKIP_DEBUG);byte[] data classWriter.toByteArray();//输出File f new File(operation-server/target/classes/meituan/bytecode/asm/Base.class);FileOutputStream fout new FileOutputStream(f);fout.write(data);fout.close();System.out.println(now generator cc success!!!!!);} }MyClassVisitor继承自ClassVisitor用于对字节码的观察。它还包含一个内部类MyMethodVisitor继承自MethodVisitor用于对类内方法的观察它的整体代码如下 import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes;public class MyClassVisitor extends ClassVisitor implements Opcodes {public MyClassVisitor(ClassVisitor cv) {super(ASM5, cv);}Overridepublic void visit(int version, int access, String name, String signature,String superName, String[] interfaces) {cv.visit(version, access, name, signature, superName, interfaces);}Overridepublic MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {MethodVisitor mv cv.visitMethod(access, name, desc, signature,exceptions);//Base类中有两个方法无参构造以及process方法这里不增强构造方法if (!name.equals(init) mv ! null) {mv new MyMethodVisitor(mv);}return mv;}class MyMethodVisitor extends MethodVisitor implements Opcodes {public MyMethodVisitor(MethodVisitor mv) {super(Opcodes.ASM5, mv);}Overridepublic void visitCode() {super.visitCode();mv.visitFieldInsn(GETSTATIC, java/lang/System, out, Ljava/io/PrintStream;);mv.visitLdcInsn(start);mv.visitMethodInsn(INVOKEVIRTUAL, java/io/PrintStream, println, (Ljava/lang/String;)V, false);}Overridepublic void visitInsn(int opcode) {if ((opcode Opcodes.IRETURN opcode Opcodes.RETURN)|| opcode Opcodes.ATHROW) {//方法在返回之前打印endmv.visitFieldInsn(GETSTATIC, java/lang/System, out, Ljava/io/PrintStream;);mv.visitLdcInsn(end);mv.visitMethodInsn(INVOKEVIRTUAL, java/io/PrintStream, println, (Ljava/lang/String;)V, false);}mv.visitInsn(opcode);}} }利用这个类就可以实现对字节码的修改。详细解读其中的代码对字节码做修改的步骤是 首先通过MyClassVisitor类中的visitMethod方法判断当前字节码读到哪一个方法了。跳过构造方法 init 后将需要被增强的方法交给内部类MyMethodVisitor来进行处理。接下来进入内部类MyMethodVisitor中的visitCode方法它会在ASM开始访问某一个方法的Code区时被调用重写visitCode方法将AOP中的前置逻辑就放在这里。MyMethodVisitor继续读取字节码指令每当ASM访问到无参数指令时都会调用MyMethodVisitor中的visitInsn方法。我们判断了当前指令是否为无参数的“return”指令如果是就在它的前面添加一些指令也就是将AOP的后置逻辑放在该方法中。综上重写MyMethodVisitor中的两个方法就可以实现AOP了而重写方法时就需要用ASM的写法手动写入或者修改字节码。通过调用methodVisitor的visitXXXXInsn()方法就可以实现字节码的插入XXXX对应相应的操作码助记符类型比如mv.visitLdcInsn(“end”)对应的操作码就是ldc “end”即将字符串“end”压入栈。完成这两个visitor类后运行Generator中的main方法完成对Base类的字节码增强增强后的结果可以在编译后的target文件夹中找到Base.class文件进行查看可以看到反编译后的代码已经改变了如图18左侧所示。然后写一个测试类MyTest在其中new Base()并调用base.process()方法可以看到下图右侧所示的AOP实现效果 2.1.3 ASM工具 利用ASM手写字节码时需要利用一系列visitXXXXInsn()方法来写对应的助记符所以需要先将每一行源代码转化为一个个的助记符然后通过ASM的语法转换为visitXXXXInsn()这种写法。第一步将源码转化为助记符就已经够麻烦了不熟悉字节码操作集合的话需要我们将代码编译后再反编译才能得到源代码对应的助记符。第二步利用ASM写字节码时如何传参也很令人头疼。ASM社区也知道这两个问题所以提供了工具ASM ByteCode Outline。 安装后右键选择“Show Bytecode Outline”在新标签页中选择“ASMified”这个tab如图19所示就可以看到这个类中的代码对应的ASM写法了。图中上下两个红框分别对应AOP中的前置逻辑于后置逻辑将这两块直接复制到visitor中的visitMethod()以及visitInsn()方法中就可以了。 2.2 Javassist ASM是在指令层次上操作字节码的阅读上文后我们的直观感受是在指令层次上操作字节码的框架实现起来比较晦涩。故除此之外我们再简单介绍另外一类框架强调源代码层次操作字节码的框架Javassist。 利用Javassist实现字节码增强时可以无须关注字节码刻板的结构其优点就在于编程简单。直接使用java编码的形式而不需要了解虚拟机指令就能动态改变类的结构或者动态生成类。其中最重要的是ClassPool、CtClass、CtMethod、CtField这四个类 CtClasscompile-time class编译时类信息它是一个class文件在代码中的抽象表现形式可以通过一个类的全限定名来获取一个CtClass对象用来表示这个类文件。ClassPool从开发视角来看ClassPool是一张保存CtClass信息的HashTablekey为类名value为类名对应的CtClass对象。当我们需要对某个类进行修改时就是通过pool.getCtClass(“className”)方法从pool中获取到相应的CtClass。CtMethod、CtField这两个比较好理解对应的是类中的方法和属性。了解这四个类后我们可以写一个小Demo来展示Javassist简单、快速的特点。我们依然是对Base中的process()方法做增强在方法调用前后分别输出”start”和”end”实现代码如下。我们需要做的就是从pool中获取到相应的CtClass对象和其中的方法然后执行method.insertBefore和insertAfter方法参数为要插入的Java代码再以字符串的形式传入即可实现起来也极为简单。 import com.meituan.mtrace.agent.javassist.*;public class JavassistTest {public static void main(String[] args) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException, IOException {ClassPool cp ClassPool.getDefault();CtClass cc cp.get(meituan.bytecode.javassist.Base);CtMethod m cc.getDeclaredMethod(process);m.insertBefore({ System.out.println(\start\); });m.insertAfter({ System.out.println(\end\); });Class c cc.toClass();cc.writeFile(/Users/zen/projects);Base h (Base)c.newInstance();h.process();} }3. 运行时类的重载 3.1 问题引出 上一章重点介绍了两种不同类型的字节码操作框架且都利用它们实现了较为粗糙的AOP。其实为了方便大家理解字节码增强技术在上文中我们避重就轻将ASM实现AOP的过程分为了两个main方法第一个是利用MyClassVisitor对已编译好的class文件进行修改第二个是new对象并调用。这期间并不涉及到JVM运行时对类的重加载而是在第一个main方法中通过ASM对已编译类的字节码进行替换在第二个main方法中直接使用已替换好的新类信息。另外在Javassist的实现中我们也只加载了一次Base类也不涉及到运行时重加载类。 如果我们在一个JVM中先加载了一个类然后又对其进行字节码增强并重新加载会发生什么呢模拟这种情况只需要我们在上文中Javassist的Demo中main()方法的第一行添加Base bnew Base()即在增强前就先让JVM加载Base类然后在执行到c.toClass()方法时会抛出错误如下图20所示。跟进c.toClass()方法中我们会发现它是在最后调用了ClassLoader的native方法defineClass()时报错。也就是说JVM是不允许在运行时动态重载一个类的。 显然如果只能在类加载前对类进行强化那字节码增强技术的使用场景就变得很窄了。我们期望的效果是在一个持续运行并已经加载了所有类的JVM中还能利用字节码增强技术对其中的类行为做替换并重新加载。为了模拟这种情况我们将Base类做改写在其中编写main方法每五秒调用一次process()方法在process()方法中输出一行“process”。 我们的目的就是在JVM运行中的时候将process()方法做替换在其前后分别打印“start”和“end”。也就是在运行中时每五秒打印的内容由”process”变为打印”start process end”。那如何解决JVM不允许运行时重加载类信息的问题呢为了达到这个目的我们接下来一一来介绍需要借助的Java类库。 import java.lang.management.ManagementFactory;public class Base {public static void main(String[] args) {String name ManagementFactory.getRuntimeMXBean().getName();String s name.split()[0];//打印当前PidSystem.out.println(pid:s);while (true) {try {Thread.sleep(5000L);} catch (Exception e) {break;}process();}}public static void process() {System.out.println(process);} }3.2 Instrument instrument是JVM提供的一个可以修改已加载类的类库专门为Java语言编写的插桩服务提供支持。它需要依赖JVMTI的Attach API机制实现JVMTI这一部分我们将在下一小节进行介绍。在JDK 1.6以前instrument只能在JVM刚启动开始加载类时生效而在JDK 1.6之后instrument支持了在运行时对类定义的修改。要使用instrument的类修改功能我们需要实现它提供的ClassFileTransformer接口定义一个类文件转换器。接口中的transform()方法会在类文件被加载时调用而在transform方法里我们可以利用上文中的ASM或Javassist对传入的字节码进行改写或替换生成新的字节码数组后返回。 我们定义一个实现了ClassFileTransformer接口的类TestTransformer依然在其中利用Javassist对Base类中的process()方法进行增强在前后分别打印“start”和“end”代码如下 import java.lang.instrument.ClassFileTransformer;public class TestTransformer implements ClassFileTransformer {Overridepublic byte[] transform(ClassLoader loader, String className, Class? classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {System.out.println(Transforming className);try {ClassPool cp ClassPool.getDefault();CtClass cc cp.get(meituan.bytecode.jvmti.Base);CtMethod m cc.getDeclaredMethod(process);m.insertBefore({ System.out.println(\start\); });m.insertAfter({ System.out.println(\end\); });return cc.toBytecode();} catch (Exception e) {e.printStackTrace();}return null;} }现在有了Transformer那么它要如何注入到正在运行的JVM呢还需要定义一个Agent借助Agent的能力将Instrument注入到JVM中。我们将在下一小节介绍Agent现在要介绍的是Agent中用到的另一个类Instrumentation。在JDK 1.6之后Instrumentation可以做启动后的Instrument、本地代码Native Code的Instrument以及动态改变Classpath等等。我们可以向Instrumentation中添加上文中定义的Transformer并指定要被重加载的类代码如下所示。这样当Agent被Attach到一个JVM中时就会执行类字节码替换并重载入JVM的操作。 import java.lang.instrument.Instrumentation;public class TestAgent {public static void agentmain(String args, Instrumentation inst) {//指定我们自己定义的Transformer在其中利用Javassist做字节码替换inst.addTransformer(new TestTransformer(), true);try {//重定义类并载入新的字节码inst.retransformClasses(Base.class);System.out.println(Agent Load Done.);} catch (Exception e) {System.out.println(agent load failed!);}} }3.3 JVMTI Agent Attach API 上一小节中我们给出了Agent类的代码追根溯源需要先介绍JPDAJava Platform Debugger Architecture。如果JVM启动时开启了JPDA那么类是允许被重新加载的。在这种情况下已被加载的旧版本类信息可以被卸载然后重新加载新版本的类。正如JDPA名称中的DebuggerJDPA其实是一套用于调试Java程序的标准任何JDK都必须实现该标准。 JPDA定义了一整套完整的体系它将调试体系分为三部分并规定了三者之间的通信接口。三部分由低到高分别是Java 虚拟机工具接口JVMTIJava 调试协议JDWP以及 Java 调试接口JDI三者之间的关系如下图所示 现在回到正题我们可以借助JVMTI的一部分能力帮助动态重载类信息。JVM TIJVM TOOL INTERFACEJVM工具接口是JVM提供的一套对JVM进行操作的工具接口。通过JVMTI可以实现对JVM的多种操作它通过接口注册各种事件勾子在JVM事件触发时同时触发预定义的勾子以实现对各个JVM事件的响应事件包括类文件加载、异常产生与捕获、线程启动和结束、进入和退出临界区、成员变量修改、GC开始和结束、方法调用进入和退出、临界区竞争与等待、VM启动与退出等等。 而Agent就是JVMTI的一种实现Agent有两种启动方式一是随Java进程启动而启动经常见到的java -agentlib就是这种方式二是运行时载入通过attach API将模块jar包动态地Attach到指定进程id的Java进程内。 Attach API 的作用是提供JVM进程间通信的能力比如说我们为了让另外一个JVM进程把线上服务的线程Dump出来会运行jstack或jmap的进程并传递pid的参数告诉它要对哪个进程进行线程Dump这就是Attach API做的事情。在下面我们将通过Attach API的loadAgent()方法将打包好的Agent jar包动态Attach到目标JVM上。具体实现起来的步骤如下 定义Agent并在其中实现AgentMain方法如上一小节中定义的代码块7中的TestAgent类然后将TestAgent类打成一个包含MANIFEST.MF的jar包其中MANIFEST.MF文件中将Agent-Class属性指定为TestAgent的全限定名如下图所示 最后利用Attach API将我们打包好的jar包Attach到指定的JVM pid上代码如下import com.sun.tools.attach.VirtualMachine;public class Attacher {public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException {// 传入目标 JVM pidVirtualMachine vm VirtualMachine.attach(39333);vm.loadAgent(/Users/zen/operation_server_jar/operation-server.jar);} }由于在MANIFEST.MF中指定了Agent-Class所以在Attach后目标JVM在运行时会走到TestAgent类中定义的agentmain()方法而在这个方法中我们利用Instrumentation将指定类的字节码通过定义的类转化器TestTransformer做了Base类的字节码替换通过javassist并完成了类的重新加载。由此我们达成了“在JVM运行时改变类的字节码并重新载入类信息”的目的。以下为运行时重新载入类的效果先运行Base中的main()方法启动一个JVM可以在控制台看到每隔五秒输出一次”process”。接着执行Attacher中的main()方法并将上一个JVM的pid传入。此时回到上一个main()方法的控制台可以看到现在每隔五秒输出”process”前后会分别输出”start”和”end”也就是说完成了运行时的字节码增强并重新载入了这个类。 3.4 使用场景 至此字节码增强技术的可使用范围就不再局限于JVM加载类前了。通过上述几个类库我们可以在运行时对JVM中的类进行修改并重载了。通过这种手段可以做的事情就变得很多了 热部署不部署服务而对线上服务做修改可以做打点、增加日志等操作。Mock测试时候对某些服务做Mock。性能诊断工具比如bTrace就是利用Instrument实现无侵入地跟踪一个正在运行的JVM监控到类和方法级别的状态信息。4. 总结 字节码增强技术相当于是一把打开运行时JVM的钥匙利用它可以动态地对运行中的程序做修改也可以跟踪JVM运行中程序的状态。此外我们平时使用的动态代理、AOP也与字节码增强密切相关它们实质上还是利用各种手段生成符合规范的字节码文件。综上所述掌握字节码增强后可以高效地定位并快速修复一些棘手的问题如线上性能问题、方法出现不可控的出入参需要紧急加日志等问题也可以在开发中减少冗余代码大大提高开发效率。 5. 参考文献 《ASM4-Guide》Oracle:The class File FormatOracle:The Java Virtual Machine Instruction Setjavassist tutorialJVM Tool Interface - Version 1.2作者简介 泽恩美团点评研发工程师。 招聘信息 美团到店住宿业务研发团队负责美团酒店核心业务系统建设致力于通过技术践行“帮大家住得更好”的使命。美团酒店屡次刷新行业记录最近12个月酒店预订间夜量达到3个亿单日入住间夜量峰值突破280万。团队的愿景是建设打造旅游住宿行业一流的技术架构从质量、安全、效率、性能多角度保障系统高速发展。 美团到店事业群住宿业务研发团队现诚聘后台开发工程师/技术专家欢迎有兴趣的同学投简历至techmeituan.com注明美团到店事业群住宿业务研发团队

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

珠海网站建设哪家好微商怎么做_和淘宝网站一样吗?

认识RBAC RBAC(基于角色的访问控制)是一种将权限分配给用户和服务的方法,基于他们的角色来确定他们可以访问和修改的资源。K8s使用RBAC作为来访请求鉴权的机制之一。 场景:访问K8s接口时的认证和鉴权 某些场景下,我…

深入解析:金融/财务图表的强大可视化引擎——Highcharts Stock

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

页面 HTTPS 化实战,从证书部署到真机验证的全流程(证书链、重定向、混合内容、抓包排查) - 实践

页面 HTTPS 化实战,从证书部署到真机验证的全流程(证书链、重定向、混合内容、抓包排查) - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block …

关于网站建设的调查报告重庆工程交易信息网

文章目录 String简单介绍常见命令应用场景 Hash简单介绍常见命令应用场景 List简单介绍常见命令应用场景 Set简单介绍常见命令应用场景 Sorted Set(Zset)简单介绍常见命令应用场景 Bitmap简单介绍常见命令应用场景 附录 Redis支持多种数据类型,比如String、hash、li…

路由器做网站80端口购物网站功能模块图

目录 步骤一:正确连接网线,插电开机正确连接网线: 认识系统灯: 插电开机: 步骤二:开机之后,系统的基本设置 1.进入设置界面: 2.设置辅助热点wifi: 3.设置日常…

网站建设哪里有wordpress的网站怎么保存

1.可放封闭原则 概念:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。即软件实体应该尽量在不修改原有代码的情况下进行扩展 2.里式替换原则 概念:所有引用父类的地方必须能透明地使用其子类的对象 3.依赖倒置原则 概念&#…

青岛网站建设公司外包photoshop免费素材库

一、数据安全的守护神:自动备份文件的重要性 在数字化时代,电脑中的文件承载着我们的工作成果、个人回忆以及众多重要信息。然而,数据丢失的风险无处不在,无论是硬件故障、软件崩溃,还是恶意软件的攻击,都…

AT_abc308_h [ABC308Ex] Make Q

还是比较牛的。 首先枚举一条边,钦定其中一个点,枚举这个点的出边作为 Q,然后再跑一个最小环就是结果了。 注意到此时是 \(O(n^4)\) 的,我们利用线段树分治解决 Floyd 中挖掉一个点求最短路的问题。 同样将枚举点换…

Coze源码分析-资源库-编辑插件-后端源码-核心技术与总结 - 教程

Coze源码分析-资源库-编辑插件-后端源码-核心技术与总结 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Co…

函数-高级用法+闭包

函数:增加代码可读性和重用性定义函数(3种形式): def funk() def funk(a,b) def funk(*args,**kwargs)调用函数(高级用法):1.函数做元素:2种形式 def funk1 ():print(888) def funk2():print(222) conten…

网站数据库怎么建立网站建设合同文百科

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 206. 反转链表 - 力扣(LeetCode) 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 思路一 运用迭代的算法,把头指针…

点云-标注-分类-航线规划软件 (一)点云自动分类 - 实践

点云-标注-分类-航线规划软件 (一)点云自动分类 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas…

JVM的内存分配策略有哪些?

JVM的内存分配策略主要围绕对象在堆内存中的分配规则展开,核心目标是高效利用内存并减少垃圾回收开销。主要分配策略如下: 1. 优先在Eden区分配 大多数对象在新生代的Eden区中创建。当Eden区没有足够空间时,JVM会触…

网站的功能有哪些公司文化建设的意义

双写机制 问题的出现 在发生数据库宕机时,可能Innodb正在写入某个页到表中,但是这个页只写了一部分,这种情况被称为部分写失效,虽然innodb会先写重做日志,在修改页,但是重做日志中记录的是对页的物理操作,但…

网站网站做代理怎么发展下线太原seo优化公司

论文链接:http://aihuang.org/p/papers/AAAI2018Denoising.pdf来源:AAAI 2018MotivationDistant Supervision 是一种常用的生成关系分类训练样本的方法,它通过将知识库与非结构化文本对齐来自动构建大量训练样本,减少模型对人工标…

网站给部分文字做遮挡代码义乌外贸网站建设

2020年android 仿微信朋友圈 评论1.如果有人问我:那些艰难的岁月你是怎么熬过来的?我想我只有一句话回答:我有一种强大的精神力量支撑着我,这种力量名字叫“想死又不敢”二十、我喜欢转身转得漂亮,放手放得潇洒你在玩以这样的挂念,会悲伤的逃脱11、我嫉…

如何建设英文网站免费建站网站

HYJY系列电压继电器 HYJY-30-01集成电路电压继电器 HYJY-30-01A HYJY-30-01B HYJY-30-02集成电路电压继电器 HYJY-30-02A HYJY-30-02B HYJY-30-03-3集成电路电压继电器 HYJY-30-03-2 HYJY-30-03-1 HYJY-30-02电压继电器(以下简称继电器)用于发…

在Linux系统上一键配置DoH,解决DNS解析被污染

前言 最近我的 swag 服务突然证书 renew 失败 诊断了一下发现原来是无法解析 acme-v02.api.letsencrypt.org 域名 换了几个 DNS 都不行,应该是 DNS 被污染或者劫持了 这时我才意识到不上 DoH/DoT 怕是没办法了🤣 本…

免费网站源码下载器网站开发报价文件

介绍 ai查询 在Java中,动态数组通常通过ArrayList类来实现,它是Java集合框架(Java Collections Framework)的一部分。ArrayList是一个可调整大小的数组实现,提供了比标准数组更多的灵活性和功能。 以下是使用ArrayLis…

大连模板网站制作哪家专业天津专业网站制作流程优势

目录 一.Python 线程队列 Queue 分类二.Python 线程优先队列 PriorityQueue 简介三.Python 线程优先队列 PriorityQueue 函数介绍四.Python 线程优先队列 PriorityQueue 使用五.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 在 线程队列…