JVM之Class文件分析详解

JVM之Class文件分析详解

逐个字节分析Class文件。了解Class文件内部结构。

原始类文件:

这个类直接拿了知乎的一个类,分析是很简单,只是因为重新画图比较麻烦,不想再做重复的苦力工作(地址(https://zhuanlan.zhihu.com/p/23068093)ps:作者仅仅提供了类和图,并无解析。)

package com.vonzhou.learn.jvm.klass;

public class Foo {
    private int m;

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

Class结构体

一个Class文件可以用如下的结构体抽象:

JVM之Class文件分析详解_第1张图片

Step by Step

接下来就是对照着字节码和JVM规范阅读的过程,对自己多点耐心。

Class魔数和版本

在这里插入图片描述

JVM之Class文件分析详解_第2张图片

JVM之Class文件分析详解_第3张图片

JVM之Class文件分析详解_第4张图片

常量池计数器和常量池

在这里插入图片描述

注意:常量池内常量的真实数量是 常量池计数器-1

JVM之Class文件分析详解_第5张图片
JVM之Class文件分析详解_第6张图片

JVM之Class文件分析详解_第7张图片

JVM之Class文件分析详解_第8张图片

JVM之Class文件分析详解_第9张图片

JVM之Class文件分析详解_第10张图片

JVM之Class文件分析详解_第11张图片

JVM之Class文件分析详解_第12张图片

JVM之Class文件分析详解_第13张图片

JVM之Class文件分析详解_第14张图片

JVM之Class文件分析详解_第15张图片

JVM之Class文件分析详解_第16张图片

JVM之Class文件分析详解_第17张图片

JVM之Class文件分析详解_第18张图片

JVM之Class文件分析详解_第19张图片

JVM之Class文件分析详解_第20张图片

JVM之Class文件分析详解_第21张图片

JVM之Class文件分析详解_第22张图片
JVM之Class文件分析详解_第23张图片

JVM之Class文件分析详解_第24张图片

JVM之Class文件分析详解_第25张图片

JVM之Class文件分析详解_第26张图片

JVM之Class文件分析详解_第27张图片

JVM之Class文件分析详解_第28张图片

JVM之Class文件分析详解_第29张图片

JVM之Class文件分析详解_第30张图片

那么接下来就是这三个了:

1556854982133

访问标志

1556855229233

JVM之Class文件分析详解_第31张图片

这里

JVM之Class文件分析详解_第32张图片

为什么是0021呢?因为 0X0001|0X0020 = 0X0021。

类索引,父类索引

1556855257629

接下来的2B是this_class指向我们的类名, super_class指示父类。

JVM之Class文件分析详解_第33张图片

JVM之Class文件分析详解_第34张图片

这里没有实现接口:

JVM之Class文件分析详解_第35张图片

字段计数器和字段表集合

fields_count以后进入字段表集合

1556851584435

JVM之Class文件分析详解_第36张图片

JVM之Class文件分析详解_第37张图片

JVM之Class文件分析详解_第38张图片

方法计数器和方法表集合

首先进入方法计数器。

1556852236787

JVM之Class文件分析详解_第39张图片

JVM之Class文件分析详解_第40张图片

JVM之Class文件分析详解_第41张图片

从上可以看出,关键的不同点在于attribute_info字段。这是属性表集合,下面附上属性表集合的一般结构。

JVM之Class文件分析详解_第42张图片

JVM之Class文件分析详解_第43张图片

从上面两个图可以看出属性表前两个字段时固定的,关键在于第三个字段各有不同,比如方法表的属性表Code的完整格式为下图

JVM之Class文件分析详解_第44张图片

接下来就对Code属性表进行梳理:

JVM之Class文件分析详解_第45张图片

JVM之Class文件分析详解_第46张图片

属性计数器和属性表集合

1556856126125

属性表集合中的SourceFile:1556854138367

JVM之Class文件分析详解_第47张图片

由属性计数器后的0010计算十进制数为

JVM之Class文件分析详解_第48张图片

下面的DEC为16,所以找到第16个常量为JVM之Class文件分析详解_第49张图片

没错了,是SourceFile

JVM之Class文件分析详解_第50张图片

附上javap -version 验证解析结果

使用javap解析出来的结果中没有包含LocalVariableTable字段,需要在使用javac编译java中时,加上-g的参数,生成的class文件中才带有LocalVariableTable的信息。LocalVariableTable属性:用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,非运行时必需属性,默认不会生成至Class文件中,可以使用Javac的-g:none或-g:vars关闭或要求生成该项属性信息。

JVM之Class文件分析详解_第51张图片

另外:LineNumberTale属性:用于描述Java源码的行号与字节码行号之间的对应关系,非运行时必需属性,会默认生成至Class文件中,可以使用Javac的-g:none或-g:lines关闭或要求生成该项属性信息。

JVM之Class文件分析详解_第52张图片

具体编译过程:

javac -g -d . Foo.java
javap -v com.vonzhou.learn.jvm.klass.Foo
ca fe ba be 20 20 20 34 20 16 0a 20 04 20 12 09 20 03 20 13 07 20 14 07 20 15 01 20 01 6d 01 20 01 49 01 20 06 3c 69 6e 69 74 3e 01 20 03 28 29 56 01 20 04 43 6f 64 65 01 20 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 20 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01 20 04 74 68 69 73 01 20 21 4c 63 6f 6d 2f 76 6f 6e 7a 68 6f 75 2f 6c 65 61 72 6e 2f 6a 76 6d 2f 6b 6c 61 73 73 2f 46 6f 6f 3b 01 20 03 69 6e 63 01 20 03 28 29 49 01 20 0a 53 6f 75 72 63 65 46 69 6c 65 01 20 08 46 6f 6f 2e 6a 61 76 61 0c 20 07 20 08 0c 20 05 20 06 01 20 1f 63 6f 6d 2f 76 6f 6e 7a 68 6f 75 2f 6c 65 61 72 6e 2f 6a 76 6d 2f 6b 6c 61 73 73 2f 46 6f 6f 01 20 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 20 21 20 03 20 04 20 20 20 01 20 02 20 05 20 06 20 20 20 02 20 01 20 07 20 08 20 01 20 09 20 20 20 2f 20 01 20 01 20 20 20 05 2a b7 20 01 b1 20 20 20 02 20 0a 20 20 20 06 20 01 20 20 20 03 20 0b 20 20 20 0c 20 01 20 20 20 05 20 0c 20 0d 20 20 20 01 20 0e 20 0f 20 01 20 09 20 20 20 31 20 02 20 01 20 20 20 07 2a b4 20 02 04 60 ac 20 20 20 02 20 0a 20 20 20 06 20 01 20 20 20 07 20 0b 20 20 20 0c 20 01 20 20 20 07 20 0c 20 0d 20 20 20 01 20 10 20 20 20 02 20 11  
E:\JVM>javap -v com.vonzhou.learn.jvm.klass.Foo
Classfile /E:/JVM/com/vonzhou/learn/jvm/klass/Foo.class
  Last modified 2019-5-3; size 391 bytes
  MD5 checksum 4d9e593620f49a9114d834ec5d923986
  Compiled from "Foo.java"
public class com.vonzhou.learn.jvm.klass.Foo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#18         // java/lang/Object."":()V
   #2 = Fieldref           #3.#19         // com/vonzhou/learn/jvm/klass/Foo.m:I
   #3 = Class              #20            // com/vonzhou/learn/jvm/klass/Foo
   #4 = Class              #21            // java/lang/Object
   #5 = Utf8               m
   #6 = Utf8               I
   #7 = Utf8               
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/vonzhou/learn/jvm/klass/Foo;
  #14 = Utf8               inc
  #15 = Utf8               ()I
  #16 = Utf8               SourceFile
  #17 = Utf8               Foo.java
  #18 = NameAndType        #7:#8          // "":()V
  #19 = NameAndType        #5:#6          // m:I
  #20 = Utf8               com/vonzhou/learn/jvm/klass/Foo
  #21 = Utf8               java/lang/Object
{
  public com.vonzhou.learn.jvm.klass.Foo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/vonzhou/learn/jvm/klass/Foo;

  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 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   Lcom/vonzhou/learn/jvm/klass/Foo;
}
SourceFile: "Foo.java"

再次附上没有 -g 的结果:

ca fe ba be 20 20 20 34 20 13 0a 20 04 20 0f 09 20 03 20 10 07 20 11 07 20 12 01 20 01 6d 01 20 01 49 01 20 06 3c 69 6e 69 74 3e 01 20 03 28 29 56 01 20 04 43 6f 64 65 01 20 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 20 03 69 6e 63 01 20 03 28 29 49 01 20 0a 53 6f 75 72 63 65 46 69 6c 65 01 20 08 46 6f 6f 2e 6a 61 76 61 0c 20 07 20 08 0c 20 05 20 06 01 20 1f 63 6f 6d 2f 76 6f 6e 7a 68 6f 75 2f 6c 65 61 72 6e 2f 6a 76 6d 2f 6b 6c 61 73 73 2f 46 6f 6f 01 20 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 20 21 20 03 20 04 20 20 20 01 20 02 20 05 20 06 20 20 20 02 20 01 20 07 20 08 20 01 20 09 20 20 20 1d 20 01 20 01 20 20 20 05 2a b7 20 01 b1 20 20 20 01 20 0a 20 20 20 06 20 01 20 20 20 03 20 01 20 0b 20 0c 20 01 20 09 20 20 20 1f 20 02 20 01 20 20 20 07 2a b4 20 02 04 60 ac 20 20 20 01 20 0a 20 20 20 06 20 01 20 20 20 07 20 01 20 0d 20 20 20 02 20 0e  
E:\JVM>javac  -d . Foo.java

E:\JVM>javap -v com.vonzhou.learn.jvm.klass.Foo
Classfile /E:/JVM/com/vonzhou/learn/jvm/klass/Foo.class
  Last modified 2019-5-3; size 291 bytes
  MD5 checksum 45262c23d72e75c78347d2f05b918bee
  Compiled from "Foo.java"
public class com.vonzhou.learn.jvm.klass.Foo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#15         // java/lang/Object."":()V
   #2 = Fieldref           #3.#16         // com/vonzhou/learn/jvm/klass/Foo.m:I
   #3 = Class              #17            // com/vonzhou/learn/jvm/klass/Foo
   #4 = Class              #18            // java/lang/Object
   #5 = Utf8               m
   #6 = Utf8               I
   #7 = Utf8               
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               inc
  #12 = Utf8               ()I
  #13 = Utf8               SourceFile
  #14 = Utf8               Foo.java
  #15 = NameAndType        #7:#8          // "":()V
  #16 = NameAndType        #5:#6          // m:I
  #17 = Utf8               com/vonzhou/learn/jvm/klass/Foo
  #18 = Utf8               java/lang/Object
{
  public com.vonzhou.learn.jvm.klass.Foo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 3: 0

  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 7: 0
}
SourceFile: "Foo.java"

参考

《深入理解Java虚拟机》周志明,

另外找到的其他关于Class文件解析的文章有:

https://www.cnblogs.com/timlong/p/8143839.html

https://www.cnblogs.com/noteless/p/9540876.html#0

https://www.jianshu.com/p/d0f3e361f92e

https://www.jb51.net/article/116203.htm(这个有对文中没有详细解释的method方法的属性的解释)

你可能感兴趣的:(JVM)