空手套白狼,硬阅java字节码class文件

Java基础

浏览数:126

2019-6-8

AD:资源代下载服务

 如下,是一些java字节码也就是原始的class文件,当应用部署到线上之后,我们能够看到的也就是这样的字样了。那么怎样解呢?就让我们一起,来解读解读字节码吧!

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   CA FE BA BE 00 00 00 34  00 6A 0A 00 1C 00 39 0A   漱壕   4 j    9 
00000010   00 13 00 3A 09 00 13 00  3B 09 00 3C 00 3D 08 00      :    ;  < =  
00000020   3E 0A 00 3F 00 40 07 00  41 0A 00 07 00 39 09 00   >  ? @  A    9  
00000030   13 00 42 07 00 43 0A 00  0A 00 39 08 00 44 0A 00     B  C    9  D  
00000040   0A 00 45 0A 00 46 00 47  0A 00 0A 00 48 08 00 49     E  F G    H  I
00000050   0A 00 0A 00 4A 0B 00 4B  00 4C 07 00 4D 0A 00 13       J  K L  M   
00000060   00 39 0A 00 13 00 4E 07  00 4F 0A 00 16 00 39 08    9    N  O    9 
00000070   00 50 0A 00 16 00 51 0A  00 16 00 52 0A 00 16 00    P    Q    R    
00000080   53 07 00 54 01 00 0B 75  73 65 72 53 65 72 76 69   S  T   userServi
00000090   63 65 01 00 24 4C 63 6F  6D 2F 79 6F 75 67 65 2F   ce  $Lcom/youge/
000000A0   73 65 72 76 69 63 65 2F  75 73 65 72 2F 55 73 65   service/user/Use
000000B0   72 53 65 72 76 69 63 65  3B 01 00 09 69 6E 69 74   rService;   init
000000C0   69 61 6C 65 64 01 00 01  5A 01 00 06 3C 69 6E 69   ialed   Z   <ini
000000D0   74 3E 01 00 03 28 29 56  01 00 04 43 6F 64 65 01   t>   ()V   Code 

原始类源码如下:

package com.youge.api;

import com.youge.pojo.user.UserInfo;
import com.youge.service.user.UserService;
import com.youge.service.user.UserServiceImpl;

public class ByteCodeClassKen {

    private UserService userService;

    private boolean initialed;

    public ByteCodeClassKen() {
        init();
    }

    private void init() {
        if(!initialed) {
            System.out.println("init...");
            userService = new UserServiceImpl();
            initialed = true;
        }
    }

    public Integer addUser() throws Exception {
        UserInfo userInfo = new UserInfo();
        userInfo.setName("lilei");
        userInfo.setAge(12);
        userInfo.setAddress("new road.");
        return userService.addUser(userInfo);
    }

    public static void main(String[] args) throws Exception {
        // test
        ByteCodeClassKen classKen = new ByteCodeClassKen();
        Integer affect = classKen.addUser();
        System.out.println("affect: " + affect);
    }
}

