JVM规范阅读:徒手解析class文件

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 源码

/**
 * @author Rubin
 * @version v1 2020/7/30 11:05
 */
public class Main {

    double doubleLocals(double d1, double d2) {
        return d1 + d2;
    }

}

javap 查看字节码

javap -c .\Main.class

Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  double doubleLocals(double, double);
    Code:
       0: dload_1
       1: dload_3
       2: dadd
       3: dreturn
}

javap -verbose .\Main.class

Classfile /D:/Java/IdeaProjects/ieacs/target/classes/Main.class
  Last modified 202084; size 388 bytes
  MD5 checksum 9dbf204007d5615fb43c9c0e03b1d1f5
  Compiled from "Main.java"
class Main
  minor version: 0
  major version: 52
  flags: (0x0020) ACC_SUPER
  this_class: #2                          // Main
  super_class: #3                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
   #1 = Methodref          #3.#19         // java/lang/Object."<init>":()V
   #2 = Class              #20            // Main
   #3 = Class              #21            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               LMain;
  #11 = Utf8               doubleLocals
  #12 = Utf8               (DD)D
  #13 = Utf8               d1
  #14 = Utf8               D
  #15 = Utf8               d2
  #16 = Utf8               MethodParameters
  #17 = Utf8               SourceFile
  #18 = Utf8               Main.java
  #19 = NameAndType        #4:#5          // "<init>":()V
  #20 = Utf8               Main
  #21 = Utf8               java/lang/Object
{
  Main();
    descriptor: ()V
    flags: (0x0000)
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  double doubleLocals(double, double);
    descriptor: (DD)D
    flags: (0x0000)
    Code:
      stack=4, locals=5, args_size=3
         0: dload_1
         1: dload_3
         2: dadd
         3: dreturn
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  this   LMain;
            0       4     1    d1   D
            0       4     3    d2   D
    MethodParameters:
      Name                           Flags
      d1
      d2
}
SourceFile: "Main.java"

解析ing…

出现的指令

指令意思
aload_0从局部变量加载一个 reference 类型值到操作数栈25(0x19)
invokespecial #1调用实例方法,专门用来调用父类方法、私有方法和实例初始化方法183(0xb7)
return方法中返回 void177(0xb1)
dload_1从局部变量加载一个 double 类型值到操作数栈中24(0x18)
dload_3从局部变量加载一个 double 类型值到操作数栈中24(0x18)
dadddouble 类型数据相加99(0x63)
dreturn从方法中返回一个 double 类型数据175(0xaf)

Class 文件解析

常量池结构

cp_info {
    u1 tag;
    u1 info[];
}

常量池只包含本身类的常量,不包含父类。最大数量 65,535

常量池的常量分两类,一是有所属类或接口的,如类的方法、字段等。另一类是没有所属类或接口的(CONSTANT_NameAndType_info),如构造方法。

类目包含
1. Object类
2. 自己本身
字段1. 本身的字段
方法1. 默认的构造方法
2. 本身的方法
CONSTANT_NameAndType_info<init>

access_flags
在这里插入图片描述

涉及到的结构体

CONSTANT_Methodref_info {
      u1 tag(10 0xa;
      u2 class_index;
      u2 name_and_type_index;
  }

  CONSTANT_Class_info {
      u1 tag(7;
      u2 name_index;
  }

  CONSTANT_Utf8_info {
      u1 tag(1;
      u2 length;
      u1 bytes[length];
  }

  CONSTANT_NameAndType_info {
      u1 tag(12 0xc;
      u2 name_index;
      u2 descriptor_index;
  }

  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_Class7(0x7)
CONSTANT_Fieldref9(0x9)字段
CONSTANT_Methodref10(0xa)方法
CONSTANT_InterfaceMethodref11(0xb)接口方法
CONSTANT_String8 (0x8)
CONSTANT_Integer3(0x3)
CONSTANT_Float4(0x4)
CONSTANT_Long5(0x5)
CONSTANT_Double6(0x6)
CONSTANT_NameAndType12(0xc)
CONSTANT_Utf81(0x1)
CONSTANT_MethodHandle15(0xf)
CONSTANT_MethodType16(0x10)
CONSTANT_Dynamic17(0x11)
CONSTANT_InvokeDynamic18(0x12)
CONSTANT_Module19(0x13)
CONSTANT_Package20(0x14)

解析后的样子

cafe babe // 魔数

0000 // 副版本号
0034 // 主版本号

0016 // 常量池计数器

(1) 0a 0003 0013 // Methodref

(2) 07 0014  // Class
(3) 07 0015  // Class

// Utf8
(4) 01 0006 3c69 6e69 743e
(5) 01 0003 28 2956
(6) 01 0004 43 6f64 65
(7) 01 000f 4c69 6e65 4e75 6d62 6572 5461 626c 65
(8) 01 0012 4c6f 6361 6c56 6172 6961 626c 6554 6162 6c65
(9) 01 0004 74 6869 73
(10) 01 0006 4c4d 6169 6e3b
(11) 01 000c 64 6f75 626c 654c 6f63 616c 73
(12) 01 0005 2844 4429 44
(13) 01 0002 6431
(14) 01 0001 44
(15) 01 0002 64 32
(16) 01 0010 4d65 7468 6f64 5061 7261 6d65 7465 7273
(17) 01 000a 53 6f75 7263 6546 696c 65
(18) 01 0009 4d61 696e 2e6a 6176 61

(19) 0c 0004 0005  // NameAndType

(20) 01 0004 4d 6169 6e // Utf8
(21) 01 0010 6a61 7661 2f6c 616e 672f 4f62 6a65 6374 // Utf8

0021 // access_flags 类的访问标志

0002 // this_class

0003 // super_class

0000 // interfaces_count

0000 // fields_count

0002 // methods_count  2个方法

/ 方法1
0001 // access_flags
0004 // name_index
0005 // descriptor_index
0001 // attributes_count

0006      // attribute_name_index
0000 002f // attribute_length  0x2f = 47
0001 0001
0000 0005
2ab7 0001
b100 0000
0200 0700
0000 0600
0100 0000
0500 0800
0000 0c00
0100 0000
0500 0900 0a00 00

/ 方法2
0000 // access_flags
000b // name_index
000c // descriptor_index
0002 // attributes_count

0006       // attribute_name_index
00 0000 42 // attribute_length  0x42 = 66

00 0400 0500 0000
0427 2963 af00 0000 0200
0700 0000 0600 0100 0000
0800 0800 0000 2000 0300
0000 0400 0900 0a00 0000
0000 0400 0d00 0e00 0100
0000 0400 0f00 0e00 03

0010
00 0000 09

02 000d 0000 000f 0000

0001 // attributes_count

00 11     // attribute_name_index
0000 0002 // attribute_length

0012
简单验证

就拿最后一个属性的字节来验证吧

00 11     // attribute_name_index
0000 0002 // attribute_length

0012

attribute_name_index

0012     // 0x0012 = 18    // 01 0009 4d61 696e 2e6a 6176 61

在这里插入图片描述
attribute_length

0000 0002

attribute_name_index

00 11     // 0x0011 = 17  // 01 000a 53 6f75 7263 6546 696c 65

在这里插入图片描述

在这里插入图片描述

👌 完全一致,磕磕碰碰,终于还是给解了 😁

参考

官方文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值