Class文件的结构

分析的Class

package com.travelsky.config;

public class TestClass {
    private int m;

    public int inc() {
        return m + 1;
    }
}

Class的字节码内容

执行 javap -verbose TestClass

警告: 二进制文件TestClass包含com.study.TestClass
Classfile /D:/x/target/classes/com/study/TestClass.class
  Last modified 2023-2-6; size 373 bytes
  MD5 checksum b58de906f1861bd978aa734a600ad2d2
  Compiled from "TestClass.java"
public class com.study.TestClass
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#18         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#19         // com/study/TestClass.m:I
   #3 = Class              #20            // com/study/TestClass
   #4 = Class              #21            // java/lang/Object
   #5 = Utf8               m
   #6 = Utf8               I
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/study/TestClass;
  #14 = Utf8               inc
  #15 = Utf8               ()I
  #16 = Utf8               SourceFile
  #17 = Utf8               TestClass.java
  #18 = NameAndType        #7:#8          // "<init>":()V
  #19 = NameAndType        #5:#6          // m:I
  #20 = Utf8               com/study/TestClass
  #21 = Utf8               java/lang/Object
{
  public com.study.TestClass();
    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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/study/TestClass;

  public int inc();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field m:I
         4: iconst_1
         5: iadd
         6: ireturn
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/study/TestClass;
}
SourceFile: "TestClass.java"

Class的16进制

CA FE BA BE 00 00 00 34 00 16 0A 00 04 00 12 09

00 03 00 13 07 00 14 07 00 15 01 00 01 6D 01 00

01 49 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29

56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E

75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63

61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01

00 04 74 68 69 73 01 00 15 4C 63 6F 6D 2F 73 74

75 64 79 2F 54 65 73 74 43 6C 61 73 73 3B 01 00

03 69 6E 63 01 00 03 28 29 49 01 00 0A 53 6F 75

72 63 65 46 69 6C 65 01 00 0E 54 65 73 74 43 6C

61 73 73 2E 6A 61 76 61 0C 00 07 00 08 0C 00 05

00 06 01 00 13 63 6F 6D 2F 73 74 75 64 79 2F 54

65 73 74 43 6C 61 73 73 01 00 10 6A 61 76 61 2F

6C 61 6E 67 2F 4F 62 6A 65 63 74 00 21 00 03 00

04 00 00 00 01 00 02 00 05 00 06 00 00 00 02 00

01 00 07 00 08 00 01 00 09 00 00 00 2F 00 01 00

01 00 00 00 05 2A B7 00 01 B1 00 00 00 02 00 0A

00 00 00 06 00 01 00 00 00 03 00 0B 00 00 00 0C

00 01 00 00 00 05 00 0C 00 0D 00 00 00 01 00 0E

00 0F 00 01 00 09 00 00 00 31 00 02 00 01 00 00

00 07 2A B4 00 02 04 60 AC 00 00 00 02 00 0A 00

00 00 06 00 01 00 00 00 06 00 0B 00 00 00 0C 00

01 00 00 00 07 00 0C 00 0D 00 00 00 01 00 10 00

00 00 02 00 11

Class文件的组成

Class文件格式采用一种类似于C语言结构体的伪结构来存储数 据,这种伪结构中只有两种数据类型:“无符号数”和“表”。Class文件的内容由下图中的数据项按严格顺序排列构成

类型

名称

数量

u4

magic

1

u2

minor_version

1

u2

major_version

1

u2

constant_pool_count

1

cp_info

constant_pool

constant_pool_count-1

u2

access_flags

1

u2

this.class

1

u2

super_class

1

u2

interfaces_count

1

u2

interfaces

interfaces_count

u2

fields_count

1

field_info

fields

fields_count

u2

methods_count

1

methods_info

methods

methods_count

u2

attributes_count

1

attributes_info

attributes

attributes_count

·无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个 字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串 值。 ·表是由多个无符号数或者其他表作为数据项构成的复合数据类型,为了便于区分,所有表的命名 都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上也可以视 作是一张表