完整字节码文件,如有兴趣请展开:


Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   CA FE BA BE 00 00 00 34  00 6A 0A 00 1C 00 39 0A   漱壕   4 j    9 
00000010   00 13 00 3A 09 00 13 00  3B 09 00 3C 00 3D 08 00      :    ;  < =  
00000020   3E 0A 00 3F 00 40 07 00  41 0A 00 07 00 39 09 00   >  ? @  A    9  
00000030   13 00 42 07 00 43 0A 00  0A 00 39 08 00 44 0A 00     B  C    9  D  
00000040   0A 00 45 0A 00 46 00 47  0A 00 0A 00 48 08 00 49     E  F G    H  I
00000050   0A 00 0A 00 4A 0B 00 4B  00 4C 07 00 4D 0A 00 13       J  K L  M   
00000060   00 39 0A 00 13 00 4E 07  00 4F 0A 00 16 00 39 08    9    N  O    9 
00000070   00 50 0A 00 16 00 51 0A  00 16 00 52 0A 00 16 00    P    Q    R    
00000080   53 07 00 54 01 00 0B 75  73 65 72 53 65 72 76 69   S  T   userServi
00000090   63 65 01 00 24 4C 63 6F  6D 2F 79 6F 75 67 65 2F   ce  $Lcom/youge/
000000A0   73 65 72 76 69 63 65 2F  75 73 65 72 2F 55 73 65   service/user/Use
000000B0   72 53 65 72 76 69 63 65  3B 01 00 09 69 6E 69 74   rService;   init
000000C0   69 61 6C 65 64 01 00 01  5A 01 00 06 3C 69 6E 69   ialed   Z   <ini
000000D0   74 3E 01 00 03 28 29 56  01 00 04 43 6F 64 65 01   t>   ()V   Code 
000000E0   00 0F 4C 69 6E 65 4E 75  6D 62 65 72 54 61 62 6C     LineNumberTabl
000000F0   65 01 00 12 4C 6F 63 61  6C 56 61 72 69 61 62 6C   e   LocalVariabl
00000100   65 54 61 62 6C 65 01 00  04 74 68 69 73 01 00 20   eTable   this   
00000110   4C 63 6F 6D 2F 79 6F 75  67 65 2F 61 70 69 2F 42   Lcom/youge/api/B
00000120   79 74 65 43 6F 64 65 43  6C 61 73 73 4B 65 6E 3B   yteCodeClassKen;
00000130   01 00 04 69 6E 69 74 01  00 0D 53 74 61 63 6B 4D      init   StackM
00000140   61 70 54 61 62 6C 65 01  00 07 61 64 64 55 73 65   apTable   addUse
00000150   72 01 00 15 28 29 4C 6A  61 76 61 2F 6C 61 6E 67   r   ()Ljava/lang
00000160   2F 49 6E 74 65 67 65 72  3B 01 00 08 75 73 65 72   /Integer;   user
00000170   49 6E 66 6F 01 00 1E 4C  63 6F 6D 2F 79 6F 75 67   Info   Lcom/youg
00000180   65 2F 70 6F 6A 6F 2F 75  73 65 72 2F 55 73 65 72   e/pojo/user/User
00000190   49 6E 66 6F 3B 01 00 0A  45 78 63 65 70 74 69 6F   Info;   Exceptio
000001A0   6E 73 07 00 55 01 00 04  6D 61 69 6E 01 00 16 28   ns  U   main   (
000001B0   5B 4C 6A 61 76 61 2F 6C  61 6E 67 2F 53 74 72 69   [Ljava/lang/Stri
000001C0   6E 67 3B 29 56 01 00 04  61 72 67 73 01 00 13 5B   ng;)V   args   [
000001D0   4C 6A 61 76 61 2F 6C 61  6E 67 2F 53 74 72 69 6E   Ljava/lang/Strin
000001E0   67 3B 01 00 08 63 6C 61  73 73 4B 65 6E 01 00 06   g;   classKen   
000001F0   61 66 66 65 63 74 01 00  13 4C 6A 61 76 61 2F 6C   affect   Ljava/l
00000200   61 6E 67 2F 49 6E 74 65  67 65 72 3B 01 00 0A 53   ang/Integer;   S
00000210   6F 75 72 63 65 46 69 6C  65 01 00 15 42 79 74 65   ourceFile   Byte
00000220   43 6F 64 65 43 6C 61 73  73 4B 65 6E 2E 6A 61 76   CodeClassKen.jav
00000230   61 0C 00 21 00 22 0C 00  28 00 22 0C 00 1F 00 20   a  ! "  ( "     
00000240   07 00 56 0C 00 57 00 58  01 00 07 69 6E 69 74 2E     V  W X   init.
00000250   2E 2E 07 00 59 0C 00 5A  00 5B 01 00 26 63 6F 6D   ..  Y  Z [  &com
00000260   2F 79 6F 75 67 65 2F 73  65 72 76 69 63 65 2F 75   /youge/service/u
00000270   73 65 72 2F 55 73 65 72  53 65 72 76 69 63 65 49   ser/UserServiceI
00000280   6D 70 6C 0C 00 1D 00 1E  01 00 1C 63 6F 6D 2F 79   mpl        com/y
00000290   6F 75 67 65 2F 70 6F 6A  6F 2F 75 73 65 72 2F 55   ouge/pojo/user/U
000002A0   73 65 72 49 6E 66 6F 01  00 05 6C 69 6C 65 69 0C   serInfo   lilei 
000002B0   00 5C 00 5B 07 00 5D 0C  00 5E 00 5F 0C 00 60 00    \ [  ]  ^ _  ` 
000002C0   61 01 00 09 6E 65 77 20  72 6F 61 64 2E 0C 00 62   a   new road.  b
000002D0   00 5B 07 00 63 0C 00 2A  00 64 01 00 1E 63 6F 6D    [  c  * d   com
000002E0   2F 79 6F 75 67 65 2F 61  70 69 2F 42 79 74 65 43   /youge/api/ByteC
000002F0   6F 64 65 43 6C 61 73 73  4B 65 6E 0C 00 2A 00 2B   odeClassKen  * +
00000300   01 00 17 6A 61 76 61 2F  6C 61 6E 67 2F 53 74 72      java/lang/Str
00000310   69 6E 67 42 75 69 6C 64  65 72 01 00 08 61 66 66   ingBuilder   aff
00000320   65 63 74 3A 20 0C 00 65  00 66 0C 00 65 00 67 0C   ect:   e f  e g 
00000330   00 68 00 69 01 00 10 6A  61 76 61 2F 6C 61 6E 67    h i   java/lang
00000340   2F 4F 62 6A 65 63 74 01  00 13 6A 61 76 61 2F 6C   /Object   java/l
00000350   61 6E 67 2F 45 78 63 65  70 74 69 6F 6E 01 00 10   ang/Exception   
00000360   6A 61 76 61 2F 6C 61 6E  67 2F 53 79 73 74 65 6D   java/lang/System
00000370   01 00 03 6F 75 74 01 00  15 4C 6A 61 76 61 2F 69      out   Ljava/i
00000380   6F 2F 50 72 69 6E 74 53  74 72 65 61 6D 3B 01 00   o/PrintStream;  
00000390   13 6A 61 76 61 2F 69 6F  2F 50 72 69 6E 74 53 74    java/io/PrintSt
000003A0   72 65 61 6D 01 00 07 70  72 69 6E 74 6C 6E 01 00   ream   println  
000003B0   15 28 4C 6A 61 76 61 2F  6C 61 6E 67 2F 53 74 72    (Ljava/lang/Str
000003C0   69 6E 67 3B 29 56 01 00  07 73 65 74 4E 61 6D 65   ing;)V   setName
000003D0   01 00 11 6A 61 76 61 2F  6C 61 6E 67 2F 49 6E 74      java/lang/Int
000003E0   65 67 65 72 01 00 07 76  61 6C 75 65 4F 66 01 00   eger   valueOf  
000003F0   16 28 49 29 4C 6A 61 76  61 2F 6C 61 6E 67 2F 49    (I)Ljava/lang/I
00000400   6E 74 65 67 65 72 3B 01  00 06 73 65 74 41 67 65   nteger;   setAge
00000410   01 00 16 28 4C 6A 61 76  61 2F 6C 61 6E 67 2F 49      (Ljava/lang/I
00000420   6E 74 65 67 65 72 3B 29  56 01 00 0A 73 65 74 41   nteger;)V   setA
00000430   64 64 72 65 73 73 01 00  22 63 6F 6D 2F 79 6F 75   ddress  "com/you
00000440   67 65 2F 73 65 72 76 69  63 65 2F 75 73 65 72 2F   ge/service/user/
00000450   55 73 65 72 53 65 72 76  69 63 65 01 00 33 28 4C   UserService  3(L
00000460   63 6F 6D 2F 79 6F 75 67  65 2F 70 6F 6A 6F 2F 75   com/youge/pojo/u
00000470   73 65 72 2F 55 73 65 72  49 6E 66 6F 3B 29 4C 6A   ser/UserInfo;)Lj
00000480   61 76 61 2F 6C 61 6E 67  2F 49 6E 74 65 67 65 72   ava/lang/Integer
00000490   3B 01 00 06 61 70 70 65  6E 64 01 00 2D 28 4C 6A   ;   append  -(Lj
000004A0   61 76 61 2F 6C 61 6E 67  2F 53 74 72 69 6E 67 3B   ava/lang/String;
000004B0   29 4C 6A 61 76 61 2F 6C  61 6E 67 2F 53 74 72 69   )Ljava/lang/Stri
000004C0   6E 67 42 75 69 6C 64 65  72 3B 01 00 2D 28 4C 6A   ngBuilder;  -(Lj
000004D0   61 76 61 2F 6C 61 6E 67  2F 4F 62 6A 65 63 74 3B   ava/lang/Object;
000004E0   29 4C 6A 61 76 61 2F 6C  61 6E 67 2F 53 74 72 69   )Ljava/lang/Stri
000004F0   6E 67 42 75 69 6C 64 65  72 3B 01 00 08 74 6F 53   ngBuilder;   toS
00000500   74 72 69 6E 67 01 00 14  28 29 4C 6A 61 76 61 2F   tring   ()Ljava/
00000510   6C 61 6E 67 2F 53 74 72  69 6E 67 3B 00 21 00 13   lang/String; !  
00000520   00 1C 00 00 00 02 00 02  00 1D 00 1E 00 00 00 02                   
00000530   00 1F 00 20 00 00 00 04  00 01 00 21 00 22 00 01              ! "  
00000540   00 23 00 00 00 3B 00 01  00 01 00 00 00 09 2A B7    #   ;        *?
00000550   00 01 2A B7 00 02 B1 00  00 00 02 00 24 00 00 00     *? ?    $   
00000560   0E 00 03 00 00 00 12 00  04 00 13 00 08 00 14 00                   
00000570   25 00 00 00 0C 00 01 00  00 00 09 00 26 00 27 00   %           & ' 
00000580   00 00 02 00 28 00 22 00  01 00 23 00 00 00 63 00       ( "   #   c 
00000590   03 00 01 00 00 00 20 2A  B4 00 03 9A 00 1B B2 00          *? ? ?
000005A0   04 12 05 B6 00 06 2A BB  00 07 59 B7 00 08 B5 00      ? *? Y? ?
000005B0   09 2A 04 B5 00 03 B1 00  00 00 03 00 24 00 00 00    * ? ?    $   
000005C0   16 00 05 00 00 00 17 00  07 00 18 00 0F 00 19 00                   
000005D0   1A 00 1A 00 1F 00 1C 00  25 00 00 00 0C 00 01 00           %       
000005E0   00 00 20 00 26 00 27 00  00 00 29 00 00 00 03 00       & '   )     
000005F0   01 1F 00 01 00 2A 00 2B  00 02 00 23 00 00 00 6C        * +   #   l
00000600   00 02 00 02 00 00 00 28  BB 00 0A 59 B7 00 0B 4C          (? Y? L
00000610   2B 12 0C B6 00 0D 2B 10  0C B8 00 0E B6 00 0F 2B   +  ? +  ? ? +
00000620   12 10 B6 00 11 2A B4 00  09 2B B9 00 12 02 00 B0     ? *? +?   ?
00000630   00 00 00 02 00 24 00 00  00 16 00 05 00 00 00 1F        $          
00000640   00 08 00 20 00 0E 00 21  00 17 00 22 00 1D 00 23          !   "   #
00000650   00 25 00 00 00 16 00 02  00 00 00 28 00 26 00 27    %         ( & '
00000660   00 00 00 08 00 20 00 2C  00 2D 00 01 00 2E 00 00          , -   .  
00000670   00 04 00 01 00 2F 00 09  00 30 00 31 00 02 00 23        /   0 1   #
00000680   00 00 00 71 00 03 00 03  00 00 00 27 BB 00 13 59      q       '? Y
00000690   B7 00 14 4C 2B B6 00 15  4D B2 00 04 BB 00 16 59   ? L+? M? ? Y
000006A0   B7 00 17 12 18 B6 00 19  2C B6 00 1A B6 00 1B B6   ?   ? ,? ? ?
000006B0   00 06 B1 00 00 00 02 00  24 00 00 00 12 00 04 00     ?    $       
000006C0   00 00 28 00 08 00 29 00  0D 00 2A 00 26 00 2B 00     (   )   * & + 
000006D0   25 00 00 00 20 00 03 00  00 00 27 00 32 00 33 00   %         ' 2 3 
000006E0   00 00 08 00 1F 00 34 00  27 00 01 00 0D 00 1A 00         4 '       
000006F0   35 00 36 00 02 00 2E 00  00 00 04 00 01 00 2F 00   5 6   .       / 
00000700   01 00 37 00 00 00 02 00  38                          7     8

0. class文件的整体架构格式如下:

type    descriptor                                remark
u4        magic                                    0xCAFEBABE
u2        minor_version
u2        major_version
u2        constant_pool_count
cp_info    constant_pool[cosntant_pool_count – 1]    index 0 is invalid
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]

图表class文件整体架构格式如下:

 

1. 文件头

0xCAFE BABE: 前四个字节,魔数,代表文件类型,java的class文件固定为 0xCAFEBABE. 助记词: 咖啡宝贝

0x0000 0034: 接下来要知道版本号。版本号含主版本号和次版本号,都是各占2个字节。在此Demo种为0X0000 0034。其中前面的0000是次版本号,后面的0034是主版本号。通过进制转换得到的是次版本号为0,主版本号为52。

从oracle官方网站我们能够知道,52对应的正式jdk1.8,而其次版本为0,所以该文件的版本为1.8.0。如果需要验证,可以在用java –version命令输出版本号,或者修改编译目标版本–target重新编译,查看编译后的字节码文件版本号是否做了相应的修改。

2. 常量池

至此,我们共了解了前8字节的含义,下面讲讲常量池相关内容。

紧接着主版本号之后的就是常量池入口。常量池是Class文件中的资源仓库,在接下来的内容中我们会发现很多地方会涉及,如Class Name,Interfaces等。常量池中主要存储2大类常量:字面量和符号引用。字面量如文本字符串,java中声明为final的常量值等等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符。

为什么需要类和接口的全局限定名呢?系统引用类或者接口的时候不是通过内存地址进行操作吗?这里大家仔细想想,java虚拟机在没有将类加载到内存的时候根本都没有分配内存地址,也就不存在对内存的操作,所以java虚拟机首先需要将类加载到虚拟机中,那么这个过程设计对类的定位(需要加载A包下的B类,不能加载到别的包下面的别的类中),所以需要通过全局限定名来判别唯一性。这就是为什么叫做全局,限定的意思,也就是唯一性。

在进行具体常量池分析之前,我们先来了解一下常量池的项目类型表:

这里tag用来表示当前常量池不同类型的项。info中存放常量池项中存放的数据。

tag中表示的数据类型:

tag名称                                remark            tag值
CONSTANT_Utf8_info                    记录字符串的值                 1
CONSTANT_Integer_info                 用于记录int类型的常量值         3
CONSTANT_Float_info                   用于记录float类型的常量值       4
CONSTANT_Long_info                    用于记录long类型的常量值      5
CONSTANT_Double_info                  用于记录double类型的常量值    6
CONSTANT_Class_info                   用于记录类或接口名         7
CONSTANT_String_info                  用于记录常量字符串的值       8
CONSTANT_Fieldref_info                用于记录字段信息(包括类或接口中定义的字段以及代码中使用到的字段) 9
CONSTANT_Methodref_info               用于记录方法信息(包括类中定义的方法以及代码中使用到的方法)    10
CONSTANT_InterfaceMethodref_info     用于记录接口中的方法信息(包括接口中定义的方法以及代码中使用到的方法)11
CONSTANT_NameAndType_info            记录方法或字段的名称(name)和描述符(descriptor)           12
CONSTANT_MethodHandle_info           方法句柄表,1.7增加       15
CONSTANT_MethodType_info             方法类型表,1.7增加        16
CONSTANT_InvokeDynamic_info          动态方法调用点,1.7增加      18

  

上面的表中描述了14种数据类型的结构。接下来我们按照Demo的字节码进行逐一翻译。

0x006A: 由于常量池的数量不固定(n+2),所以需要在常量池的入口处放置一项u2类型的数据代表常量池数量。因此该6A进制是106,表示有105项常量,索引范围为1~105。Class文件格式规定,设计者就讲第0项保留出来了,以备后患。从这里我们知道接下来我们需要翻译出105项常量。

Constant #1 (一共有105个常量,这是第一个,以此类推…)

0x0A:从常量类型表中我们发现,第一个数据均是u1类型的tag,16进制的0a是十进制的10,对应表中的MethodRef_info。

0x001C: 指向方法描述符 CONSTANT_Class_info 的索引项, 为 #28, 查找得: #84           // java/lang/Object

0x0039: 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项, 为 #57, 查找得 #33:#34       // “<init>”:()V

如上,依次计算出每个常量池的范围,直到 105项全部计算完成。

3 Access_Flag 访问标志

Flag Name        Value        Remarks
ACC_PUBLIC        0x0001        pubilc,包外可访问。
ACC_FINAL        0x0010        final,不能有子类。
ACC_SUPER        0x0020    用于兼容早期编译器,新编译器都设置该标记,以在使用 invokespecial指令时对子类方法做特定处理。
ACC_INTERFACE    0x0200        接口,同时需要设置:ACC_ABSTRACT。不可同时设置:ACC_FINAL、ACC_SUPER、ACC_ENUM
ACC_ABSTRACT    0x0400        抽象类,无法实例化。不可和ACC_FINAL同时设置。
ACC_SYNTHETIC     0x1000        synthetic,由编译器产生,不存在于源代码中。     ACC_ANNOTATION     0x2000    注解类型(annotation),需同时设置:ACC_INTERFACE、ACC_ABSTRACT
ACC_ENUM        0x4000        枚举类型

访问标志信息包括该Class文件是类还是接口,是否被定义成public,是否是abstract,如果是类,是否被声明成final。通过上面的源代码,我们知道该文件是类并且是public。

4  this_class

0x0013: this_class是指向constant pool的索引值,该值必须是CONSTANT_Class_info类型,指定当前字节码定义的类或接口。 

5 父类索引 super_class

0x001C: 同理:#28(Class #84 java/lang/Object)

6 接口索引 interfaces

0x0000: 通过java_byte.jpeg图我们知道,这个接口有2+n个字节,前两个字节表示的是接口数量,后面跟着就是接口的表。偏移量为: 4(magic)+4(version)+2+106*(constant)+2(access_flags)+2(this)+2(super)=122*,我们这个类没有任何接口,所以应该是0000。常量池是动态变化的,有点难算,不过算出来就是这个值。此处可以先找到super

7 字段表集合

字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。

同样,接下来就是2+n个字段属性。我们只有一个属性a,按道理应该是0001。查找文件果不其然是0001。

那么接下来我们要针对这样的字段进行解析。附上字段表如下:

type        descriptor            remark
u2            access_flags        记录字段的访问权限。
u2            name_index            constant_pool中的索引,CONSTANT_Utf8_info类型。指定字段的名称。
u2            descriptor_index    constant_pool中的索引,CONSTANT_Utf8_info类型,指定字段的描述符(见附录C)。
u2            attributes_count    attributes包含的项目数。
attribute_info        attributes[attributes_count]

0x00 02: 访问标志为private(自行搜索字段访问标志)

0x00 02: 字段名称索引为#2,对应的是 Methodref          #19.#58       // com/youge/api/ByteCodeClassKen.init:()V

0x001D: 描述符索引为#29,对应的是 Utf8 userService

0x001E :描述符索引为#30,对应的是 Utf8 Lcom/youge/service/user/UserService;

0x00 00 :属性表数量为0,因此没有属性表。

8 方法

我们只有一个方法testMethod,按照道理应该前2个字节是0001。通过查找发现是0x00 02。这是什么原因,这代表着有2个方法呢?且继续看……

上图是一张方法表结构图,按照这个图我们分析下面的字节码:

type        descriptor            remark
u2            access_flags        记录方法的访问权限。见2.9.1
u2            name_index            constant_pool中的索引,CONSTANT_Utf8_info类型。指定方法名称。
u2            descriptor_index    constant_pool中的索引,CONSTANT_Utf8_info类型,指定方法的描述符(见附录C)。
u2            attributes_count    attributes包含的项目数。
attribute_info    attributes[attributes_count]    字段中包含的Attribute集合。

第1个方法:

0x00 01: 访问标志 ACC_PUBLIC,表明该方法是public。(可自行搜索方法访问标志表)

0x00 07: 方法名索引为#7,对应的是”<init>”

0x00 08: 方法描述符索引为#8,对应的是”()V”

0x00 01: 属性表数量为1(一个属性表)

那么这里涉及到了属性表。什么是属性表呢?可以这么理解,它是为了描述一些专有信息的,上面的方法带有一张属性表。所有属性表的结构如下图:

一个u2的属性名称索引,一个u2的属性长度加上属性长度的info。

虚拟机规范预定义的属性有很多,比如Code,LineNumberTable,LocalVariableTable,SourceFile等等,这个网上可以搜索到。

按照上面的表结构解析得到下面信息:

Offset      0  1  2  3  4  5  6  7   8  9 10 11 12 13 14 15


00001344   00 23 00 00 00 3B 00 01  00 01 00 00 00 09 2A B7    #   ;        *?

00001360   00 01 2A B7 00 02 B1 00  00 00 02 00 24 00 00 00     *? ?    $   

00001376   0E 00 03 00 00 00 12 00  04 00 13 00 08 00 14 00                   

00001392   25 00 00 00 0C 00 01 00  00 00 09 00 26 00 27 00   %           & ' 

00001408   00 00 02 00 28 00 22 00  01 00 23 00 00 00 63 00       ( "   #   c 

00001424   03 00 01 00 00 00 20 2A  B4 00 03 9A 00 1B B2 00          *? ? ?

00001440   04 12 05 B6 00 06 2A BB  00 07 59 B7 00 08 B5 00      ? *? Y? ?

00001456   09 2A 04 B5 00 03 B1 00  00 00 03 00 24 00 00 00    * ? ?    $   

00001472   16 00 05 00 00 00 17 00  07 00 18 00 0F 00 19 00                   

00001488   1A 00 1A 00 1F 00 1C 00  25 00 00 00 0C 00 01 00           %       

00001504   00 00 20 00 26 00 27 00  00 00 29 00 00 00 03 00       & '   )     

00001520   01 1F 00 01 00 2A 00 2B  00 02 00 23 00 00 00 6C        * +   #   l

00001536   00 02 00 02 00 00 00 28  BB 00 0A 59 B7 00 0B 4C          (? Y? L

00001552   2B 12 0C B6 00 0D 2B 10  0C B8 00 0E B6 00 0F 2B   +  ? +  ? ? +

00001568   12 10 B6 00 11 2A B4 00  09 2B B9 00 12 02 00 B0     ? *? +?   ?

0x0023:名称索引为#35(“Code”)。

0x0000 003B: 属性长度为59字节。

那么接下来解析一个Code属性表,按照下图解析 Code Attribute:

type    descriptor    remark
u2    attribute_name_index    constant_pool中的索引,CONSTANT_Utf8_info类型。指定Attribute的名称("Code")。
u4    attribute_length    该Attribute内容的字节长度。
u2    max_stack    该方法操作栈的最大深度。
u2    max_locals    该方法调用时需要分配的局部变量的最大个数,包括该方法的参数。
u4    code_length    该方法字节码长度(以字节为单位)
u1    code[code_length]    存放字节码数组(字节码如何解析?)。
u2    exception_table_length    异常表的长度。
exception_table_info    每个表项记录一段异常处理代码信息和范围。
u2    start_pc    记录应用该项异常处理的起始字节码。在字节码数组中的起始索引号[start_pc, end_pc)。索引号必须是opcode(一条指令的开始位置)对应的位置。
u2    end_pc    u2
handler_pc    记录该项异常处理代码的开始地址。在字节码数组中的开始索引号。索引号必须是opcode对应的位置。
u2    catch_type    constant_pool中的索引,CONSTANT_Class_info类型。指定该项能捕获的异常类(或其子类)。或0用于实现finally语法(即不管什么类型都会捕获。)
exception_table[exception_table_length]
u2    attributes_count    attributes包含的项目数。
attribute_info    attributes[attributes_count]

前面6个字节(名称索引2字节+属性长度4字节)已经解析过了,所以接下来就是解析剩下的59-6=53字节即可。

0x00 01: max_stack=1

0x00 01: max_locals=1

0x0000 0009: code_length=9

0x2A B7 00 01 2A B7 00 02 B1: 这是code代码,可以通过虚拟机字节码指令进行查找。

2a=aload_0(将第一个引用变量推送到栈顶)

b7=invokespecial(调用父类构造方法)

00=什么都不做

01=将 #1 常量池推送到栈顶,即默认”<init>”:()V 构造方法

2a=同上

b7=同上

00=什么都不做

02=将#2常量池推送栈顶,即 init() 方法

b1=return 从当前方法返回void

整理,去除无动作指令得到下面

0: aload_0

1: invokespecial #1                  // Method java/lang/Object.”<init>”:()V

4: aload_0

5: invokespecial #2                  // Method init:()V

8: return

接下来顺着Code属性表继续解析下去:

0x00 00 : exception_table_length=0

0x00 02 : attributes_count=2(Code属性表内部还含有2个属性表)

0x00 24: 第一个属性表是”LineNumberTable”

0x00 00 00 0E : “属性长度为14″

0x00 03 :line_number_table_length=3

line_number_table是一个数量为line_number_table_length,类型为line_number_info的集合,line_number_info表包括了start_pc和line_number两个u2类型的数据项,前者是字节码行号,后者是Java源码行号

0x00 00 : start_pc=0

0x00 12 : end_pc=18

0x00 04 : start_pc=4

0x00 13 : end_pc=19

0x00 25 第二个属性表是:”LocalVariableTable”

0x00 0000 0c:属性长度为12

0x00 01 : local_variable_table_length=1

然后按照local_variable_info表结构进行解析:

0x00 00 : start_pc=0

0x00 09:length=9

0x0026 : #38 name_index=”this”

0x0027 : #39 descriptor_index #13 (“Lcom/youge/api/ByteCodeClassKen;”)

0000 index=0

———–到这里第一个方法就解析完成了——————–

Method(<init>)–1个属性Code表-2个属性表(LineNumberTable ,LocalVariableTable)

10 Attribute

attributes数组记录了和类或接口相关的所有Attribute项(和字段相关的Attribute在field_info的attributes中,和方法相关的Attribute在method_info的attrubutes中,和字节码相关的Attribute在Code Attribute的attributes中)。attributes数组中的每项都是attribute_info类型,它描述了Attribute的名称、详细信息等。该attributes数组描述了ClassFile的一些额外信息。JVM必须忽略它不能识别的Attribute,而且那些JVM不能识别的的Attribute也不能影响class文件的语义。

当前定义的Attribute有:

Code Attribute、Constant Value Attibute、Deprecated Attribute、Enclosing Method Attribute、Exceptions Attribute、Inner Classes Attribute、Line Number Table Attribute、Local Variable Table Attribute、Local Variable Type Table Attribute、Runtime Visible Annotations Attribute、Runtime Invisible Annotation Attribute、Runtime Visible Parameter Annotation Attribute、Runtime Invisible Parameter Annotation Attribute、Signature Attribute、Source Debug Extension Attribute、Source File Attribute、Stack Map Table Attribute、Synthetic Attribute、Annotation Default Attribute等。

它们有些只存在于field_info中,有些只存在method_info中,有些只存在ClassFile中,有些只存在于Code Attribute中,还有些可以同时存在于field_info、method_info、classfile中。

Attribute结构只存在与ClassFile、method_info、field_info、Code Attribute结构中。

0x0002 :同样的,表示有2个Attributes了。

0x0037 : #15(“SourceFile”)

0x0000 0002 attribute_length=2

0x0038 : sourcefile_index = #56(“ByteCodeClassKen.java”)

SourceFile属性用来记录生成该Class文件的源码文件名称。

11. 直接使用 javap 反编译成可读的文本型字节码

javap -verbose xxx.class

Classfile /D:/www/test/target/classes/com/youge/api/ByteCodeClassKen.class
  Last modified 2018-9-26; size 1801 bytes
  MD5 checksum 76e207e502c3b096346480457d98cdef
  Compiled from "ByteCodeClassKen.java"
public class com.youge.api.ByteCodeClassKen
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
    #1 = Methodref          #28.#57       // java/lang/Object."<init>":()V
    #2 = Methodref          #19.#58       // com/youge/api/ByteCodeClassKen.init:()V
    #3 = Fieldref           #19.#59       // com/youge/api/ByteCodeClassKen.initialed:Z
    #4 = Fieldref           #60.#61       // java/lang/System.out:Ljava/io/PrintStream;
    #5 = String             #62           // init...
    #6 = Methodref          #63.#64       // java/io/PrintStream.println:(Ljava/lang/String;)V
    #7 = Class              #65           // com/youge/service/user/UserServiceImpl
    #8 = Methodref          #7.#57        // com/youge/service/user/UserServiceImpl."<init>":()V
    #9 = Fieldref           #19.#66       // com/youge/api/ByteCodeClassKen.userService:Lcom/youge/service/user/UserService;
   #10 = Class              #67           // com/youge/pojo/user/UserInfo
   #11 = Methodref          #10.#57       // com/youge/pojo/user/UserInfo."<init>":()V
   #12 = String             #68           // lilei
   #13 = Methodref          #10.#69       // com/youge/pojo/user/UserInfo.setName:(Ljava/lang/String;)V
   #14 = Methodref          #70.#71       // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #15 = Methodref          #10.#72       // com/youge/pojo/user/UserInfo.setAge:(Ljava/lang/Integer;)V
   #16 = String             #73           // new road.
   #17 = Methodref          #10.#74       // com/youge/pojo/user/UserInfo.setAddress:(Ljava/lang/String;)V
   #18 = InterfaceMethodref #75.#76       // com/youge/service/user/UserService.addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer;
   #19 = Class              #77           // com/youge/api/ByteCodeClassKen
   #20 = Methodref          #19.#57       // com/youge/api/ByteCodeClassKen."<init>":()V
   #21 = Methodref          #19.#78       // com/youge/api/ByteCodeClassKen.addUser:()Ljava/lang/Integer;
   #22 = Class              #79           // java/lang/StringBuilder
   #23 = Methodref          #22.#57       // java/lang/StringBuilder."<init>":()V
   #24 = String             #80           // affect:
   #25 = Methodref          #22.#81       // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #26 = Methodref          #22.#82       // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
   #27 = Methodref          #22.#83       // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #28 = Class              #84           // java/lang/Object
   #29 = Utf8               userService
   #30 = Utf8               Lcom/youge/service/user/UserService;
   #31 = Utf8               initialed
   #32 = Utf8               Z
   #33 = Utf8               <init>
   #34 = Utf8               ()V
   #35 = Utf8               Code
   #36 = Utf8               LineNumberTable
   #37 = Utf8               LocalVariableTable
   #38 = Utf8               this
   #39 = Utf8               Lcom/youge/api/ByteCodeClassKen;
   #40 = Utf8               init
   #41 = Utf8               StackMapTable
   #42 = Utf8               addUser
   #43 = Utf8               ()Ljava/lang/Integer;
   #44 = Utf8               userInfo
   #45 = Utf8               Lcom/youge/pojo/user/UserInfo;
   #46 = Utf8               Exceptions
   #47 = Class              #85           // java/lang/Exception
   #48 = Utf8               main
   #49 = Utf8               ([Ljava/lang/String;)V
   #50 = Utf8               args
   #51 = Utf8               [Ljava/lang/String;
   #52 = Utf8               classKen
   #53 = Utf8               affect
   #54 = Utf8               Ljava/lang/Integer;
   #55 = Utf8               SourceFile
   #56 = Utf8               ByteCodeClassKen.java
   #57 = NameAndType        #33:#34       // "<init>":()V
   #58 = NameAndType        #40:#34       // init:()V
   #59 = NameAndType        #31:#32       // initialed:Z
   #60 = Class              #86           // java/lang/System
   #61 = NameAndType        #87:#88       // out:Ljava/io/PrintStream;
   #62 = Utf8               init...
   #63 = Class              #89           // java/io/PrintStream
   #64 = NameAndType        #90:#91       // println:(Ljava/lang/String;)V
   #65 = Utf8               com/youge/service/user/UserServiceImpl
   #66 = NameAndType        #29:#30       // userService:Lcom/youge/service/user/UserService;
   #67 = Utf8               com/youge/pojo/user/UserInfo
   #68 = Utf8               lilei
   #69 = NameAndType        #92:#91       // setName:(Ljava/lang/String;)V
   #70 = Class              #93           // java/lang/Integer
   #71 = NameAndType        #94:#95       // valueOf:(I)Ljava/lang/Integer;
   #72 = NameAndType        #96:#97       // setAge:(Ljava/lang/Integer;)V
   #73 = Utf8               new road.
   #74 = NameAndType        #98:#91       // setAddress:(Ljava/lang/String;)V
   #75 = Class              #99           // com/youge/service/user/UserService
   #76 = NameAndType        #42:#100      // addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer;
   #77 = Utf8               com/youge/api/ByteCodeClassKen
   #78 = NameAndType        #42:#43       // addUser:()Ljava/lang/Integer;
   #79 = Utf8               java/lang/StringBuilder
   #80 = Utf8               affect:
   #81 = NameAndType        #101:#102     // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #82 = NameAndType        #101:#103     // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
   #83 = NameAndType        #104:#105     // toString:()Ljava/lang/String;
   #84 = Utf8               java/lang/Object
   #85 = Utf8               java/lang/Exception
   #86 = Utf8               java/lang/System
   #87 = Utf8               out
   #88 = Utf8               Ljava/io/PrintStream;
   #89 = Utf8               java/io/PrintStream
   #90 = Utf8               println
   #91 = Utf8               (Ljava/lang/String;)V
   #92 = Utf8               setName
   #93 = Utf8               java/lang/Integer
   #94 = Utf8               valueOf
   #95 = Utf8               (I)Ljava/lang/Integer;
   #96 = Utf8               setAge
   #97 = Utf8               (Ljava/lang/Integer;)V
   #98 = Utf8               setAddress
   #99 = Utf8               com/youge/service/user/UserService
  #100 = Utf8               (Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer;
  #101 = Utf8               append
  #102 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #103 = Utf8               (Ljava/lang/Object;)Ljava/lang/StringBuilder;
  #104 = Utf8               toString
  #105 = Utf8               ()Ljava/lang/String;
{
  public com.youge.api.ByteCodeClassKen();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: invokespecial #2                  // Method init:()V
         8: return
      LineNumberTable:
        line 18: 0
        line 19: 4
        line 20: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/youge/api/ByteCodeClassKen;

  public java.lang.Integer addUser() throws java.lang.Exception;
    descriptor: ()Ljava/lang/Integer;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #10                 // class com/youge/pojo/user/UserInfo
         3: dup
         4: invokespecial #11                 // Method com/youge/pojo/user/UserInfo."<init>":()V
         7: astore_1
         8: aload_1
         9: ldc           #12                 // String lilei
        11: invokevirtual #13                 // Method com/youge/pojo/user/UserInfo.setName:(Ljava/lang/String;)V
        14: aload_1
        15: bipush        12
        17: invokestatic  #14                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        20: invokevirtual #15                 // Method com/youge/pojo/user/UserInfo.setAge:(Ljava/lang/Integer;)V
        23: aload_1
        24: ldc           #16                 // String new road.
        26: invokevirtual #17                 // Method com/youge/pojo/user/UserInfo.setAddress:(Ljava/lang/String;)V
        29: aload_0
        30: getfield      #9                  // Field userService:Lcom/youge/service/user/UserService;
        33: aload_1
        34: invokeinterface #18,  2           // InterfaceMethod com/youge/service/user/UserService.addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer;
        39: areturn
      LineNumberTable:
        line 31: 0
        line 32: 8
        line 33: 14
        line 34: 23
        line 35: 29
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      40     0  this   Lcom/youge/api/ByteCodeClassKen;
            8      32     1 userInfo   Lcom/youge/pojo/user/UserInfo;
    Exceptions:
      throws java.lang.Exception

  public static void main(java.lang.String[]) throws java.lang.Exception;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: new           #19                 // class com/youge/api/ByteCodeClassKen
         3: dup
         4: invokespecial #20                 // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #21                 // Method addUser:()Ljava/lang/Integer;
        12: astore_2
        13: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: new           #22                 // class java/lang/StringBuilder
        19: dup
        20: invokespecial #23                 // Method java/lang/StringBuilder."<init>":()V
        23: ldc           #24                 // String affect:
        25: invokevirtual #25                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        28: aload_2
        29: invokevirtual #26                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
        32: invokevirtual #27                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        35: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        38: return
      LineNumberTable:
        line 40: 0
        line 41: 8
        line 42: 13
        line 43: 38
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      39     0  args   [Ljava/lang/String;
            8      31     1 classKen   Lcom/youge/api/ByteCodeClassKen;
           13      26     2 affect   Ljava/lang/Integer;
    Exceptions:
      throws java.lang.Exception
}
SourceFile: "ByteCodeClassKen.java"

    字节码阅读或者简单反编译的字节码阅读能力,不是排查问题所必须的,但是,如果能够顺手读懂这些语言,无疑会给自己带来莫大的方便。另外,也能够更深层理解你写的代码本来的样子,从而从另一个角度发现不一样的自己。

 后记,java字节码指令表速查:


### java字节码指令列表

字节码    助记符    指令含义
0x00    nop    什么都不做
0x01    aconst_null    将null推送至栈顶
0x02    iconst_m1    将int型-1推送至栈顶
0x03    iconst_0    将int型0推送至栈顶
0x04    iconst_1    将int型1推送至栈顶
0x05    iconst_2    将int型2推送至栈顶
0x06    iconst_3    将int型3推送至栈顶
0x07    iconst_4    将int型4推送至栈顶
0x08    iconst_5    将int型5推送至栈顶
0x09    lconst_0    将long型0推送至栈顶
0x0a    lconst_1    将long型1推送至栈顶
0x0b    fconst_0    将float型0推送至栈顶
0x0c    fconst_1    将float型1推送至栈顶
0x0d    fconst_2    将float型2推送至栈顶
0x0e    dconst_0    将do le型0推送至栈顶
0x0f    dconst_1    将do le型1推送至栈顶
0x10    bipush    将单字节的常量值(-128~127)推送至栈顶
0x11    sipush    将一个短整型常量值(-32768~32767)推送至栈顶
0x12    ldc    将int, float或String型常量值从常量池中推送至栈顶
0x13    ldc_w    将int, float或String型常量值从常量池中推送至栈顶(宽索引)
0x14    ldc2_w    将long或do le型常量值从常量池中推送至栈顶(宽索引)
0x15    iload    将指定的int型本地变量
0x16    lload    将指定的long型本地变量
0x17    fload    将指定的float型本地变量
0x18    dload    将指定的do le型本地变量
0x19    aload    将指定的引用类型本地变量
0x1a    iload_0    将第一个int型本地变量
0x1b    iload_1    将第二个int型本地变量
0x1c    iload_2    将第三个int型本地变量
0x1d    iload_3    将第四个int型本地变量
0x1e    lload_0    将第一个long型本地变量
0x1f    lload_1    将第二个long型本地变量
0x20    lload_2    将第三个long型本地变量
0x21    lload_3    将第四个long型本地变量
0x22    fload_0    将第一个float型本地变量
0x23    fload_1    将第二个float型本地变量
0x24    fload_2    将第三个float型本地变量
0x25    fload_3    将第四个float型本地变量
0x26    dload_0    将第一个do le型本地变量
0x27    dload_1    将第二个do le型本地变量
0x28    dload_2    将第三个do le型本地变量
0x29    dload_3    将第四个do le型本地变量
0x2a    aload_0    将第一个引用类型本地变量
0x2b    aload_1    将第二个引用类型本地变量
0x2c    aload_2    将第三个引用类型本地变量
0x2d    aload_3    将第四个引用类型本地变量
0x2e    iaload    将int型数组指定索引的值推送至栈顶
0x2f    laload    将long型数组指定索引的值推送至栈顶
0x30    faload    将float型数组指定索引的值推送至栈顶
0x31    daload    将do le型数组指定索引的值推送至栈顶
0x32    aaload    将引用型数组指定索引的值推送至栈顶
0x33    baload    将boolean或byte型数组指定索引的值推送至栈顶
0x34    caload    将char型数组指定索引的值推送至栈顶
0x35    saload    将short型数组指定索引的值推送至栈顶
0x36    istore    将栈顶int型数值存入指定本地变量
0x37    lstore    将栈顶long型数值存入指定本地变量
0x38    fstore    将栈顶float型数值存入指定本地变量
0x39    dstore    将栈顶do le型数值存入指定本地变量
0x3a    astore    将栈顶引用型数值存入指定本地变量
0x3b    istore_0    将栈顶int型数值存入第一个本地变量
0x3c    istore_1    将栈顶int型数值存入第二个本地变量
0x3d    istore_2    将栈顶int型数值存入第三个本地变量
0x3e    istore_3    将栈顶int型数值存入第四个本地变量
0x3f    lstore_0    将栈顶long型数值存入第一个本地变量
0x40    lstore_1    将栈顶long型数值存入第二个本地变量
0x41    lstore_2    将栈顶long型数值存入第三个本地变量
0x42    lstore_3    将栈顶long型数值存入第四个本地变量
0x43    fstore_0    将栈顶float型数值存入第一个本地变量
0x44    fstore_1    将栈顶float型数值存入第二个本地变量
0x45    fstore_2    将栈顶float型数值存入第三个本地变量
0x46    fstore_3    将栈顶float型数值存入第四个本地变量
0x47    dstore_0    将栈顶do le型数值存入第一个本地变量
0x48    dstore_1    将栈顶do le型数值存入第二个本地变量
0x49    dstore_2    将栈顶do le型数值存入第三个本地变量
0x4a    dstore_3    将栈顶do le型数值存入第四个本地变量
0x4b    astore_0    将栈顶引用型数值存入第一个本地变量
0x4c    astore_1    将栈顶引用型数值存入第二个本地变量
0x4d    astore_2    将栈顶引用型数值存入第三个本地变量
0x4e    astore_3    将栈顶引用型数值存入第四个本地变量
0x4f    iastore    将栈顶int型数值存入指定数组的指定索引位置
0x50    lastore    将栈顶long型数值存入指定数组的指定索引位置
0x51    fastore    将栈顶float型数值存入指定数组的指定索引位置
0x52    dastore    将栈顶do le型数值存入指定数组的指定索引位置
0x53    aastore    将栈顶引用型数值存入指定数组的指定索引位置
0x54    bastore    将栈顶boolean或byte型数值存入指定数组的指定索引位置
0x55    castore    将栈顶char型数值存入指定数组的指定索引位置
0x56    sastore    将栈顶short型数值存入指定数组的指定索引位置
0x57    pop    将栈顶数值弹出 (数值不能是long或do le类型的)
0x58    pop2    将栈顶的一个(long或do le类型的)或两个数值弹出(其它)
0x59    dup    复制栈顶数值并将复制值压入栈顶
0x5a    dup_x1    复制栈顶数值并将两个复制值压入栈顶
0x5b    dup_x2    复制栈顶数值并将三个(或两个)复制值压入栈顶
0x5c    dup2    复制栈顶一个(long或do le类型的)或两个(其它)数值并将复制值压入栈顶
0x5d    dup2_x1    dup_x1 指令的双倍版本
0x5e    dup2_x2    dup_x2 指令的双倍版本
0x5f    swap    将栈最顶端的两个数值互换(数值不能是long或do le类型的)
0x60    iadd    将栈顶两int型数值相加并将结果压入栈顶
0x61    ladd    将栈顶两long型数值相加并将结果压入栈顶
0x62    fadd    将栈顶两float型数值相加并将结果压入栈顶
0x63    dadd    将栈顶两do le型数值相加并将结果压入栈顶
0x64    is    将栈顶两int型数值相减并将结果压入栈顶
0x65    ls    将栈顶两long型数值相减并将结果压入栈顶
0x66    fs    将栈顶两float型数值相减并将结果压入栈顶
0x67    ds    将栈顶两do le型数值相减并将结果压入栈顶
0x68    imul    将栈顶两int型数值相乘并将结果压入栈顶
0x69    lmul    将栈顶两long型数值相乘并将结果压入栈顶
0x6a    fmul    将栈顶两float型数值相乘并将结果压入栈顶
0x6b    dmul    将栈顶两do le型数值相乘并将结果压入栈顶
0x6c    idiv    将栈顶两int型数值相除并将结果压入栈顶
0x6d    ldiv    将栈顶两long型数值相除并将结果压入栈顶
0x6e    fdiv    将栈顶两float型数值相除并将结果压入栈顶
0x6f    ddiv    将栈顶两do le型数值相除并将结果压入栈顶
0x70    irem    将栈顶两int型数值作取模运算并将结果压入栈顶
0x71    lrem    将栈顶两long型数值作取模运算并将结果压入栈顶
0x72    frem    将栈顶两float型数值作取模运算并将结果压入栈顶
0x73    drem    将栈顶两do le型数值作取模运算并将结果压入栈顶
0x74    ineg    将栈顶int型数值取负并将结果压入栈顶
0x75    lneg    将栈顶long型数值取负并将结果压入栈顶
0x76    fneg    将栈顶float型数值取负并将结果压入栈顶
0x77    dneg    将栈顶do le型数值取负并将结果压入栈顶
0x78    ishl    将int型数值左移位指定位数并将结果压入栈顶
0x79    lshl    将long型数值左移位指定位数并将结果压入栈顶
0x7a    ishr    将int型数值右(符号)移位指定位数并将结果压入栈顶
0x7b    lshr    将long型数值右(符号)移位指定位数并将结果压入栈顶
0x7c    iushr    将int型数值右(无符号)移位指定位数并将结果压入栈顶
0x7d    lushr    将long型数值右(无符号)移位指定位数并将结果压入栈顶
0x7e    iand    将栈顶两int型数值作“按位与”并将结果压入栈顶
0x7f    land    将栈顶两long型数值作“按位与”并将结果压入栈顶
0x80    ior    将栈顶两int型数值作“按位或”并将结果压入栈顶
0x81    lor    将栈顶两long型数值作“按位或”并将结果压入栈顶
0x82    ixor    将栈顶两int型数值作“按位异或”并将结果压入栈顶
0x83    lxor    将栈顶两long型数值作“按位异或”并将结果压入栈顶
0x84    iinc    将指定int型变量增加指定值(i++, i–, i+=2)
0x85    i2l    将栈顶int型数值强制转换成long型数值并将结果压入栈顶
0x86    i2f    将栈顶int型数值强制转换成float型数值并将结果压入栈顶
0x87    i2d    将栈顶int型数值强制转换成do le型数值并将结果压入栈顶
0x88    l2i    将栈顶long型数值强制转换成int型数值并将结果压入栈顶
0x89    l2f    将栈顶long型数值强制转换成float型数值并将结果压入栈顶
0x8a    l2d    将栈顶long型数值强制转换成do le型数值并将结果压入栈顶
0x8b    f2i    将栈顶float型数值强制转换成int型数值并将结果压入栈顶
0x8c    f2l    将栈顶float型数值强制转换成long型数值并将结果压入栈顶
0x8d    f2d    将栈顶float型数值强制转换成do le型数值并将结果压入栈顶
0x8e    d2i    将栈顶do le型数值强制转换成int型数值并将结果压入栈顶
0x8f    d2l    将栈顶do le型数值强制转换成long型数值并将结果压入栈顶
0x90    d2f    将栈顶do le型数值强制转换成float型数值并将结果压入栈顶
0x91    i2b    将栈顶int型数值强制转换成byte型数值并将结果压入栈顶
0x92    i2c    将栈顶int型数值强制转换成char型数值并将结果压入栈顶
0x93    i2s    将栈顶int型数值强制转换成short型数值并将结果压入栈顶
0x94    lcmp    比较栈顶两long型数值大小,并将结果(1,0,-1)压入栈顶
0x95    fcmpl    比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x96    fcmpg    比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x97    dcmpl    比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x98    dcmpg    比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x99    ifeq    当栈顶int型数值等于0时跳转
0x9a    ifne    当栈顶int型数值不等于0时跳转
0x9b    iflt    当栈顶int型数值小于0时跳转
0x9c    ifge    当栈顶int型数值大于等于0时跳转
0x9d    ifgt    当栈顶int型数值大于0时跳转
0x9e    ifle    当栈顶int型数值小于等于0时跳转
0x9f    if_icmpeq    比较栈顶两int型数值大小,当结果等于0时跳转
0xa0    if_icmpne    比较栈顶两int型数值大小,当结果不等于0时跳转
0xa1    if_icmplt    比较栈顶两int型数值大小,当结果小于0时跳转
0xa2    if_icmpge    比较栈顶两int型数值大小,当结果大于等于0时跳转
0xa3    if_icmpgt    比较栈顶两int型数值大小,当结果大于0时跳转
0xa4    if_icmple    比较栈顶两int型数值大小,当结果小于等于0时跳转
0xa5    if_acmpeq    比较栈顶两引用型数值,当结果相等时跳转
0xa6    if_acmpne    比较栈顶两引用型数值,当结果不相等时跳转
0xa7    goto    无条件跳转
0xa8    jsr    跳转至指定16位offset位置,并将jsr下一条指令地址压入栈顶
0xa9    ret    返回至本地变量
0xaa    tableswitch    用于switch条件跳转,case值连续(可变长度指令)
0xab    lookupswitch    用于switch条件跳转,case值不连续(可变长度指令)
0xac    ireturn    从当前方法返回int
0xad    lreturn    从当前方法返回long
0xae    freturn    从当前方法返回float
0xaf    dreturn    从当前方法返回do le
0xb0    areturn    从当前方法返回对象引用
0xb1    return    从当前方法返回void
0xb2    getstatic    获取指定类的静态域,并将其值压入栈顶
0xb3    putstatic    为指定的类的静态域赋值
0xb4    getfield    获取指定类的实例域,并将其值压入栈顶
0xb5    putfield    为指定的类的实例域赋值
0xb6    invokevirtual    调用实例方法
0xb7    invokespecial    调用超类构造方法,实例初始化方法,私有方法
0xb8    invokestatic    调用静态方法
0xb9    invokeinterface    调用接口方法
0xba    –    无此指令
0xbb    new    创建一个对象,并将其引用值压入栈顶
0xbc    newarray    创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶
0xbd    anewarray    创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶
0xbe    arraylength    获得数组的长度值并压入栈顶
0xbf    athrow    将栈顶的异常抛出
0xc0    checkcast    检验类型转换,检验未通过将抛出ClassCastException
0xc1    instanceof    检验对象是否是指定的类的实例,如果是将1压入栈顶,否则将0压入栈顶
0xc2    monitorenter    获得对象的锁,用于同步方法或同步块
0xc3    monitorexit    释放对象的锁,用于同步方法或同步块
0xc4    wide    <待补充>
0xc5    multianewarray    创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶
0xc6    ifnull    为null时跳转
0xc7    ifnonnull    不为null时跳转
0xc8    goto_w    无条件跳转(宽索引)
0xc9    jsr_w    跳转至指定32位offset位置,并将jsr_w下一条指令地址压入栈顶

分类助词符:


JVM指令助记符
变量到操作数栈:iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_ 
操作数栈到变量:istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstor_,astore,astore_ 
常数到操作数栈:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_ 
加:iadd,ladd,fadd,dadd 
减:is ,ls ,fs ,ds 
乘:imul,lmul,fmul,dmul 
除:idiv,ldiv,fdiv,ddiv 
余数:irem,lrem,frem,drem 
取负:ineg,lneg,fneg,dneg 
移位:ishl,lshr,iushr,lshl,lshr,lushr 
按位或:ior,lor 
按位与:iand,land 
按位异或:ixor,lxor 
类型转换:i2l,i2f,i2d,l2f,l2d,f2d(放宽数值转换) 
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(缩窄数值转换)

创建类实便:new 
创建新数组:newarray,anewarray,multianwarray 
访问类的域和类实例域:getfield,putfield,getstatic,putstatic 
把数据装载到操作数栈:baload,caload,saload,iaload,laload,faload,daload,aaload 
从操作数栈存存储到数组:bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore 
获取数组长度:arraylength 
检相类实例或数组属性:instanceof,checkcast 
操作数栈管理:pop,pop2,dup,dup2,dup_xl,dup2_xl,dup_x2,dup2_x2,swap 
有条件转移:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpene, 
if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne,lcmp,fcmpl 
fcmpg,dcmpl,dcmpg 
复合条件转移:tableswitch,lookupswitch 
无条件转移:goto,goto_w,jsr,jsr_w,ret 
调度对象的实便方法:invokevirt l 
调用由接口实现的方法:invokeinterface 
调用需要特殊处理的实例方法:invokespecial 
调用命名类中的静态方法:invokestatic 
方法返回:ireturn,lreturn,freturn,dreturn,areturn,return 
异常:athrow 
finally关键字的实现使用:jsr,jsr_w,ret

 

作者:等你归去来