JDK1.8 CLASS文件结构(参考深入理解JAVA虚拟机)

本部分分析CLASS文件中各部分意义
JAVA代码

package com.memory.javaclass;

public class TestClass {

    private int m;

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

    public static void main(String[] args) {

    }
}

生成class文件用16位编辑器打开后

00000000h: CA FE BA BE 00 00 00 34 00 1A 0A 00 04 00 16 09 ; 漱壕...4........
00000010h: 00 03 00 17 07 00 18 07 00 19 01 00 01 6D 01 00 ; .............m..
00000020h: 01 49 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 ; .I...<init>...()
00000030h: 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E ; V...Code...LineN
00000040h: 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63 ; umberTable...Loc
00000050h: 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01 ; alVariableTable.
00000060h: 00 04 74 68 69 73 01 00 20 4C 63 6F 6D 2F 6D 65 ; ..this.. Lcom/me
00000070h: 6D 6F 72 79 2F 6A 61 76 61 63 6C 61 73 73 2F 54 ; mory/javaclass/T
00000080h: 65 73 74 43 6C 61 73 73 3B 01 00 03 69 6E 63 01 ; estClass;...inc.
00000090h: 00 03 28 29 49 01 00 04 6D 61 69 6E 01 00 16 28 ; ..()I...main...(
000000a0h: 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 ; [Ljava/lang/Stri
000000b0h: 6E 67 3B 29 56 01 00 04 61 72 67 73 01 00 13 5B ; ng;)V...args...[
000000c0h: 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E ; Ljava/lang/Strin
000000d0h: 67 3B 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 ; g;...SourceFile.
000000e0h: 00 0E 54 65 73 74 43 6C 61 73 73 2E 6A 61 76 61 ; ..TestClass.java
000000f0h: 0C 00 07 00 08 0C 00 05 00 06 01 00 1E 63 6F 6D ; .............com
00000100h: 2F 6D 65 6D 6F 72 79 2F 6A 61 76 61 63 6C 61 73 ; /memory/javaclas
00000110h: 73 2F 54 65 73 74 43 6C 61 73 73 01 00 10 6A 61 ; s/TestClass...ja
00000120h: 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 00 21 ; va/lang/Object.!
00000130h: 00 03 00 04 00 00 00 01 00 02 00 05 00 06 00 00 ; ................
00000140h: 00 03 00 01 00 07 00 08 00 01 00 09 00 00 00 2F ; .............../
00000150h: 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 ; ........*?.?..
00000160h: 02 00 0A 00 00 00 06 00 01 00 00 00 03 00 0B 00 ; ................
00000170h: 00 00 0C 00 01 00 00 00 05 00 0C 00 0D 00 00 00 ; ................
00000180h: 01 00 0E 00 0F 00 01 00 09 00 00 00 31 00 02 00 ; ............1...
00000190h: 01 00 00 00 07 2A B4 00 02 04 60 AC 00 00 00 02 ; .....*?..`?...
000001a0h: 00 0A 00 00 00 06 00 01 00 00 00 08 00 0B 00 00 ; ................
000001b0h: 00 0C 00 01 00 00 00 07 00 0C 00 0D 00 00 00 09 ; ................
000001c0h: 00 10 00 11 00 01 00 09 00 00 00 2B 00 00 00 01 ; ...........+....
000001d0h: 00 00 00 01 B1 00 00 00 02 00 0A 00 00 00 06 00 ; ....?..........
000001e0h: 01 00 00 00 0D 00 0B 00 00 00 0C 00 01 00 00 00 ; ................
000001f0h: 01 00 12 00 13 00 00 00 01 00 14 00 00 00 02 00 ; ................
00000200h: 15                                              ; .

然后进行分析

  1. 每个Class文件的头4个字节称为魔数,它的唯一的作用是确定这个文件是否为一个能被虚拟机接受的Class文件。CA FE BA BE就是Class文件的魔数。
  2. 紧接着魔数的4个字节存储的是Class的版本号:第5和第6个字节是次版本号,第7和第8个字节是主版本号。Java版本号从45开始,高版本向下兼容,此部分是00 00 00 34。
  3. 紧接着主次版本号之后的是常量池入口。由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数器。这个容量计数器是从1而不是0开始的,00 1A转成十进制为26代表有25项常量,索引值范围为1~25。
  4. Class文件结构中只有常量池的容量计数是从1开始,对于其他集合的类型,包括接口索引集合、字段表集合、方法、标记和等容量计数都是从0开始。
  5. 常量池主要存放两大类常量:字面量和符号引用。
    字面量:文本字符串、生命为final的常量值等。
    符号引用:
    类和接口的全限定名
    字段的名称和描述符
    方法的名称和描述符
  6. 常量池每一项常量都是一个表

《JDK1.8 CLASS文件结构(参考深入理解JAVA虚拟机)》
《JDK1.8 CLASS文件结构(参考深入理解JAVA虚拟机)》
《JDK1.8 CLASS文件结构(参考深入理解JAVA虚拟机)》
7. 0x0A为十进制的10,根据定义10代表CONSTANT_Methodref_info,由于每种类型不同,所以还需要根据结构表进行查找,u1、u2、u4代表占用几个字节。所以根据表2,CONSTANT_Methodref_info占用5个字节,所以0x09为下一个常量的开始,其他以此类推。
8. 可以通过javap -verbose对TestClass.class的字节码内容进行输出(如何使用可以参考IDEA->Setting->Tools->External Tools进行配置)

Classfile /D:/MyWorkSpace/JavaHotSpot/target/classes/com/memory/javaclass/TestClass.class
  Last modified 2018-8-22; size 513 bytes
  MD5 checksum 7986017c7bbe4f564d1ba33df52d42ec
  Compiled from "TestClass.java"
public class com.memory.javaclass.TestClass
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#22         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#23         // com/memory/javaclass/TestClass.m:I
   #3 = Class              #24            // com/memory/javaclass/TestClass
   #4 = Class              #25            // 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/memory/javaclass/TestClass;
  #14 = Utf8               inc
  #15 = Utf8               ()I
  #16 = Utf8               main
  #17 = Utf8               ([Ljava/lang/String;)V
  #18 = Utf8               args
  #19 = Utf8               [Ljava/lang/String;
  #20 = Utf8               SourceFile
  #21 = Utf8               TestClass.java
  #22 = NameAndType        #7:#8          // "<init>":()V
  #23 = NameAndType        #5:#6          // m:I
  #24 = Utf8               com/memory/javaclass/TestClass
  #25 = Utf8               java/lang/Object
{
  public com.memory.javaclass.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/memory/javaclass/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 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/memory/javaclass/TestClass;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;
}
SourceFile: "TestClass.java"

Methodref代表类型
#4代表类型长度
#22代表与#22行关联
9.在常量池结束后,紧接着的两个字节代表访问标志

标志名称  标志值 含义
ACC_PUBLIC 0x00 01 是否为Public类型
ACC_FINAL 0x00 10 是否被声明为final,只有类可以设置
ACC_SUPER 0x00 20 是否允许使用invokespecial字节码指令的新语义.
ACC_INTERFACE 0x02 00 标志这是一个接口
ACC_ABSTRACT 0x04 00 是否为abstract类型,对于接口或者抽象类来说,次标志值为真,其他类型为假
ACC_SYNTHETIC 0x10 00 标志这个类并非由用户代码产生
ACC_ANNOTATION 0x20 00 标志这是一个注解
ACC_ENUM 0x40 00 标志这是一个枚举

10.访问标志后是类索引、父类索引和接口索引,类索引与父类索引是u2类型,接口索引是一组u2类型,除了java/lang/Object外,其他类都有父类索引#25 = Utf8 java/lang/Object代表父类索引
11、其他还有字段表集合、方法表集合、属性表集合

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/ciqingloveless/article/details/81949094
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