1.魔数

每个Class文件的头4个字节(CA FE BA BE)被称为魔数,唯一作用是确定这个文件是否为 一个能被虚拟机接受的Class文件

2.版本

随后的2个字节(00 00)标识次版本号,再后面两个字节(00 34)是主版本号, Java的版本号是从45开始,每个JDK大版本发布主版本号向上加1,次版本号目前用于标识“技术预览版”功 能特性的支持

3.常量池

版本之后的类容为常量池,前两个字节(00 16)表示常量池中的常量数量(21个常量项,索引从1-21,第0项被用来表示 不引用任何一个常量池项目);

每一项常量都是一个表,表结构起始的第一位是个u1类型的标志位,代表着当前常量属于哪种常量类型.

常量池中的数据类型结构表如下

常量

项目

类型

描述

CONSTANT_Utf8_info

tag

u1

1

length

u2

UTF-8编码的字符串占用的字节数

bytes

u1

长度为length的UTF-8编码的字符串

CONSTANT_Class_info

tag

u1

值为7

index

u2

指向全限定名常量项的索引

CONSTANT_Fieldref_info

tag

u1

值为9

index

u2

指向声明字段的类或接口描述符CONSTANT_Class_info的索引项

index

u2

指向字段描述符CONSTANT_NameAndType的索引项

CONSTANT_Methodref_info

tag

u1

值为10

index

u2

指向声明方法的类描述符CONSTANT_Class_info的索引项

index

u2

指向名称及类型描述符CONSTANT_NameAndType的索引项

CONSTANT_NameAndType_info

tag

u1

值为12

index

u2

指向该字段或方法名称常量项索引

index

u2

指向该字段或方法描述符常量项索引

针对例子分析如下:

  • 1.标志位为0A,对应的类型为CONSTANT_Methodref_info,对应表可以看到随后的两个字节(00 04)表示引用第4项常量,再随后的两个字节(00 12)表示引用第18项常量

  • 2.标志位为09,对应的类型为CONSTANT_Fieldref_info,对应表可以看到随后的两个字节(00 03)表示引用第3项常量,再随后的两个字节(00 13)表示引用第19项常量

  • 3.标志位为07,对应的类型为CONSTANT_Class_info,对应表可以看到随后的两个字节(00 14)表示引用第20项常量

  • 4.标志位为07,对应的类型为CONSTANT_Class_info,对应表可以看到随后的两个字节(00 15)表示引用第21项常量

  • 5.01 00 01 6D:标志位为01,对应的类型为CONSTANT_Utf8_info,对应表可以看到随后的两个字节(00 01)表示UTF-8编码的字符串占用了一个字节,随后的一个字节是UTF-8编码的字符串(6D,对应m)

  • 6.01 00 01 49:标志位为01,对应的类型为CONSTANT_Utf8_info,对应表可以看到随后的两个字节(00 01)表示UTF-8编码的字符串占用了一个字节,随后的一个字节是UTF-8编码的字符串(49,对应I)

  • 7.01 00 06 3C 69 6E 69 74 3E:

  • 8.01 00 03 28 29 56:

  • 9.01 00 04 43 6F 64 65:

  • 10.01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65:

  • 11.01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65:

  • 12.01 00 04 74 68 69 73:

  • 13.01 00 15 4C 63 6F 6D 2F 73 74 75 64 79 2F 54 65 73 74 43 6C 61 73 73 3B:

  • 14.01 00 03 69 6E 63:

  • 15.01 00 03 28 29 49

  • 16.01 00 0A 53 6F 75 72 63 65 46 69 6C 65

  • 17.01 00 0E 54 65 73 74 43 6C 61 73 73 2E 6A 61 76 61:

  • 18.0C 00 07 00 08:标志位为0C,对应的类型为CONSTANT_NameAndType_info,对应表可以看到随后的两个字节(00 07)表示指向该字段或方法名称常量项的索引,随后的两个字节(00 08)表示该字段或方法描述符常量项的索引

  • 19.0C 00 05 00 06:

  • 20.01 00 13 63 6F 6D 2F 73 74 75 64 79 2F 54 65 73 74 43 6C 61 73 73

  • 21.01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F

