龙空技术网

Java字节码翻译

IT农民工1024 377

前言:

现在朋友们对“oracle数据字典翻译中文”大约比较看重,看官们都需要学习一些“oracle数据字典翻译中文”的相关资讯。那么小编也在网上搜集了一些对于“oracle数据字典翻译中文””的相关知识,希望各位老铁们能喜欢,同学们一起来学习一下吧!

作为一个大龄程序员,从开始学习java的时候就开始接触class文件,作为一个拿来即用主义的人,仅需要知道怎么跑起程序即可完成所有工作,从来没有想到看什么是字节码,无奈Java太内卷了,只能尝试花了一天,去看懂字节码。

啥是class文件

Class文件格式采用一种类似于C语言结构体的方式进行数据存储,这种结构中只有两种数据类型:无符号数和表。

无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和 8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8编码构成字符串值。

表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class 文件本质上就是一张表。由于表没有固定长度,所以通常会在其前面加上个数说明。

为了避免与类和类实例等字段的混淆,描述类文件格式的结构的内容被称为项。连续的项是按顺序存储在类文件中,且没有填充或对齐。

class文件结构与每项占用的字节长度

ClassFile {   u4 magic;                        //魔数,标识为Class文件  u2 minor_version;                     //副版本号(小版本)  u2 major_version;                     //主版本号(大版本)  u2 constant_pool_count;                 //常量池计数器  cp_info constant_pool[constant_pool_count-1];        //常量池表  u2 access_flags;                     //访问标识  u2 this_class;                       //类索引  u2 super_class;                     //父类索引  u2 interfaces_count;                   //接口计数器  u2 interfaces[interfaces_count];               //接口索引集合  u2 fields_count;                      //字段计数器  field_info fields[fields_count];               //字段表  u2 methods_count;                    //方法计数器  method_info methods[methods_count];           //方法表  u2 attributes_count;                   //属性计数器  attribute_info attributes[attributes_count];          //属性表}

从javac对源码进行编译后得到的class文件,打开后类似于乱码的文件,实际上内容是按照class的结构以每一项类似数组按顺序排列。

例如 [u4 magic][u2 minor_version][u2 major_version][...][...][...][...][...][...][...]

参考ClassFile里面的结构,根据字节码文件按照对应的字节数进行翻译。

通过javap -v -p .class文件也能得到字节码翻译后的文件

源代码

javac编译后的class文件

开始痛苦之旅

魔数、主次版本号、常量池计数器

魔数(2字节)

提供标识类文件格式的魔术数字;它的值为0xCAFEBABE。

主版本号(2字节)与次版本号(2字节)

决定class文件的版本号,通常以主版本号.次版本号作为class文件的版本,高版本的字节码文件不能在低版本的虚拟机中运行,Java8对应的版本号为52.0,16进制34代表是52。

常量池计数器(2字节)

常量池计数器值为常量池长度 + 1 ,即常量池长度为10 ,那么常量池计数器值为11。该常量池计数器 26即常量池计数器为38。

常量池表

常量池主要存放两大常量:字面量和符号引用。字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为final的常量值。而符号引用则属于编译原理方面的概念。

常量池中每一项常量都是一个表 ,开始的第一位是一个 u1 类型的标志位 【tag】 来表示常量类型。

#常量池结构cp_info {   u1 tag;   u1 info[]; }

tag代表的含义:

类型

标志(tag)

描述

CONSTANT_utf8_info

1

UTF-8编码的字符串

CONSTANT_Integer_info

3

整型字面量

CONSTANT_Float_info

4

浮点型字面量

CONSTANT_Long_info

5

长整型字面量

CONSTANT_Double_info

6

双精度浮点型字面量

CONSTANT_Class_info

7

类或接口的符号引用

CONSTANT_String_info

8

字符串类型字面量

CONSTANT_Fieldref_info

9

字段的符号引用

CONSTANT_Methodref_info

10

类中方法的符号引用

CONSTANT_InterfaceMethodref_info

11

接口中方法的符号引用

CONSTANT_NameAndType_info

12

字段或方法的符号引用

CONSTANT_MethodHandle_info

15

表示方法句柄

CONSTANT_MethodType_info

16

标志方法类型

CONSTANT_InvokeDynamic_info

18

表示一个动态方法调用点

常量池第一项中0A等于10进制里面的10,根据tag对照表中属于CONSTANT_Methodref_info结构一共5个字节。

CONSTANT_Methodref_info {   u1 tag;   u2 class_index;   u2 name_and_type_index; } 

常量池第一项字节码

{  tag:CONSTANT_Methodref_info ,   class_index:6 ,  name_and_type_index:24}

参照javap

第7项

常量池表中第7项中,tag = 01 ,length = 8 ,bytes长度 = 8 , 总长度 1 + 2 + 8 = 11

每项常量池都以这种方式去翻译得出下表,一共37个项,证明常量池表长度 + 1 = 常量池计数器的值。

	 #1 = Methodref          #6.#24         // java/lang/Object."<init>":()V   #2 = Fieldref           #5.#25         // simple02/ByteCodeTest.fieldInt:I   #3 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;   #4 = Methodref          #28.#29        // java/io/PrintStream.println:(I)V   #5 = Class              #30            // simple02/ByteCodeTest   #6 = Class              #31            // java/lang/Object   #7 = Utf8               fieldInt   #8 = Utf8               I   #9 = Utf8               <init>  #10 = Utf8               ()V  #11 = Utf8               Code  #12 = Utf8               LineNumberTable  #13 = Utf8               LocalVariableTable  #14 = Utf8               this  #15 = Utf8               Lsimple02/ByteCodeTest;  #16 = Utf8               method01  #17 = Utf8               ()I  #18 = Utf8               main  #19 = Utf8               ([Ljava/lang/String;)V  #20 = Utf8               args  #21 = Utf8               [Ljava/lang/String;  #22 = Utf8               SourceFile  #23 = Utf8               ByteCodeTest.java  #24 = NameAndType        #9:#10         // "<init>":()V  #25 = NameAndType        #7:#8          // fieldInt:I  #26 = Class              #32            // java/lang/System  #27 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;  #28 = Class              #35            // java/io/PrintStream  #29 = NameAndType        #36:#37        // println:(I)V  #30 = Utf8               simple02/ByteCodeTest  #31 = Utf8               java/lang/Object  #32 = Utf8               java/lang/System  #33 = Utf8               out  #34 = Utf8               Ljava/io/PrintStream;  #35 = Utf8               java/io/PrintStream  #36 = Utf8               println  #37 = Utf8               (I)V

此时,正常情况需要停下休息,去治疗一下眼睛了。

访问权限(2字节)

访问标识用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为 public类型,是否定义为 abstract类型;如果是类的话,是否被声明为final等。

类索引(2字节)

该索引指向的字面量格式文件的全限定名,如:java/lang/Object

父类索引(2字节)

该索引指向常量池中CONSTANT_Class_info结构。

接口计数器(2字节)

为类实现的接口数量

接口索引表

该索引集合每项指向常量池中CONSTANT_Class_info结构。

访问标识 21 即 ACC_PUBLIC , ACC_SUPER

类索引指向常量池中#5 字面值 =simple02/ByteCodeTest

父类索引指向常量池中#6 字面值 =java/lang/Object

接口计数器=0,所以后面没有接口索引表

字段计数器(2字节)

描述类中有多少字段。

字段索引集合

用于描述接口或类中声明的变量。字段(field)包括类级变量以及实例级变量,但是不包括方法内部、代码块内部声明的局部变量。

字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。

它指向常量池索引集合,它描述了每个字段的完整信息。比如字段的标识符、访问修饰符(public、private或protected)、是类变量还是实例变量(static修饰符)、是否是常量(final修饰符)等。

#字段结构field_info {   u2 access_flags;   u2 name_index;   u2 descriptor_index;   u2 attributes_count;   attribute_info attributes[attributes_count]; } 

字段计数器 = 1 ,字段表项长度1,字段标识符 = 0x0002 即ACC_PRIVATE,字段名索引=#7字面量=fieldInt,描述=#8 字面量I 即基本数据类型int,attributes_count = 0

方法计数器(2字节)

表示该类中有多少个方法,但不包括超类或超接口中的方法数量。

方法表

表中的每个成员都必须是一个method_info结构,用于表示当前类或接口中某个方法的完整描述。

如果某个method_info结构的access_flags项既没有设置 ACC_NATIVE 标志也没有设置ACC_ABSTRACT标志,那么该结构中也应包含实现这个方法所用的Java虚拟机指令。

method_info结构可以表示类和接口中定义的所有方法,包括实例方法、类方法、实例初始化方法和类或接口初始化方法·方法表的结构实际跟字段表是一样的:

method_info {   u2 access_flags;   u2 name_index;   u2 descriptor_index;   u2 attributes_count;   attribute_info attributes[attributes_count]; } 

方法计数器

方发表中第一个方法

根据字节码,方法表中第一个方法有一个属性,属性指向的字面量是Code,其结构

Code_attribute {   u2 attribute_name_index;   u4 attribute_length;   u2 max_stack;   u2 max_locals;   u4 code_length;   u1 code[code_length];   u2 exception_table_length;   { u2 start_pc;   u2 end_pc;   u2 handler_pc;   u2 catch_type;   } exception_table[exception_table_length];   u2 attributes_count;   attribute_info attributes[attributes_count];}

code[]就是属于虚拟机的指令,它由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。由于Java虚拟机采用面向操作数栈而不是寄存器的结构,所以大多数的指令都不包含操作数,只有一个操作码。

由于限制了Java 虚拟机操作码的长度为一个字节(即0~255),这意味着指令集的操作码总数不可能超过256条。

对照字节码指令索引,一个一个查字典方式去标识翻译命令,与javap的文件对比验证是否有误。

翻译后变成如下代码 public simple02.ByteCodeTest();    descriptor: ()V    flags: ACC_PUBLIC    Code:      stack=2, locals=1, args_size=1         0: aload_0         1: invokespecial 	#1                  // Method java/lang/Object."<init>":()V         4: aload_0         5: iconst_1         6: putfield      #2                  // Field fieldInt:I         9: return      LineNumberTable:        line 3: 0        line 5: 4      LocalVariableTable:        Start  Length  Slot  Name   Signature            0      10     0  this   Lsimple02/ByteCodeTest;

得出3个方法,不同颜色标记

终于到最后了。。。。。

attributes_count 、 attributes[]

属性表集合,指的是class文件所携带的辅助信息,比如该class文件的源文件的名称。以及任何带有RetentionPolicy.CLASS 或者RetentionPolicy.RUNTIME的注解。这类信息通常被用于Java虚拟机的验证和运行,以及Java程序的调试,一般无须深入了解。

此外,字段表、方法表都可以有自己的属性表。用于描述某些场景专有的信息。

属性表集合的限制没有那么严格,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,但]ava虚拟机运行时会忽略掉它不认识的属性。

名字索引得出属性是属于SourceFile结构,定长占8个字节,指向常量池中#23,字面值为ByteCodeTest.java

#属性结构attribute_info {   u2 attribute_name_index;   u4 attribute_length;   u1 info[attribute_length];}#SourceFile结构SourceFile_attribute {   u2 attribute_name_index;   u4 attribute_length;   u2 sourcefile_index;}

总结

class文件的结构以每一项类似数组按顺序排列。[u4 magic][u2 minor_version][u2 major_version][...][...][...][...][...][...][...]

每项都有对应的结构,根据【java虚拟机规范】文档,好似查字典一样去解析即可。

文档目录

标签: #oracle数据字典翻译中文