深圳 三人 网站建设,上孩做网站,优化技术基础,网站开发需要那些技能【0】README0.1#xff09;本文部分文字描述转自 “深入理解jvm”#xff0c;旨在学习类文件结构 的基础知识#xff1b;0.2#xff09;本文荔枝以及荔枝的分析均为原创#xff1b;0.3#xff09;下面的截图中有附注t*编号#xff0c;不关乎博文内容#xff1b;0.4本文部分文字描述转自 “深入理解jvm”旨在学习类文件结构 的基础知识0.2本文荔枝以及荔枝的分析均为原创0.3下面的截图中有附注t*编号不关乎博文内容0.4鉴于大家对这篇文章这么热爱如觉得有必要的话请移步到 class字节码文件结构总结该篇文章是对本文的总结[update timestamp:1603290818]【1】类文件概述1各种不同平台的虚拟机与所有平台都统一使用存储格式——字节码他是构成平台无关性的基石2时至今日商业机构和开源机构已经在 java语言外发展出一大批在 jvm 上运行的语言如 Groovy JRuby JythonScala等3实现语言无关性的基础 是虚拟机和字节码存储格式4jvm 不和包括java在内的任何语言绑定它只与“Class” 文件这种特定的二进制文件格式进行关联Class 文件中包含了jvm 指令集和符号表以及若干其他辅助信息干货——意在说明jvm不是专门针对java 设计的而是针对 Class 文件格式设计的【2】Class类文件结构Attention任何一个Class 文件都对应着唯一一个类或接口的定义信息但反过来说类或接口都不一定定义在文件里如类或接口也可以通过类加载器直接生成1Class文件介绍 Class文件是一组 以 8位字节为基础单位的二进制流各个数据项目严格按照顺序紧凑地排列在 Class文件中中间没有添加任何分隔符这使得整个Class 文件中存储的内容几乎全部是程序运行的必要数据。 当遇到需要占用8位字节以上空间的数据项时则会按照高位在前的方式分割成若干个8位字节进行存储2根据jvm 规范的规定Class 文件格式采用一种类似于C 语言结构体的伪结构来存储数据这种伪结构中只有两种数据类型 无符号和表后面的解析都要以这两种数据类型为基础干货——Class 文件格式采用伪结构存储数据这种伪结构中只有两种数据类型 无符号和表2.1无符号类型 属于基本的数据类型以u1, u2, u4, u8 分别代表1、2、4个字节和8个字节的无符号数 无符号数可以用来描述数字索引引用数量值或者按照UTF-8编码构成字符串值2.2表 表是由多个无符号数或者其他表作为数据项构成的复合数据类型所有表都习惯性的以“_info”结尾。表用于描述有层次关系的复合结构的数据整个Class文件本质上就是一张表如下所示注意_info 后缀t1 【2.1】魔数与Clas文件的版本1魔数定义每个Class文件的头四个字节称为魔数 它的唯一作用是确定这个文件是否为一个能被虚拟机接收的Class 文件Class 文件的魔数值为 0xCAFEBABY-咖啡宝贝2魔数后面的是Class文件的版本号第5、6个字节是次版本号第7、8个字节是主版本号java的版本号是从45开始的 高版本的jdk 能向下兼容以前版本的Class文件 但不能运行以后版本的Class文件Attention现在的JDK为1.7可生成的Class文件主版本号最大值为51.0仅了解 3下表列出了从JDK1.1~1.7主流JDK 版本编译器输出的默认和可支持的Class文件版本号(t2)4看个荔枝(t4)【2.2】常量池Class文件的资源仓库1主版本号之后是常量池入口常量池可以理解为Class 文件中的资源仓库它是Class 文件结构中与其他项目关联最多的数据类型 也是占用Class文件空间最大的数据项目之一同时它还是在Class文件中第一个出现的表类型数据项目2常量池中主要有两大类常量 字面量和 符号引用字面量比较接近于 java 语言层面的常量概念如文本字符串声明为final 的常量值等。2.0常量池容量从1开始计数而不是0在Class文件格式规范制定时设计者将第0项常量空出来是有考虑的目的是 满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义这种情况就可以用索引值0来表示2.1看个荔枝(t4)(t3)3符号引用则属于编译原理方面的概念包括下面三类常量constantc1类和接口的全限定名c2字段的名称和描述符c3方法的名称和描述符 4java代码在进行javac的时候并没有C或C的连接步骤而是在虚拟机加载Class 文件的时候进行动态链接。当虚拟机运行时需要从常量池获取对应的符号引用再在类创建时或运行时解析翻译到具体的内存地址之中。5常量池中每一项都是一个表在JDK1.7之前有11种结构各部相同的表结构数据之后又添加了3种CONSTANT_MethodHandle_info, CONSTANT_MethodType_info 和 CONSTANT_InvokeDynamic_info5.1这14种表有一个共同特点表开始 的 第一位是一个 u1 类型的标志位tag代表当前常量的常量类型。这14种常量类型所代表的具体含义如下(t7) 5.2之所以说常量池是最繁琐的数据是因为这14种常量类型各自都有自己的结构。5.3看个荔枝以 CONSTANT_Class_info为例CONSTANT_Class_info的标志位是0x07此类型的常量代表一个类或接口的符号引用如下表所示(t8) 对上图的分析AnalysisA1tag是标志位它用于区分常量类型A2name_index是一索引值它指向常量池中一个 CONSTANT_Utf_info 类型常量此常量代表了这个类或者接口的全限定名这里的name_index0x0004也即是指向了常量池中的第二项常量A3还以及继续查找常量池常量偏移量0x0Dvalue0x01CONSTANT_Utf8_info 类型的常量。 5.4CONSTANT_Utf8_info类型结构见下表(t9) 对上图的分析Analysis A1length说明了这个UTF8 的字符串长度是多少字节 他后面紧跟着的长度是length 字节的连续数据是一个使用UTF8 缩略编码表示的字符串A2UTF8缩略编码和普通UTF8编码的区别是 从\u0001~\u007f之间的字符相当于1~127的ASCII码的缩略编码使用一个字节表示从\u0080~u07ff128~2023之间的所有字符的缩略编码用两个字节表示从\u0800~\uffff2048~65535之间的所有字符的缩略编码就按照普通UTF8 编码规则使用三个字节表示干货——UTF8缩略编码和普通UTF8编码的区别A3由于Class文件中方法字段等需要引用 CONSTANT_Utf8_info 型常量来描述名称所以 CONSTANT_Utf8_info型常量的最大长度是java中方法字段名的最大长度而这里的最大长度就是length的最大值即u2类型能表达的最大值65535。所以java 程序中如果定义了超过64KB 英文字符的变量或方法名将会无法编译 5.5javap一个专门用于分析Class 文件字节码的工具它可以把常量池中的常量都计算出来干货——javap 的作用 E:\bench-cluster\cloud-data-preprocess\jvm\srcjavap -verbose com.jvm.chapter6.TestClass
Classfile /E:/bench-cluster/cloud-data-preprocess/jvm/src/com/jvm/chapter6/TestClass.classLast modified 2016-3-26; size 292 bytesMD5 checksum 8c182f29b5f2790f2bf1155837ce89cdCompiled from TestClass.java
public class com.jvm.chapter6.TestClassminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 Methodref #4.#15 // java/lang/Object.init:()V#2 Fieldref #3.#16 // com/jvm/chapter6/TestClass.m:I#3 Class #17 // com/jvm/chapter6/TestClass#4 Class #18 // java/lang/Object#5 Utf8 m#6 Utf8 I#7 Utf8 init#8 Utf8 ()V#9 Utf8 Code#10 Utf8 LineNumberTable#11 Utf8 inc#12 Utf8 ()I#13 Utf8 SourceFile#14 Utf8 TestClass.java#15 NameAndType #7:#8 // init:()V#16 NameAndType #5:#6 // m:I#17 Utf8 com/jvm/chapter6/TestClass#18 Utf8 java/lang/Object
{public com.jvm.chapter6.TestClass();descriptor: ()Vflags: ACC_PUBLICCode:stack1, locals1, args_size10: aload_01: invokespecial #1 // Method java/lang/Object.init:()V4: returnLineNumberTable:line 3: 0public int inc();descriptor: ()Iflags: ACC_PUBLICCode:stack2, locals1, args_size10: aload_01: getfield #2 // Field m:I4: iconst_15: iadd6: ireturnLineNumberTable:line 7: 0
}
SourceFile: TestClass.java6总结14种常量项的结构定义如下表所示(t10,11)【2.3】 访问标志1在常量池结束后紧接着的两个字节代表访问标志这个标志用于识别一些类或接口层次的访问信息包括这个Class是类还是接口是否定义为 public是否定义为abstract类型如果是类的话是否被声明为final类型等具体的标志位如下表所示2访问标志一共有16个这里只列出了8个没有使用到的标志位为零(t12)Attention我使用的是jdk8的编译器进行编译而书作者使用的是jdk7所以对于访问标志的荔枝我这里不举了因为我发现算出来不对【2.4】类索引、父类索引与接口索引集合1Class 文件中由这三项数据来确定这个类的继承关系类索引this_class和父类索引super_class都是一个u2类型的数据而接口索引集合interfaces是一组 u2类型的数据的集合1.1类索引用于确定这个类的全限定名1.2父类索引用于确定这个类的父类的全限定名1.3接口索引集合 就是用来描述这个了实现了哪些接口这些被实现的接口将按 implements 语句若这个类本身就是接口则应该是 extends语句 后的接口顺序从左到右排列在 接口索引集合中 2类索引父类索引和接口索引集合都按照顺序排列在访问标志之后类索引和父类索引用两个u2 类型的索引值标识它们各自指向一个类型为 CONSTANT_Class_info的类描述符常量通过 CONSTANT_Class_info 类型的常量中的索引值可以找到定义在 CONSTANT_Utf8_info类型的常量中的全限定名字符串这个过程如下图所示(t13)2.1对于接口索引 入口的第一项——u2 类型的数据为接口计数器表示索引表的容量若该类没有实现任何接口则其值0后面接口的索引表不再占用任何字节(t14) 【2.5】 字段表集合1字段表用于描述接口或类中声明的变量2字段包括类级变量以及实例级变量但不包括在方法内的局部变量3在Java中 描述一个字段需要哪些信息包括的信息有 字段的作用域public, private, protected是实例变量还是类变量static修饰符可变性final修饰符并发可见性volatile修饰符是否强制从主内存读写是否可被序列化transient修饰符字段数据类型基本类型对象数组字段名称3.1以上这些信息中各个修饰符都是布尔值要么有某个修饰符要么没有很适合使用标志位来表示。而字段叫什么名字 字段被定义为什么数据类型这些都是无法固定的只能引用常量池中的常量来描述下表给出了字段表的最终格式(t15) 3.2字符修饰符放在 access_flags 项目中他与类中的access_flags 项目非常类似的都是一个 u2数据类型如下表所示(t16) 3.3跟随access_flag标志的是两项索引值 name_index 和 desc_index他们都是对常量池的引用分别代表着字段的简单名称以及字段和方法的描述符。 4全限定名简单名称描述符4.1全限定名 如com/jvm/chapter6/TestClass 是 com.jvm.chapter6.TestClass.java 这个类的全限定名仅仅是把类中的点替换为 “/” 而已一般在其后面加上“”以表全限定名结束4.2简单名称 是指没有类型和参数类型的方法或者字段名称这个类中的inc() 方法 和 m 字段的简单名称分别是 “inc” 和 “m”4.3描述符的作用是用来描述字段的数据类型方法的参数列表包括数量类型以及顺序和返回值。工具描述符规则基本数据类型byte, char, double, flaot, int ,long, short, boolena以及代表无返回值的void 类型都用一个大写字符来表示 而对象类型用字符L 加对象的全限定名来表示如下表所示t17 4.4对于数组类型每一维度将使用一个前置的“[”来描述如java.lang.String[][] 类型的二维数组将被记录为[[Ljava/lang/String一个整型数组int[] 将被记录为 [I4.5用描述符来描述方法时按照先参数列表后返回值的顺序描述参数列表按照参数的严格顺序放在一组小括号“”内4.6看个荔枝examples e1如方法 void int() 的描述符为 “()V” e2方法java.lang.String toString() 的描述符为 ()Ljava/lang/String e3方法int indexOf(char[]source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)的描述符为 (CII[CIII)I 5字段表集合的荔枝(t18)6字段表都包含的固定数据项目到 desc_index 为止就ending了。不过在desc_index 之后跟随着一个属性表集合用于存储一些额外的信息字段都可以在属性表中描述零个至多个的额外信息。对于本例中的字段m他的属性表计数器为零【2.6】方法表集合1方法表的结构如同字符表一样依次包括了 访问标志access_flags名称索引name_index描述符索引desc_index属性表集合attributes几项如下表所示t192方法访问标志(t20)3方法里面的代码到哪里去了 方法里面的代码经过编译器编译成字节码指令后存放在方法属性表集合中一个名为“Code”的属性里面属性表作为Class文件格式中最具扩展性的一种数据项目(t21)对上图的分析AnalysisA1代表方法集合有两个方法分别是 编译器添加的实例构造器init方法和源码中的方法inc方法A2与字段表集合相对应的如果父类方法在子类中没有被重写方法表集合中就不会出现来自父类的方法信息。但同样的有可能会出现由编译器自动添加的方法最典型的便是类构造器clinit方法和 实例构造器init 方法A3在java中要重载Override一个方法除了要与原方法具有相同的简单名称外还必须要求拥有与原方法不同的特征签名。特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合也就是因为返回值不会包含在特征签名中所以java 中是无法仅仅依靠返回值的不同来对一个已有方法进行重载的干货——为什么java 中是无法仅仅依靠返回值的不同来对一个已有方法进行重载 【2.7】 属性表集合1虚拟机规范预定义的属性t22,23,242对于每个属性它的名称需要从常量池中引用一个CONSTANT_Utf8_info 类型的常量来表示而属性值的结构则是完全自定义的只需要通过一个u4的长度属性去说明属性值所占用的位数即可属性表结构如下图所示t253虚拟机规范定义的属性3.1Code属性java程序方法体中的代码经过javac 编译后最终变为字节码指令存储在Code属性内。Code属性出现在方法表的属性集合中但并非所有的方法表都必须存在这个属性如接口或抽象类中的方法就不存在。Code属性表的结构如下(t26) 对上图的分析Analysis A1attribute_name_index是一项指向CONSTANT_Utf8_info 型常量的索引常量固定值为 “Code”它代表了该属性的属性名称A2attribute_length代表属性长度A3max_stack代表操作数栈深度的最大值A4max_locals代表了局部变量表所需要的存储空间Slot是虚拟机为局部变量分配内容所使用的最小单位对于byte, char, flaot, int, short, boolean and returnAddress 等长度不超过32位的数据类型每个局部变量占用1个Slot而double 和 long 这两种64位的数据类型则需要 两个Slot来存放方法参数显式异常处理器的参数方法体中定义的局部变量都需要使用局部变量表来存放还需要注意的是 并不是在方法中用到了多少个局部变量就把这些局部变量所占Slot之和作为max_locals的值原因是局部变量表中的Slot是可以重用的当代码执行超过了一个局部变量的作用域时这个局部变量所占的Slot 可以被其他局部变量使用javac编译器会根据变量的作用域来分配Slot给各个变量使用 然后计算出 max_locals的大小A5code_length 和 code用来存储java 源代码编译后生成的字节码指令。code_length代表字节码长度code用于存储 字节码指令的一系列字节流关于code_length需要注意的是虽然它是一个u4类型的长度值但虚拟机中明确限制了一个方法不允许超过65535条字节码指令即它实际上只使用了u2的长度如果超过这个限制 javac编译器会拒绝编译A6Code属性是Class文件中最重要的一个属性如果把一个java程序中的信息分为代码Code方法体里面的java代码和元数据metadata包括类字段方法定义以及其他信息两部分那么在整个Class文件中Code属性用于描述代码所有的其他数据项目都用于描述元数据 3.1.1看个例子总结以上属性(t27) 3.1.2再看个荔枝(t28) 对上图的分析Analysis A1大家注意到args_size1应该会有疑问 这个类有两个方法——实例构造器init和inc方法这两个方法都没有参数那么这个args_size1是如何而来的 而且无论是在参数列表里还是方法体内都没有定义任何局部变量那locals1 又是如何来的A2answer在任何实例方法里面都可以通过this 关键字访问到此方法所属的对象。这个访问机制非常重要而实现却很简单仅仅是通过javac 编译器编译的时候吧对this 关键字的访问转变为 对一个普通方法参数的访问然后在虚拟机调用实例方法时自动传入此参数而已因此在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量局部变量表中也会预留出第一个Slot 位来存放对象实例的引用方法参数值从1开始计算。这个处理只对实例方法有效如果代码清单中的inc方法为static那args_size 就不会等于1而是0了因为static关键字表明它是类变量this指针无法引用到 3.1.3异常表格式如下表所示(t29) 对上图的分析Analysis A1他包含4个字段这些字段的定义为 如果当字节码在第start_pc 行到 第 end_pc 行之间出现了类型为catch_type或者其子类的异常catch_type是一个指向一个 CONSTANT_Class_info类型常量的索引则转到 第 handler_pc 行继续处理。当catch_type0时代表任意异常情况都需要转向到 handler_pc 处进行处理 看个荔枝(t30) 对上图的分析Analysis这里有3条异常表记录 A1如果try 语句块中出现属于Exception或其子类异常则转到catch语句进行处理A2如果try 语句块中出现不属于Exception或其子类异常则转到finally语句进行处理A3如果catch语句块中出现了任何异常则转到finally语句块处理A4problem这段代码的返回值是多少 对以上字节码的分析Analysis A1字节码中0~9行所做的操作是将整数1赋值给变量m并将此时x的值复制一份副本到最后一个本地变量表的Slot中这个Slot里面的值在ireturn指令执行前将会被重新读入到操作栈顶作为方法返回值使用为了方便我们将本地变量表的最后一个Slot命名为returnValueA2字节码中10~16行如果没有出现异常的话继续10~16行将变量m 赋值为3然后将之前保存在returnValue中的整数1 读入到操作栈顶最后ireturn 指令会以int 形式返回操作栈顶的值方法结束。A3字节码中17~行如果出现了异常PC寄存器指针转到第17行第17~34行所做的事情就是将2 赋值给变量m然后将变量m 此时的值赋给 returnValue最后再将变量m 的值改为3.方法返回前同样将returnValue中保留的整数2读入到操作栈顶。从第35行开始的代码作用是变量m的值赋值为3并将栈顶的异常抛出方法结束注以上是3.1Code属性的全部内容有点多且杂不要搞混淆了我们继续 3.2Exception属性的内容 3.2Exception属性见上表6-13 1作用Exception属性的作用是列举出方法中可能抛出的受查异常也就是方法描述时在 throws 关键字后面列举的异常如下表所示(t31) 对上图的分析Analysis A1number_of_exceptions表示方法可能抛出 number_of_exceptions 种异常每一种异常使用一个 exceptoin_index_table 项表示 exceptoin_index_table是一个指向常量池中 CONSTANT_Class_info 型常量的索引代表了该异常类型 3.3LineNumberTable 属性 3.3.1作用用于描述java 源码行号与字节码行号的对应关系他并不是必须的但默认生成在javac时通过 -g:none 或 -g:lines 来取消或要求生成这个信息。如果选择不生成那么当抛出异常时堆栈中将不会显示错误的行号且在调试程序的时候也无法按照源码行来设置断点 3.3.2LineNumberTable属性的结构如下表所示(t32) 3.3.3对上图的分析line_number_table 是一个数量为 line_number_table_length类型为 line_number_info 的集合line_number_info 表包括了 start_pc 和 line_number 两个u2 类型的数据项前者是字节码行号后者是 java 源码行号(t33) 3.4LocalVariableTable 属性局部参数表属性 3.4.1作用用于描述栈帧中局部变量表中的变量与 java 源码中定义的变量之间的关系也不是必须的但默认会生成可以在javac的时候使用 -g:none 或 -g:vars 来取消或要求生成这项信息如果没有生成这项消息最大的影响是当其他人引用这个方法时所有的参数名称都将会丢失。 3.4.2其属性结构如下(t34) 3.4.3local_variable_info 项目代表了 一个栈帧与源码中的局部变量之间的关联 如下表所示(t35) 3.4.4对上图的分析Analysis A1start_pc 和 length 属性分别代表了 这个局部变量的生命周期开始的字节码偏移量及其作用范围长度两者结合起来就是这个局部变量在字节码中的作用域范围A2name_index and desc_index 都是指向常量池中 CONSTANT_Utf8_info 型常量的索引分别代表了局部变量的名称以及这个局部变量的描述符A3index是这个局部变量在栈帧局部变量表中Slot的位置。当这个变量是 64 位类型时它占用的Slot 为 index and index 1;A4LocalVariableTable属性它增加了一个姐妹属性——LocalVariableTypeTable这个新增的属性结构与LocalVariableTable 非常相似仅仅是把记录的字段描述符的desc_index 替换为字段的特征签名对于非泛型类型来说 描述符和特征签名能描述的信息是基本一致的但是泛型引入后由于描述符中泛型的参数化类型被擦除掉描述符就不能正确地描述泛型类型了所以就引入了LocalVariableTypeTable了 3.5SourceFile属性记录源文件名称(t36) 1这个属性是可选的默认生成使用 javac的-g:none 或 -g:source 来关闭或生成它如果不生成这项属性当抛出异常时堆栈中将不会显示出错代码所属的文件名 2其结构如下(t37) 对上图的分析Analysis A1sourcefile_index是指向常量池中CONSTANT_Utf8_info 型常量的索引常量值是 源码文件的文件名(t38) 3.6ConstantValue属性 1作用通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量才可以使用这项属性 2对于非static类型的变量实例变量对于它的赋值是在实例构造器init方法中进行的干货——实例构造器为init方法 3对于static类型的变量类变量有两种赋值方法 method1在类构造器clinit方法中干货——类构造器为clinit方法 method2使用 ConstantValue属性 4目前 Sun javac 编译器的选择是如果同时使用final 和 static 关键字来修饰一个变量且其数据类型是基本类型或 String的话就生成ConstantValue属性来进行初始化如果这个变量没有被final修饰或者并非是基本数据类型或String类型则将会选择在 clinit 方法中进行初始化 5对于ConstantValue属性的定义jvm 规范中并没有强制要求字段必须为final只是要求有ConstantVluae属性的字段必须设置为static而已对final关键字的要求是 javac编译器自己加入的限制而且对ConstantValue属性值只能限于基本类型和String因为Class文件格式的常量类型中只有与基本属性和字符串相对应的字面量所以ConstantValue属性不可能支持别的类型干货——对ConstantValue属性值只能限于基本类型和String 6其属性结构如下(t39) 对上图的分析Analysis A1ConstantValue属性是一个定长属性他的attribute_length数据项 必须为2 A2constantvalue_index数据项代表了常量池中一个字面量常量的引用根据字段类型的不同 字面量可以是 CONSTANT_Long_info, CONSTANT_Float_info,CONSTANT_Double_info,CONSTANT_Integer_info, CONSTANT_String_info常量中的一种 3.7InnerClasses属性 1作用用于记录内部类和宿主类间的关联 2其属性结构如下所示(t40) 对上图的分析Analysis 数据项 number_of_classes 代表需要记录多少个内部类信息每个内部类的信息都由一个 inner_classes_info 表进行描述。 其结构如下(t41) 对上图的分析Analysis A1inner_class_info_index and outer_class_info_index 都是指向常量池中 CONSTANT_Class_info 型常量的索引分别代表了内部类和宿主类的符号引用A2inner_name_index是指向常量池中 CONSTNAT_Utf8_info 型常量的索引代表这个内部类的名称如果是匿名内部类那么这项值为0A3inner_class_access_flags是内部类的访问标志他的取值范围如下所示(t42,43) t43 3.8Deprecated和Synthetic属性属于标志类型的布尔属性 1Deprecated属性用于表示某个类字段或方法已经被程序作用定为不在使用2Synthetic属性代表这个字段或方法并不是由java 源码直接产生的而是由编译器自行添加的3在jdk1.5后 标识一个类字段或方法是编译器自动产生的也可以设置它们访问标志中的ACC_SYNTHETIC 标志位4所有由非用户代码产生的类方法以及字段都应该至少设置Synthetic属性和 ACC_SYNTHETIC标志位中的一项唯一的例外就是 实例构造器init 方法和类构造器clinit 方法5两个属性的结构如下(t44) 3.9StackMapTable 属性 1作用 它位于Code属性的属性表中 该属性会在虚拟机类加载的字节码验证阶段被新类型检查验证器使用 目的在于代替以前比较消耗性能的基于数据流分析的类型推导验证器2StackMapTable属性包含零个至多个栈映射帧每个栈映射帧都显式或隐式地代表了一个字节码偏移量用于表示该执行到该字节码时局部变量表和操作数栈的验证类型。类型检查验证器会通过检查目标方法的局部变量和操作数栈所需要的类型来确定一段字节码指令是否符合逻辑约束仅供了解啦这里我也不是很懂不po 荔枝了 3.10Signature属性 1作用可以出现在类属性表和方法表结构的属性表中在jdk1.5 大幅增强了 java 的语法后 任何类接口初始化方法或成员的泛型 签名如果包含了类型变量或参数化类型则 Signature 属性会为它记录泛型签名信息。2引入它的原因因为java 的泛型采用的是擦除方法实现的伪泛型在字节码Code属性中 泛型信息编译之后都通通被擦除掉。擦除后的坏处就是 运行期无法将泛型类型与用于定义的普通类型同等对待例如运行期无法做反射时无法获得泛型信息。Signature属性就是为了弥补这个缺陷而增设的现在 java的反射API能够获得泛型类型最终的数据来源就是这个属性3Signature属性的结构如下(t45,46) 3.11BootstrapMethods 属性 1作用用于保存 invokedynamic 指令引用的引导方法限定符2这个真不懂省略掉 desc