zoukankan      html  css  js  c++  java
  • Class文件结构及方法中的指令

           class文件是以8位字节为基础单位的二进制流,数据项按照顺序存储在class文件中,相邻的项之间没有任何间隔。占据多个字节的项按照高位在前的顺序分为几个连接的字节存放。可变长度项的大小和长度位于其实际数据之前。这个特性使得class文件流可以从头到尾被顺序解析,首先读出项的大小,然后读出项的数据(这里的数据项和可变长度项指组成class文件的无符号数和表的有效无符号数,有效指实际使用的字节)。

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

                   无符号数属于基本数据类型,u1,u2,u4,u8分别代表1,2,4,8个字节长度的无符号数。无符号数用来描述数字、索引引用、数量值、UTF-8编码的字符串。

                   表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性得以_info结尾。表用于描述有层次关系的复合结构的数据,整个class文件本质上就是一张表。

     
     数据项类型  数据项名称  该名称数据项的个数
     u4(4个字节长度的基本数据类型)  magic   (魔数)   1
     u2(2个字节长度的基本数据类型)  minor_version(次版本号)  1
     u2(2个字节长度的基本数据类型)  major_version(主版本号)  1
     u2(2个字节长度的基本数据类型)  constant_pool_count(常量池容量计数)  1
     cp_info(表结构复合数据类型)  constant_pool(常量池)  constant_pool_count-1
      u2(2个字节长度的基本数据类型)  access_flags(访问标志)  1
      u2(2个字节长度的基本数据类型)  this_class  (类索引)    1
      u2(2个字节长度的基本数据类型)  super_class(父类索引)  1
      u2(2个字节长度的基本数据类型)  interfaces_count(接口计数器)  1
      u2(2个字节长度的基本数据类型)  interfaces(接口索引集合)  interfaces_count
      u2(2个字节长度的基本数据类型)  fields_count(字段计数器)  1
     field_info(表结构复合数据类型)  fields(字段)  fields_count
      u2(2个字节长度的基本数据类型)  methods_count(方法计数器)  1
     method_info(表结构复合数据类型)  methods(方法)  methods_count
     u2(2个字节长度的基本数据类型)  attributes_count(属性计数器)  1
     attribute_info(表结构复合数据类型)  attributes(属性)  attributes_count

    常量池中每一个常量都是一个表,从JDK1.7开始共有14种不同的表结构,这些表结构的特点是,表开始是u1类型的标志位,代表该常量属于哪种常量类型。 

    常量池中存放两大类常量:字面量和符号引用,字面量如文本字符串,final型的常量值,符号引用包括三类常量,类/接口的全限定名,字段的名称和描述符,方法的名称和描述符。


    package mainTest;

    public class MainTs {

    public static void main(String[] args) {
    System.out.println(2);
    }
    }

                                                                                             

    CA FE BA BE       00 00         00 32             00 20                  07  00  02(常量1)                        01  00  0f  

    魔数                次版本号0    主版本号50    常量池容量计数32    常量池第2个常量为类名         后面紧跟15个字符  

    6D  61  69  6E  54  65  73  74  2F  4D  61  69  6E  54  73(常量2)                                                         07

     m   a     i    n    T   e    s    t    /     M    a    i     n    T    s 

      00  04(常量3)                                                     01  00  10     6A  61  76  61  2F  6C  61  6E  67  2F  4F      

    常量池第4个常量为类名                                         后面紧跟16个字符 j    a    v    a    /    l     a    n    g    /    O

    62  6A  65  63  74(常量4)               01  00  06     3C  69  6E  69  74  3E(常量5)                          01  00

     b   j     e    c    t                         后面紧跟6个字符 <    i     n    i     t    >                                          

        03     28 29 56(常量6)          01 00 04  43 6F 64 65(常量7)                    0A  00  03  00  09(常量8)           

    后跟3个字符(   )   V                   后跟4个字符C   o  d   e            方法所属类为常量3,方法名和描述符为常量9

    0C  00  05  00  06(常量9)                                                               01  00  0F  4C  69  6E  65  4E  75  6D  62

    方法名为<init>,方法描述符为()V                                                   后跟15个字符     L    i     n    e    N    u    m    b

    65  72  54  61  62  6C  65(常量10)                                                            01  00  12  4C  6F  63  61  6C  56

     e    r    T    a    b   l     e                                                                       后跟18个字符  L    o    c     a    l     V

     61  72  69  61  62  6C  65  54  61  62  6C  65(常量11)                                                        01  00  04  74

     a    r     i     a    b    l     e    T    a    b    l     e                                                                    后跟4个字符   t

    68  69  73(常量12)                                                          01  00  11 4C  6D  61  69  6E  54  65  73  74  2F

    h    i     s                                                                      后跟17个字符 L    m   a    i     n    T    e    s    t    /  

    4D  61  69  6E  54  73  3B(常量13)                                        01  00  04  6D  61  69  6E(常量14)      01  00       

     M   a    i     n    T    s    ;                                                     后跟4个字符   m   a    i     n          后跟22个字符

    16  28  5B  4C  6A  61  76  61  2F  6C  61  6E  67  2F  53  74  

          (     [    L    j     a    v    a    /     l     a    n    g   /    S    t

    72  69   6E  67  3B  29  56(常量15)              09  00  11  00  13(常量16)                      07 00 12(常量17)    01 

     r     i     n    g   ;     )     V                字段所属类为第17常量,字段名和描述符为第19常量      指向常量18

          00  10  6A  61  76  61  2F  6C  61  6E  67  2F  53  79  73  74

    后跟16个字符 j    a    v    a    /     l    a     n   g    /    S    y    s    t

    65  6D(常量18)             0C  00  14  00  15(常量19)               01  00  03  6F  75  74(常量20)        01  00  15     

     e    m                  字段名为第20常量,字段描述符为第21常量     后跟3个字符  o    u   t                后跟21个字符  

    4C  6A  61  76  61  2F  69  6F  2F  50  72  69  6E  74  53  74

     L    j     a    v    a   /     i     o   /     P    r    i     n    t    S    t

    72 65 61 6D 3B(常量21)     0A  00  17  00  19                                 07  00  18     01  00  13                              

     r   e   a   m   ;             方法所属类为常量23,方法名和描述符为常量25    指向常量24    后跟19字符

    6A  61  76  61  2F  69  6F  2F  50  72  69  6E  74  53  74  72

    j     a    v    a   /     i     o   /     P    r    i     n    t    S    t     r

    65  61  6D(常量24)    0C  00  1A  00  1B                  01  00  07  70  72  69  6E  74

     e    a    m              方法名为常量26,描述符为常量27   后跟7个常量 p    r    i      n   t

    6C  6E      01  00  04  28  49  29  56     01  00  04  61  72  67  73(常量28)

     l     n     后跟4个常量    (   I  )     V       后跟4个常量   a     r    g    s

    01  00  13  5B  4C  6A  61  76  61  2F  6C  61  6E  67  2F  53

    后跟19个字符[   L     j     a    v    a    /    l     a     n    g    /    S

    74  72  69  6E  67  3B        01  00  0A  53  6F  75  72  63  65  46

     t    r     i     n    g    ;        后跟10个字符 S   o    u    r    c    e    F

    69  6C  65(常量30)        01  00  0B 4D  61  69  6E  54  73  2E  6A  61  76

    i      l     e                  后跟11个字符  M    a    i    n    T    s    .    j     a     v

    61                                00  21                       00  01                   00  03                   00  00            

    a     此类为public,ACCESS_SUPER标志为真    本类类名是常量1    本类的超类为常量3    此类接口数为0(为0则接口集合不占用字节)     

    00  00                                                                       00  02                                            00  01             

    类中字段计数为0(类变量和实例/成员/全局变量数总和)           方法计数为2                            方法访问修饰符为public

    00  05                             00  06                         00  01                    00  07                    

    方法名为常量5<init>   方法描述符为常量6()V       方法属性计数为1         方法的属性名为常量7Code

    00  00  00  2F             00  01                                            00  01                        

    方法的属性长度47      操作数栈深度的最大值          局部变量表所需存储空间为1slot                                                                                      

                                                                   (除double和long类型需要两个slot存放,其他基本类型一个slot存放)

    00  00  00  05                       2A  B7  00  08  B1         00 00                   00  02                  

     字节码指令长度为5个字节            字节码指令                 异常表长度为0      异常属性表长度为2

    00  0A                                          00  00  00  06             00  01

    异常属性名为LineNumberTable          属性长度为6           line_number_table_length    

    00  00               00  02                              00  0B                     00  00  00  0C         00  01

    start_pc    从start_pc开始的行号为2      属性名为LocalvariableTable      属性长度为12   local_variable__table_length 为1

    新行开始时代码数组的偏移量为0

    00  00(start_pc)                                        00  05                                                                              

    代码数组中指令开始位置的偏移量            从start_pc开始所有局部变量有效代码长度(两者确定的即为局部变量作用域)

    00  0C                              00  0D                                       00  00                                

    局部变量名为this           局部变量描述符为LmainTest/MainTs      此局部变量在方法栈中的位置(long或double型变量占据方法栈中index和index+1处的两个字节)

    00  09                                        00  0E                  00  0F

    方法修饰符为public和static(1+8)     方法名为main      方法描述符为([ L jav a / l a n g/String;)V

    00  01                 00  07                      00  00  00  36                    00  02                

    方法属性计数为1      属性名为Code          属性长度为54个字节        该方法的操作数栈的最大长度

    00  01                                                   

    局部变量所需存储空间的长度(以字为单位)

    无论虚拟机什么时候描述被code属性描述的方法。它都必须分配一个长度为max_locals的局部变量数组,

    这个数组用来存储传递给方法的参数以及为方法所使用的局部变量。long或double型局部变量的最大有效

    索引为max_locals-2,其他类型为max_locals-1

    00  00  00  08                          B2  00  10  05  B6  00  16  B1        00  00

    方法字节码流长度为8字节              字节码指令                           异常表个数为0

    00  02                       00  0A                                     00  00  00  0A

    异常属性计数为2         属性名为LineNumberTable           属性长度为10字节

      00  02                                      00  00               00  04                    00  07           00  05 

    line_number_table的个数为2        start_pc    从start_pc开始的行号为4     start_pc为7    从start_pc开始的行号为5

    00  0B                                    00  00  00  0C       00  01

    属性名为LocalvariableTable         属性长度为12        local_variable_table_length 为1

      00  00                                     00  08                     

     代码数组中指令开始位置的偏移量    从start_pc开始所有局部变量有效代码长度(两者确定的即为局部变量作用域)

    00  1C                    00  1D                                           00  00    

    局部变量为args      局部变量描述符为java/lang/String        此变量在方法栈中的索引为0

    00  01               00  1E                  00  00  00  02         00  1F

    属性计数为1      属性名为SourceFile      属性长度为2字节      属性值为MainTs.java

    java程序方法体中的代码经javac编译期处理后,最终变为字节码指令存放在方法表的属性表的Code属性中。如果某方法的方法体为空或无方法体,如接口或抽象类,那么该方法在class文件中对应的方法表无Code属性。

     CONSTANT_CLASS_info型常量结构(类名索引)                                                        CONSTANT_UTF8_info型常量结构(字符串)

        u1                         u2                                                                              u1                           u2                                    (length个)u1

        tag                  name_index                                                                       tag                         length  

                         (指向常量池中第几个索引)                                                                         (后面有多少字节表示UTF-8编码的字符串)

    7f及以下用一个字节描述一个字符,7ff及7f以上用两个字节描述一个字符,7ff以上四个字节描述一个字符(ASCII码表)  

     CONSTANT_Methodref_info型常量结构(方法索引)                                                                               CONSTANT_NameAndType_info

      u1                    u2                                     u2                                                             u1                            u2                                  u2

    tag               Class_index                   name_and_type_index                                           tag                         name_index                  descriptor_index

               (方法所属类在常量池中的索引)      (方法名称和描述符在常量池中的索引)                                                        字段名/方法名索引            字段/方法描述符索引

    CONSTANT_Methodref_info型常量结构(字段索引)                                                                            

     u1                    u2                                     u2                                                             

    tag               Class_index                   name_and_type_index                                      

               (字段所属类在常量池中的索引)      (字段名称和描述符在常量池中的索引)   

     field_info表的格式

    u2                   u2                    u2                     u2               attribute_info

    access_flags      name_index     descriptor_index   attributes_count     attribute

    attribute_info表的格式

    u2                                                u4                               (attribute_length个)u1

     attribute_name_index               attribute_length                info

    标志             常量池中的表类型                                                   描述

    1                CONSTANT_UTF8_info                      UTF-8编码的字符串

    3                CONSTANT_Integer_info                   整形字面量

    4                CONSTANT_Float_info                      浮点型字面量

    5                CONSTANT_Long_info                      长整形字面量

    6                CONSTANT_Double_info                   双精度浮点型字面量

    7                CONSTANT_Class_info                     类或接口的符号引用

    8                CONSTANT_String_info                    字符串类型字面量

    9                CONSTANT_Fieldref_info                   字段的符号引用

    10              CONSTANT_Methodref_info               类中方法的符号引用

    11              CONSTANT_InterfaceMethodref_info   接口方法的符号引用

    12              CONSTANT_NameAndType_info          字段或方法的部分符号引用

    15              CONSTANT_MethodHandle_info           表示方法句柄

    16              CONSTANT_MethodType_info             标识方法类型

    18              CONSTANT_InvokeDynamic_info        表示一个动态方法调用点

                 字节码指令:

                 java虚拟机指令由一个字节长度的操作码(代表某种特定操作的数字)和跟随其后的n个字节长度的操作数(操作中使用的参数)构成。由于虚拟机采用面向操作数栈而非寄存器的架构,所以大多数指令只有操作码,没有操作数。

                 如果不考虑异常处理的话,java虚拟机的解释器可以使用下面的伪代码当做基本的执行模型来理解。这个模型虽然简单,但可以有效得工作。

                 do{

                       自动计算PC寄存器的值加1

                       根据PC寄存器的指示位置,从字节码流中取出操作码

                       if (操作码后存在操作数){取出操作数}

                       执行操作码对应的操作

                }while(字节码流长度>0);

                       

                  大部分跟数据类型相关的字节码指令,他们的操作码助记符中都有特殊的字符来表明为哪种类型数据服务。s代表short,a代表reference。iload指令用于从局部变量表中加载int型数据到操作数栈中,fload指令加载的则是float型的数据。

                  arraylength指令中没有助记符,但该指令的操作数只能是一个数组类型的对象。无条件跳转指令goto则是与数据类型无关的。

                  大部分指令不支持整数类型byte,char,short,boolean。因为byte和short型数据会在编译器或运行期被带符号扩展为相应的int型数据,将boolean和char的零位扩展为相应的int型数据。因此大多数对boolean,byte,short,char型数据的操作其实是对相应的int型数据进行操作。

                 

                  1.加载和存储局部变量指令

                  加载和存储指令用于在栈帧中的局部变量表和操作数栈之间来回传输数据。

                  将局部变量从局部变量表加载到操作数栈——iload

                  将一个数值从操作数栈存储到局部变量表——istore

                  扩充局部变量表的访问索引——wide

                  getstatic:将静态变量压入操作数栈

                  iconst:将常量压入操作数栈

                  2.运算指令

                  运算或算术指令用于对操作数栈上的值进行特定运算,并把结果重新存入到操作栈顶。

                  加减乘除——add,sub,mul,div

                  按位或/与/异或——or,and,xor

                  3.类型转换指令

                  类型转换指令用于将两种不同数值类型进行相互转换。虚拟机直接支持小范围类型向大范围类型的转换(宽化类型转换),如int到long,float,double,long到float,double,float到double。相反在处理窄化类型转换时必须显示的使用转换指令来完成。尽管窄化转换可能导致上限溢出,下限溢出或精度丢失,虚拟机规范明确规定类型转换不会抛出运行时异常。

                  4.对象创建和访问指令

                  创建类实例/数组——new/newarray,anewarray,multianewarray

                  将数组元素加载到操作数栈——baload,caload

                  将操作数栈存储到数组元素——bastore,castore

                  5.操作数栈管理指令

                  将操作数栈栈顶一个或两个元素出栈——pop,pop2

                  6.控制转移指令

                  控制转移指令可以让虚拟机有条件或无条件的从指定位置指令的下一条指令继续执行程序。可以认为控制转移指令在有条件或无条件得修改PC寄存器的值,不转移的话PC寄存器的值应该是控制转移指令的下一条指令。

                  7.方法调用和返回指令

                  invokevirtual——调用对象的实例方法

                  ireturn,lreturn——方法返回指令是根据返回值的类型区分的

                  8.异常处理指令

                  显示抛出异常指令——athrow(有些运行时异常是虚拟机检测到异常后自动抛出的)

                  处理异常——处理异常不是由字节码来实现的,而是采用异常表来完成的。

                  9.同步指令

                  虚拟机可以支持方法级和方法内部一段指令序列的同步,这两种同步结构都使用管程monitor来支持。

                  方法的同步是隐式的,无需通过指令来完成,虚拟机可以通过常量池的方法表结构中的ACC_SYNCHRONIZED得知一个方法是否是同步方法,如果是,线程需要先持有该管程才能执行同步方法,执行此方法期间其他线程无法获得该管程,方法完成时释放该管程(不论正常完成还是非正常完成)。

                  同步一段指令由java语言中的synchronized语句块来表示,虚拟机的指令中用monitorrnter和monitorexit两条指令来支持synchronized。

                  查看具体代码对应的操作指令可使用javap -c/javap -verbose命令对字节码文件进行反编译。

                  常见指令:

                  代码race++对应的四条指令:

                  getstatic        将静态变量压入操作数栈顶    

                  iconst_1        将常量1压入操作数栈顶

                  iadd            进行相加运算并将相加的结果压入操作数栈顶

                  putstatic       给静态变量赋值

                  

                  空对象调用类方法的字节码指令:

                 

                  

    新生的小心情
  • 相关阅读:
    python字典
    python中List添加、删除元素的几种方法
    python数据处理之基本函数
    python批量处理
    python正则表达式
    python模块学习:os模块
    Hough transform(霍夫变换)
    MODBUS TCP/IP协议规范详细介绍
    Linux下run文件的直接运行
    双边滤波和引导滤波的原理
  • 原文地址:https://www.cnblogs.com/jianmianruxin/p/6906205.html
Copyright © 2011-2022 走看看