4.访问标志

在常量池结束之后,紧接着的2个字节(00 21)代表访问标志(access_flags),这个标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final;等等。具体的标志位以及标志的含义如下

标记名

含义

ACC_PUBLIC

0x0001

可以被包的类外访问。(是否为public)

ACC_FINAL

0x0010

不允许有子类。(是否final修饰)

ACC_SUPER

0x0020

当用到 invokespecial 指令时,需要特殊处理的父类方法。JDK1.0.2以后,这个标志必须为真

ACC_INTERFACE

0x0200

标识定义的是接口而不是类。

ACC_ABSTRACT

0x0400

不能被实例化。(是否为abstract或interface)

ACC_SYNTHETIC

0x1000

标识并非 Java 源码生成的代码。

ACC_ANNOTATION

0x2000

标识注解类

ACC_ENUM

0x4000

标识枚举类型

ACC_MODULE

0x8000

标识模块

5.类索引、父类索引与接口索引集合

访问标志后紧跟着类索引(00 03)、父类索引( 00 04)和接口数量(00 00)和索引集合(例子中没有实现接口,因此不占用任何字节)

实现一个接口如图所示,图中表示实现一个接口,接口索引(00 05)为常量池第5项

6.字段表集合

字段表数量(00 01)private修饰符( 00 02) 字段名称索引(00 05)字段描述符(00 06) 属性表计数器(00 00) 额外描述(无)

字段表的结构如图所示

类型

名称

数量

说明

u2

access_flags

1

是用于定义字段被访问权限和基础属性的掩码标志

u2

name_index

1

是对常量池的一个有效索引。常量池在该索引处的项必须是 CONSTANT_Utf8_info结构,表示一个有效的字段的非全限定名

u2

descriptor_index

1

是对常量池的一个有效索引。常量池在该索引处的项必 须是 CONSTANT_Utf8_info结构,表示一个有效的字段的描述符

u2

attributes_count

1

值表示当前字段的附加属性的数量

attribute_info

attributes

attributes_count

附加属性

access_flag用来放字段的修饰符,可以设置的标志位和含义如下:

标记名

说明

ACC_PUBLIC

0x0001

public,表示字段可以从任何包访问

ACC_PRIVATE

0x0002

private,表示字段仅能该类自身调用

ACC_PROTECTED

0x0004

protected,表示字段可以被子类调用。

ACC_STATIC

0x0008

static,表示静态字段

ACC_FINAL

0x0010

final,表示字段定义后值无法修改

ACC_VOLATILE

0x0040

volatile,表示字段是易变的

ACC_TRANSIENT

0x0080

transient,表示字段不会被序列化。

ACC_SYNTHETIC

0x1000

表示字段由编译器自动产生。

ACC_ENUM

0x4000

enum,表示字段为枚举类型。

7.方法表集合

方法表容量00 02

方法1访问标志00 01 名称索引00 07 描述符索引00 08 附加属性数量00 01 属性名称索引00 09 属性长度00 00 00 2F属性内容(

00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 02 00 0A

00 00 00 06 00 01 00 00 00 03 00 0B 00 00 00 0C

00 01 00 00 00 05 00 0C 00 0D 00 00)

方法2访问标志00 01 名称索引00 0E

描述符索引00 0F 附加属性数量00 01 属性名称索引00 09 属性长度00 00 00 31 属性内容

00 02 00 01 00 00

00 07 2A B4 00 02 04 60 AC 00 00 00 02 00 0A 00

00 00 06 00 01 00 00 00 06 00 0B 00 00 00 0C 00

01 00 00 00 07 00 0C 00 0D 00 00)

8.属性表集合

Class文件、字段表、方法表都可以 携带自己的属性表集合,以描述某些场景专有的信息

属性表固定通用的属性如下

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

