类文件结构

类文件结构中的数据:类,接口,父类方法表,字段表等等最终都变成了JVM运行时的数据区域-方法区,其中常量池则放在了方法区中的运行时常量池。我们JVM包含了==程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。所以我们一般运行时候是通过Class(在堆上)对象去方法区寻找该类的一些数据。

class文件介绍

  • 1.class文件是一组 以8位字节为基础单位的二进制流。
  • 2.因为是二进制流,所以class文件可以由磁盘文件,也可以由网络提供这些流。
  • 3.整个class文件就是一个表。

class文件结构:

  • 1.文件结构:无符号数+表。

  • 2.无符号数:u1,u2,u4,u8。分别代表1个字节,2个字节,4个字节和8个字节的无符号数。该数可以表示数字,索引引用。
    数量值或者按照UTF-8编码构成字符串值。

  • 3.表:可以有多个无符号数或者其他的表作为数据项构成的复合数据类型。所有的表都习惯性地以"_info"结尾。

  • 4.具体的结构如下:魔数+小版本号+大版本号+常量数量+常量池(表)
    +访问标志+自己类,父类+(接口的数量+接口索引表集合)+(字段数量+字段表集合)
    +(方法数量+方法表集合)+(属性数量+属性表集合)。

  • 5.上述除了魔数是u4,其他的无符号数(不包括表)都是u2,但是表中可能包含(u1-u4)。

class文件拾遗

  • 1.高版本JDK好能兼容低版的class文件,版本号以第5-8的两个次主版本号计算。比如JDK1.01.1是使用了45.045.3的版本号。
  • 2.常量数量是2个字节表示,从1开始。0是为了表达不引用任何一个常量池。
  • 3.对于其他表类型的计数值都是从0开始的

class文件--常量池主要内容

  • 1.字面量:文本字符串,被final申明的常量值等。

  • 2.符号引用:类和接口的全限定名,方法的名称和描述符号,字段的名称和描述符号。

  • 3.符号引用是什么:class文件中不会保存方法和字段再内存的最终布局信息,因此当JVM运行时候从常量池中获取符号引用,再在类创建或者运行时候解析并映射到具体的内存地址之中。

  • 4.符号引用举例子: com/springbootmesh/test/Test.value:Ljava/lang/String,这个符号JVM解析的时候就知道是 com/springbootmesh/test目录下Test类,属性名称位value的String类型变量。

class文件--常量池细致分析

  • 1.常量池可以理解为第一个4位代表常量池常量的数量
  • 2.紧着着就是第一个常量的tag+xxx_index+bytes其中xxx_index可能有多个,比如class_index,string_index,而bytes一般代表字符串。
  • 3.按照上面的结论第二个常量依然是以tag开始,tag指定这个常量是什么。
  • 4.常量池中的每一项都是CONSTANT_XXX,其中XXX可以是String,methodref,utf8等等也就是一共11项(有可能是13项 即包含了CONSTANT_InvokeDynamic和CONSTANT_InvokeDynamicTrans)
  • 5.xxx_index=2 代表是常量池中的第二项常量。举个列子:CONSTANT_Methodref[10](class_index = 11, name_and_type_index = 32),分析如下:tag=10(类中的方法符号引用),class_index = 11(代表是哪个class,这个class指向第11个常量), name_and_type_index = 32(字段或者方法的部分符号引用一般可以代表方法的返回值,指向第32个常量)
  • 6.第11个常量如下CONSTANT_Class[7](name_index = 45) tag=7代表类或者接口中的符号引用,name_index代表这个符号引用名称是啥指向第45个常量,第32个常量如下:CONSTANT_NameAndType[12](name_index = 17, signature_index = 18)。
  • 7.可以看到常量池第45是Object的class,代表我们的方法是Object的。分析第32个常量可以得到方法是name_index= ,signature_index=()v(代表void),这就是代表我们的方法是Object的构造函数 。

class文件-常量池拾遗

  • 1.常量池中的CONSTANT_utf8的表是采用UTF-8缩略编码,我们知道UTF-8可以表示1-4个字节(在mysql中UTF-8可以细分位utf8和utf8mb4 前者代表1-3个字节可以编码大部分字符,但是对于一些特殊字符需要4个字节则采用utf8mb4)。
  • 2.这边的缩略编码可以这样理解即能优先采用一个字节或者两个字节编码的就采用,如果不可以就采用3个字节编码这样节省内存。(\u001-\u007f uft-8编码16进制)

