Java 字节码

Java 字节码文件格式

首先了解一下 Java class 文件结构信息:

ClassFile {
    u4             magic;
    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];
}

Java 字节码信息说明

名称 说明
magic 魔数,用于说明这是一个 Java 字节码文件,固定值 0xCAFEBABE
minor_version, major_version 字节码文件版本,决定了文件的实际格式信息,对于 Java 1.2 之后版本 Java X,major_version = 44 + X
constant_pool_count,constant_pool 常量池信息,常量池索引从 1 到 constant_pool_count – 1
access_flags 类的访问标识
this_class,super_class,interfaces_count,interfaces 类、父类、接口信息,为常量池中索引。super_class 索引为0,则标示为 Object 类。非零,则此类及父类访问标识不能为 ACC_FINAL
fields_count,fields 字段信息
methods_count,methods 方法信息
attributes_count,attributes 属性信息,比如源文件名称、行号信息、注解、异常等(详细

数据结构说明

数据结构

cp_info {
    u1 tag;
    u1 info[];
}
field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}

常量信息tag含义

常量信息中 tag 标识此信息类型,具体如下:

Constant Type Value
CONSTANT_Class 7
CONSTANT_Fieldref 9
CONSTANT_Methodref 10
CONSTANT_InterfaceMethodref 11
CONSTANT_String 8
CONSTANT_Integer 3
CONSTANT_Float 4
CONSTANT_Long 5
CONSTANT_Double 6
CONSTANT_NameAndType 12
CONSTANT_Utf8 1
CONSTANT_MethodHandle 15
CONSTANT_MethodType 16
CONSTANT_InvokeDynamic 18

access_flag 对应类型

Flag Name Value Interpretation
ACC_PUBLIC 0x0001 Marked or implicitly public in source.
ACC_PRIVATE 0x0002 Marked private in source.
ACC_PROTECTED 0x0004 Marked protected in source.
ACC_STATIC 0x0008 Marked or implicitly static in source.
ACC_FINAL 0x001 Marked final in source.
ACC_INTERFACE 0x0200 Was an interface in source.
ACC_ABSTRACT 0x0400 Marked or implicitly abstract in source.
ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.
ACC_ANNOTATION 0x2000 Declared as an annotation type.
ACC_ENUM 0x4000 Declared as an enum type.

示例

Java 代码

创建目录 com/dezng,其中新建文件 Hello.java,文件中内容:

package com.dezng;

@Deprecated
public class Hello {
    public static void main(String[] args){
        System.out.println("Hello");
    }
}
javac com/dezng/Hello.java # 编译成功得到 Hello.class 文件
java com.dezng.Hello # 执行输出 Hello 字符

Javap 查看字节码内容

使用 javap -v com.dezng.Hello

Classfile /tmp/000/com/dezng/Hello.class
  Last modified 2021-7-2; size 503 bytes
  MD5 checksum 9b9bcbe922b43b9e99d78dd059a14565
  Compiled from "Hello.java"
public class com.dezng.Hello
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#18         // java/lang/Object."<init>":()V
   #2 = Fieldref           #19.#20        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #21            // Hello
   #4 = Methodref          #22.#23        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #24            // com/dezng/Hello
   #6 = Class              #25            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               SourceFile
  #14 = Utf8               Hello.java
  #15 = Utf8               Deprecated
  #16 = Utf8               RuntimeVisibleAnnotations
  #17 = Utf8               Ljava/lang/Deprecated;
  #18 = NameAndType        #7:#8          // "<init>":()V
  #19 = Class              #26            // java/lang/System
  #20 = NameAndType        #27:#28        // out:Ljava/io/PrintStream;
  #21 = Utf8               Hello
  #22 = Class              #29            // java/io/PrintStream
  #23 = NameAndType        #30:#31        // println:(Ljava/lang/String;)V
  #24 = Utf8               com/dezng/Hello
  #25 = Utf8               java/lang/Object
  #26 = Utf8               java/lang/System
  #27 = Utf8               out
  #28 = Utf8               Ljava/io/PrintStream;
  #29 = Utf8               java/io/PrintStream
  #30 = Utf8               println
  #31 = Utf8               (Ljava/lang/String;)V
{
  public com.dezng.Hello();
    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: return
      LineNumberTable:
        line 4: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String Hello
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
}
SourceFile: "Hello.java"
Deprecated: true
RuntimeVisibleAnnotations:
  0: #17()

