Learn && Live
虚度年华浮萍于世,勤学善思至死不渝
前言
Hey,欢迎阅读Connor学JVM系列,这个系列记录了我的JVM基础知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/YszaF,话不多说我们马上开始!
1.Java语言无关性(跨语言的平台和跨平台的语言)
2.类文件结构
1.定义
(1)Class文件是一组以字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列,中间没有任何分隔符
(2)Class文件结构采用一种类似于C语言结构体的伪结构,只有两种数据类型
- 无符号数,基本的数据类型,以u1、u2等分别代表1个字符、2个字符的无符号数,可用来描述数组、索引引用、数值量或UTF-8编码的字符串值
- 表,由多个无符号数或其他表作为数据项组合成的复合数据类型,整个Class文件本质上也可以看成一张表
2.Class文件结构
magic
(1)魔数,u4
(2)它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件,值为0xCAFEBABE。
(3)此外如GIF等图片格式的文件头中也有魔数
(4)使用魔数而不是扩展名来进行识别主要是基于安全考虑,因为文件扩展名可以随意改动
minor_version
(1)次版本号,u2
(2)JDK1.2之前用于指定该JDK版本支持的Class文件版本号的下限,如JDK1.1支持版本不低于45.0
(3)JDK1.2至JDK12之前次版本号均为使用,固定为0
(4)JDK12次版本号用于标识“技术预览版”,如果Class文件中使用了该版本尚未列入正式特性的预览功能,则将次版本号指定为65535
major_version
(1)主版本号,u2,
(2)用于指定该JDK版本支持的Class文件版本号的上限,如JDK1.1支持版本不高于45.65535
(3)高版本的JDK能向下兼容以前版本的Class文件,但不能运行以后版本的Class文件,虚拟机会拒绝执行超过其版本号的Class文件
constants_pool_count
(1)常量池容量计数值,u2
(2)这个计数值从1而不是从0开始,这样做可以用0来表示不引用任何一个常量池项目的索引值
constrants_pool
(1)常量池,不定长,长度与池中常量的个数和长度有关
(2)主要存放两大类常量:字面量、符号引用。字面量包括文本字符串、final常量等,符号引用可以包含下面几类
- 包
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
- 方法句柄和方法类型
- 动态调用点和动态常量
(3)每一项常量都是一个表,表的结构根据类型的不同而各不相同
access_flags
(1)访问标志,u2
(2)用于识别一些类或接口层次的访问信息或识别是否为注解、枚举、模块,如:
- 类 or 接口
- 是否是public
- 是否是abstract
- 是否是final(类)
this_class
(1)类索引,u2
(2)用于确定这个类的全限定类名
super_class
(1)父类索引,u2
(2)用于确定这个类的父类的全限定类名,除Object类外,所有Java类的父类索引只能为1
interfaces
(1)接口索引集合,不定长
(2)按implements(若这个Class文件表示一个接口,则为extends)后的接口顺序从左到右排列在接口索引集合中
fields_count
(1)接口或类中声明的变量的个数,u2
(2)包括成员变量(类变量 + 实例变量),不包含局部变量
fields
(1)字段集合,不定长
(2)用于描述类或接口中定义的成员变量的修饰符(权限、static、final、volatile、transient等)、类型、名称信息
(3)字段集合中不会列出从父类或父接口中继承来的字段,但有可能出现原本Java代码中不存在的字段,如在内部类中为了保持对外部类的访问性,编译器会自动添加指向外部类实例的字段
methods_count
方法的个数,u2
methods
(1)方法集合,不定长
(2)用于描述方法的修饰符(权限、static、final、synchronized、native、abstract)、返回值类型、名称、参数(数量、类型及顺序)
(3)方法内的代码在经过Javac编译器编译成字节码指令后,存放在Code属性中,而不是方法集合中
(4)重载:若两个方法有相同的名称和特征签名,但返回值不同,是可以合法存在于同一个Class文件的。特征签名是指一个方法中各个参数在常量池中的字段符号引用的集合,注意这里不包含返回值
attributes_count
属性个数,u2
attributes
属性表集合,Class文件、字段表、方法表都可以携带自己的属性表集合,属性表集合不要求各个属性表具有严格顺序,只要不重名即可
(1)Code,方法表的属性集合中,Java程序方法体内的代码经Javac编译器处理变为字节码指令存储在其中,可以包含以下属性
- attrbute_name_index,u2,该属性的属性名称,固定为"Code"
- attribute_length,u4,后续属性的总长度,固定为属性值的长度 - 4 - 2
- max_stack,u2,操作数栈深度的最大值,虚拟机运行时根据该值分配栈帧中的操作栈深度
- max_locals,u2,局部变量表所需的内存空间,方法中局部变量所占变量槽数量之和(小于4B的类型占1个,其他占2个)
- code_length,u4,字节码指令长度,一个方法不允许超过65535条字节码指令,超过限制拒绝编译
- code,u1,存储字节码指令的一系列字节流,一共可以表达256条指令
- exception_table_length,u2,异常表长度
- exception_table,不定长集合,指定了代码开始、结束字节码指令的位置、异常类型及出现异常后要跳转的字节码指令的位置
(2)Exceptions,方法表中与Code属性平级的属性,作用是列举出方法throws关键字后列举的异常
(3)LineNumberTable,Code属性中
- 用于描述Java代码行号与字节码位置的对应关系,方便运行时查看异常信息、断点调试。
- 默认会自动生成到Class文件,可使用Javac -g:none或-g:line取消或要求生成这项信息,
(4)LocalVariableTable,类中
- 用于描述栈帧中局部变量表的变量与Java代码中定义的变量之间的关系
- 默认会自动生成,可使用Javac -g:none或-g:vars取消或要求生成,取消后在调试期间无法根据参数名称从上下文中获得参数值
(5)LocalVariableTypeTable,类中,JDK5新增属性,使用特征签名(Signature)描述泛型参数化类型
(6)ConstantValue,用于通知虚拟机自动为静态变量赋值
(7)InnerClasses,用于记录内部类和外部类之间的关联。如果一个类定义了内部类,编译器会自动为它和它的内部类生成该属性
(8)Signature,用于单独记录泛型签名信息,因为字节码中所有泛型信息编译在编译之后都会被擦除掉
(9)MethodParameters,方法表中不定长属性,用于记录方法的各个形参名称和信息