class文件-类索引和父类索引和接口索引集合

  • 这2个值都是u2类型的数据,他们的值都是指向常量池中的第几个常量,然后jvm根据这些常量知道我们的类和父类以及继承的接口。

class文件-字段表

  • 1.一个字段表包含:name_index(字段名称),signature_index(字段类型),attributes,attributes_count,access_flag

  • 2.字段表可以表示类变量和实例变量。

  • 3.我们举个例子private final static int finalStaticValue = 100; 则attributes=100,attributes_count=1。

  • 4.字段表中只列出了当前类的字段。

  • 5.如果一个类和其子类都有同样的变量,我们主要是根据形参去获取值(比如a.value=3 A.value=4,JVM主要判断对象是什么类型比如我们A修饰就是4,用a就是3)。

class文件-方法表

  • 1.方法表包含的数据和字段表一样。
  • 2.方法表中的attributes里面是Code,Code包含的方法体内代码。
  • 3.方法重载(类中)的时候,指的是方法名称一样但是签名不一样
  • 4.重写是指子类重写父类的方法
  • 5.方法签名是指:方法名称+方法参数顺序+方法参数类型

class文件-方法表-Code属性表

  • 1.max_stack:操作数栈的最大深度,用于对于指令进行操作的。(递归的时候深度,是指的线程的栈帧)

  • 2.max_locals:代表局部变量表所需要的空间,即是分给引用和returnAddress,基本单位是slot,除了long和doubble占有2个,其他都是占1个。
    我们的方法参数,excetion参数,局部变量都需要用到slot。this占用第一个slot。max_slot不等于之前所有的之和,因为其
    可以复用,只要这个slot包含的数据,过了作用域即可。

  • 3.code_length:代表源码编译后的字节码指令长度。虽然其是4个字节长度,但是JDK规定了方法的字节码指令不能超过
    65535

  • 4.code:存放编译后源码的字节码指令的流。因为是字节码指令,这就意味着指令最多只有256个。

  • 5.exception_table_length:异常处理表的长度。

  • 6.exception_table:不是必须的一般只有加上trycatch才有。

  • 7.attributes:Code属性表里面内容

  • 8.attributes_count:有多少个属性表,一般有lineNubmer(0,22)和LocalVariable

  • 9.name_index:固定为Code

  • 10.tag:代表是什么。一般2等于Code属性

  • 11.length:Code属性表的总长度减去属性名称索引与属性长度一共是6个字节。

  • lineNubmer(0,22):0代表方法的第一行,22代表在源码的位置。

  • LocalVariableTable:就是局部变量表,局部变量表里面一般只有给某个变量赋值了才会在里面,而我们的max_locals,只是计算了最大有多少个局部变量

  • LocalVariableTable: Start_pr Length两个属性代表某个变量的作用域范围。

  • int vv=3;的指令就是=iconst_3, istore_1 即是把常量3保存到slot为1中 如果有return 则增加指令 iload_1和 ireturn即指的是
    把slot1的变量推至栈顶,然后return出去。

  • 我们有时候会在字节码中看到 iconst_xx或者 aconst_xx 其实i表示的是int,a表示的是调用方法或者this,其他还有dalod等

class文件-属性表

  • 1.属性表:Code(方法表),ConstantValue(字段表)等等。

class文件-Exceptions

  • 1.一般就是我们在方法上面throw出去的异常,这些异常都是受检查异常。

class文件-ConstantValue

  • 1.该属性主要是通知JVM自动为静态变量赋值,只有被static关键子修饰的变量才可以使用。
  • 2.非static变量则在方法赋值,static变量则即可以在赋值也可以使用ConstantValue属性赋值。
  • 3.sun的默认是如果同时有final+static且是String或者其他的基本类型就使用ConstantValve来初始化。
  • 4.其他没有被final修饰的static,或者是非String以及基本类型的就在
  • 5.虽然final的语义更符合ConstantValve,但是虚拟机规范中并没有强制要求字段必须设置了ACC_FINAL,只是
    要求ACC_STATIC标志。
  • 6.对于final关键子的要求是javac编译器自己加入的限制。

二进制和十六进制

1.我们可以看class文件中最小的无符号数是2个字节,因为一个字节是八位所以两个字节就是16位

2.我们的一个二进制字节最大值等于16进制的ff即代表2位,所以一般2个字节就算代表4位16进制。

3.而我们知道魔数是4个字节代表8位=正好等于cafebabe

你可能感兴趣的:(类文件结构)