table

info

attribute_length

方法表结束后紧跟的内容时Class文件的属性表集合,内容为(00 01 00 10 00 00 00 02 00 11)

属性数量(00 01)表示该Class文件只有一个属性,属性名称索引( 00 10 )对应常量池中的第16项,为SourceFile,属性长度(00 00 00 02)为2 ,参考SourceFile属性结构可知紧跟的两个字符(00 11)为内容索引,指向常量池中的第17项(TestClass.java

SourceFile属性结构

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

source_file_index

1

方法的属性表分析

00 09 00 00 00 31 00 02 00 01 00 00

00 07 2A B4 00 02 04 60 AC 00 00 00 02 00 0A 00

00 00 06 00 01 00 00 00 06 00 0B 00 00 00 0C 00

01 00 00 00 07 00 0C 00 0D 00 00)

属性索引(00 09)为常量值第9项(Code),Code的属性表结构如下:

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

max_stack

1

u2

max_locals

1

u4

code_length

1

u1

code

code_length

u2

exception_table_length

1

exception_info

exception_table

exception_table_length

u2

attributes_count

1

attribute_info

attributes

attributes_count

根据表可以分析出

属性占用的长度(00 00 00 31)为49个字节;操作数栈的最大值(00 02)为2;局部变量表所需的存储空间(00 01)为1(max_locals的单位是变量槽);字节码指令的长度(00 00 00 07)为7;字节码指令为`2A B4 00 02 04 60 AC`,可以根据虚拟机字节码指令表找到对应的指令;异常表长度(00 00 )为0 ,所以没有异常信息;附加属性的长度(00 02)为2

  • 第一个附加属性在常量池中的索引(00 0A)为10(LineNumberTable),LineNumberTable(Java源码行号与字节码行号之间的对应关系)的属性表结构如下:

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

line_number_table_length

1

line_number_table{

u2 start_pc; u2 line_number;

}

line_number_info

line_number_table_length

长度(00 00 00 06)为6 ,包含1个(00 01)对应关系,为 00 00 00 06

  • 第二个附加属性在常量池中的索引(00 0B)为11(LocalVariableTable),LocalVariableTable(栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系)的属性表结构如下:

类型

名称

数量

u2

attribute_name_index

1

u4

attribute_length

1

u2

local_variable_table_length

1

local_variable_table { u2 start_pc; u2 length; u2 name_index; u2 descriptor_index; u2 index; }

local_variable_info

local_variable_table_length

长度(00 00 00 0C)为12个字节,包含一个( 00 01)对应关系,生命周期开始的字节码偏移量( 00 00)为0,作用范围覆盖的长度( 00 07)为7,局部变量的名称索引( 00 0C)为常量池第12项,描述符的索引( 00 0D)为常量值的第13项,在栈帧局部变量表中的Slot位置( 00 00)为0

变量槽(Slot):是虚拟机为局部变量分配内存所使用的最小单位。对于byte、char、float、int、short、boolean和returnAddress等长度不超过32位的数据类型,每个局部变量占用一个变量槽,而double和long这两种64位的数据类型则需要两个变量槽来存放。方法参数(包括实例方法中的隐藏参数“this”)、显式异常处 理程序的参数(Exception Handler Parameter,就是try-catch语句中catch块中所定义的异常)、方法体中 定义的局部变量都需要依赖局部变量表来存放。注意,并不是在方法中用了多少个局部变量,就把这 些局部变量所占变量槽数量之和作为max_locals的值,操作数栈和局部变量表直接决定一个该方法的栈 帧所耗费的内存,不必要的操作数栈深度和变量槽数量会造成内存的浪费。Java虚拟机的做法是将局 部变量表中的变量槽进行重用,当代码执行超出一个局部变量的作用域时,这个局部变量所占的变量 槽可以被其他局部变量所使用,Javac编译器会根据变量的作用域来分配变量槽给各个变量使用,根据 同时生存的最大局部变量数量和类型计算出max_locals的大小

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值