二进制信息

查看对应二进制信息
二进制信息与标注
对比 javap -v命令结果查看以上信息
4 段绿色分别对应了 魔术,小版本,主版本(0x34=52,对应 Java 8),常量池数量(32=0x20,对应 javap 中 #1-#31的常量信息)
31 段红蓝交替着色对应了 #1-#31 的常量信息
6 段绿色对应了 访问权限(0x21, ACC_PUBLIC, ACC_SUPER),类信息(#5 com/dezng/Hello),父类信息(#6 Object),接口数量(0),字段数量(0),方法数量(2)
2 段红蓝信息对应方法内容
1 段绿色对应属性数量(3)
3 段红蓝交替对应属性信息

方法二进制解读

以方法区第二个方法查看方法结构,再来看看方法对应数据结构:

method_info {
    u2             access_flags; // 0x0009 -> ACC_STATIC,ACC_FINAL
    u2             name_index; // 0x000b -> main
    u2             descriptor_index; // 0x000c -> ([Ljava/lang/String;)V
    u2             attributes_count; // 0x0001
    attribute_info attributes[attributes_count]; 
}
attribute_info {
    u2 attribute_name_index; // 0x0009 -> Code
    u4 attribute_length; // 0x00000025 -> 37
    u1 info[attribute_length];
}
// 根据类型查看结构
Code_attribute {
    u2 attribute_name_index; // 0x0009 -> Code
    u4 attribute_length; // 0x00000025 -> 37
    u2 max_stack; // 0x0002
    u2 max_locals; // 0x0001
    u4 code_length; // 0x00000009
    u1 code[code_length]; // 0xb20002 1203 b60004 b1
    u2 exception_table_length;0x0000
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count; // 0x0001
    attribute_info attributes[attributes_count];// 0x000a -> LineNumberTable
}
LineNumberTable_attribute {
    u2 attribute_name_index; // 0x000a -> LineNumberTable
    u4 attribute_length; // 0x0000000a
    u2 line_number_table_length; // 0x0002
    {   u2 start_pc; // 0x0000,0x0008
        u2 line_number;	// 0x0006,0x0007
    } line_number_table[line_number_table_length];
}

文字版:
方法名称:main
方法访问属性:static final
方法参数信息:参数 (String[]),返回 void
方法属性数量:1
方法属性类型:Code
方法属性长度:37(接下来37字节为方法 Code 具体内容,如下)
最大栈:2
本地变量:1
代码长度:9(接下来9字节为字节码指令)
指令内容:0xb20002 1203 b60004 b1(此 9 字节稍后分析)
异常表长度:0
Code属性数量:1
属性类型:LineNumberTable
此部分属性长度:10
此部分属性数量:2
指令位置:0 行号:6
指令位置:8 行号:7
结束

Java 字节码指令

0xb200021203b60004b1 以此9字节做一个简单解读。

0xb2 查文档可知为 getstatic 指令,后接两字节(0x0002)标示常量池中的索引,此索引内容为 Fieldref(java/lang/System.out:Ljava/io/PrintStream;),并将结果放回操作数栈。
0x12 查文档可知为 ldc 指令,后接一个字节(0x03)表示常量池中索引,此索引内容为String(Hello)。指令将代表此常量的引用放到操作数栈。
0xb6 插文档可知为 invokevirtual 指令,后接两字节(0x0004)表示常量池中的索引,此索引的内容为Methodref,指令会调用此方法,将结果放入操作数栈。
0xb1 查文档可知为 return 指令,并且是返回 void,根据方法是否为 synchronized 处理锁,销毁操作数栈和方法帧,接下来由调用者处理。

以上指令即为 main 方法中内容,和 javap 中查看到的 main 方法内容一致。

内容太多了,到此为止,需要了解再补充,主要就是看官方文档。

参考链接

Jvm 规范文档 Oracle
字节码列表 Wiki
字节码文档 Oracle


版权声明:本文为O_O___O_O原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/O_O___O_O/article/details/118421632