IT人生

  • 首页
  • 归档
  • kafka
  • Java
  • Spring
  • Golang
  • SQL
  • Spark
  • ElasticSearch
  • 关于

  • 搜索
Phoenix HBase Kudu ElasticSearch Spring 数据结构 操作系统 Kettle Azkaban Sqoop Hive Yarn Redis Mybatis Impala Cloudera 大数据 HDFS mycat shell Linux 架构 并发 mysql sql golang java 工具 spark kafka 人生

深入JVM(三)Class文件结构实例分析

发表于 2018-09-10 | 分类于 java | 0 | 阅读次数 106

一. 环境准备

Class文件结构环境准备主要使用到了javap,ASM字节码框架和jclasslib工具

1. javap命令

有如下的java源文件

public interface DemoInterface {

    void test(int paramInt);
}


public class DemoClass implements DemoInterface {

    private String info = "hello world";

    public void test(int paramInt){
        paramInt++;
    }
}

编译过后,可以使用 javap 来查阅 DemoClass 的字节码

javac DemoClass.java
javap -v -p DemoClass.class

第一个选项是 -p。默认情况下 javap 会打印所有非私有的字段和方法,当加了 -p 选项后,它还将打印私有的字段和方法
第二个选项是 -v。它尽可能地打印所有信息。

Classfile /Users/Randy/Developer/1Projects/6Self/demo/src/main/java/DemoClass.class
  Last modified Sep 6, 2020; size 668 bytes
  MD5 checksum 8570320ab11f209c38eb847caac27357
  Compiled from "DemoClass.java"

public class DemoClass implements DemoInterface
 ....
}
SourceFile: "DemoClass.java"

2. jclasslib bytecode viewer

jclasslib bytecode viewer是idea中的一个插件,本质上javap -v -p按照图形化的方式展示出来

image.png

二.Class文件分析

以上javap命令的输出内容本质上就是把class文件内容打印出来,其结构是按照 深入JVM(三)Class文件结构 中的类文件结构图展示的
image.png
代码输出的完整内容如下:

Classfile /Users/Randy/Developer/1Projects/6Self/demo/target/classes/com/randy/DemoClass.class
  Last modified Sep 6, 2020; size 479 bytes
  MD5 checksum 5c9ff7c479dcb22e279b9726426d0131
  Compiled from "DemoClass.java"
public class com.randy.DemoClass implements com.randy.DemoInterface
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#22         // java/lang/Object."<init>":()V
   #2 = String             #23            // hello world
   #3 = Fieldref           #4.#24         // com/randy/DemoClass.info:Ljava/lang/String;
   #4 = Class              #25            // com/randy/DemoClass
   #5 = Class              #26            // java/lang/Object
   #6 = Class              #27            // com/randy/DemoInterface
   #7 = Utf8               info
   #8 = Utf8               Ljava/lang/String;
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               this
  #15 = Utf8               Lcom/randy/DemoClass;
  #16 = Utf8               test
  #17 = Utf8               (I)V
  #18 = Utf8               paramInt
  #19 = Utf8               I
  #20 = Utf8               SourceFile
  #21 = Utf8               DemoClass.java
  #22 = NameAndType        #9:#10         // "<init>":()V
  #23 = Utf8               hello world
  #24 = NameAndType        #7:#8          // info:Ljava/lang/String;
  #25 = Utf8               com/randy/DemoClass
  #26 = Utf8               java/lang/Object
  #27 = Utf8               com/randy/DemoInterface
{
  private java.lang.String info;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE

  public com.randy.DemoClass();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String hello world
         7: putfield      #3                  // Field info:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 8: 0
        line 10: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/randy/DemoClass;

  public void test(int);
    descriptor: (I)V
    flags: ACC_PUBLIC
    Code:
      stack=0, locals=2, args_size=2
         0: iinc          1, 1
         3: return
      LineNumberTable:
        line 13: 0
        line 14: 3
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  this   Lcom/randy/DemoClass;
            0       4     1 paramInt   I
}
SourceFile: "DemoClass.java"

总体上可以分为四大区域:

2.1. 基本信息区域:

  - 魔数:

前4个字节(CA FE BA BE)用来确定这个文件是否是一个可以被JVM读取的class文件,但是在上面的输出中没有展示。而通过文本工具可以查看如下:
image.png
- 主副版本号:minor version: 0 major version: 52
class 文件的版本号指的是编译生成该 class 文件时所用的 JRE 版本。由较新的 JRE 版本中的 javac 编译而成的 class 文件,不能在旧版本的 JRE 上跑,否则,会出现UnsupportedClassVersionError异常。 52对应的是Java 8
- 访问标志:flags: ACC_PUBLIC, ACC_SUPER 标志这个类的访问权限
image.png
- 类索引: 常量池中#7 = Class #29 // DemoClass
- 父索引: #8 = Class #30 // java/lang/Object,java中出了Object意外的所有类都存在父类
- 实现接口:#9 = Class #31 // DemoInterface
- 接口计数器:1
- 字段计数器:0
- 方法计数器:2 一个是方法test,另外一个是默认的构造方法

2.22. 常量池:

常量池是一个类似于数组的数据结构,中的每一项都有一个对应的索引(如 #1),并且可能引用其他的常量池项。
常量池计数器是从1开始计数的,将第0项常量空出来是有特殊考虑的,索引值为0代表“不引用任何一个常量池项”

Constant pool:
   #1 = Methodref          #5.#22         // java/lang/Object."<init>":()V
   #2 = String             #23            // hello world
   #3 = Fieldref           #4.#24         // com/randy/DemoClass.info:Ljava/lang/String;
   #4 = Class              #25            // com/randy/DemoClass
   #5 = Class              #26            // java/lang/Object
   #6 = Class              #27            // com/randy/DemoInterface
   #7 = Utf8               info
   #8 = Utf8               Ljava/lang/String;
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               this
  #15 = Utf8               Lcom/randy/DemoClass;
  #16 = Utf8               test
  #17 = Utf8               (I)V
  #18 = Utf8               paramInt
  #19 = Utf8               I
  #20 = Utf8               SourceFile
  #21 = Utf8               DemoClass.java
  #22 = NameAndType        #9:#10         // "<init>":()V
  #23 = Utf8               hello world
  #24 = NameAndType        #7:#8          // info:Ljava/lang/String;
  #25 = Utf8               com/randy/DemoClass
  #26 = Utf8               java/lang/Object
  #27 = Utf8               com/randy/DemoInterface

常量池主要用于存放各种字面量和符号引用,可以理解为class文件中的资源仓库。
字面量比较接近于 Java 语言层面的的常量概念,如文本字符串、声明为 final 的常量值等。而符号引用则属于编译原理方面的概念。包括下面三类常量:

  1. 类和接口的全限定名,也就是Ljava/lang/String;这样,将类名中原来的"."替换为"/"得到的,主要用于在运行时解析得到类的直接引用
#7 = Class              #37            // com/randy/DemoClass
  1. 字段的名称和描述符,字段也就是类或者接口中声明的变量,包括类级别变量(static)和实例级的变量
#2 = Fieldref           #30.#31        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #32            // paramInt should be great than 0
  1. 方法的名称和描述符,方法的描述类似于JNI动态注册时的“方法签名”,也就是参数类型+返回值类型
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               args
  #20 = Utf8               [Ljava/lang/String;
  #21 = Utf8               test
  #22 = Utf8               (I)V

java代码在进行javac编译的时候并不会解析为内存中的最终布局,必须要经过运行期转换才能得到最终的内存入口地址。

2.3. 字段区域:

字段包括类级变量以及实例变量,用于描述接口或类中声明的变量

#3 = Fieldref           #4.#24         // com/randy/DemoClass.info:Ljava/lang/String;
#4 = Class              #25            // com/randy/DemoClass
#7 = Utf8               info
#8 = Utf8               Ljava/lang/String;
#24 = NameAndType        #7:#8          // info:Ljava/lang/String;

2.4. 方法区域:

用来列举该类中的各个方法,包括:方法描述符,访问权限和Code代码区域,
image.png
从截图可以看出这个类有两个方法,如构造方法:

#1 = Methodref          #5.#22         // java/lang/Object."<init>":()V
#5 = Class              #26            // java/lang/Object
#22 = NameAndType        #9:#10         // "<init>":()V

public com.randy.DemoClass();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String hello world
         7: putfield      #3                  // Field info:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 8: 0
        line 10: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/randy/DemoClass;
  
  • 本文作者: Randy
  • 本文链接: http://www.itrensheng.com/archives/deepinjvmclassinaction
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# Phoenix # HBase # Kudu # ElasticSearch # Spring # 数据结构 # 操作系统 # Kettle # Azkaban # Sqoop # Hive # Yarn # Redis # Mybatis # Impala # Cloudera # 大数据 # HDFS # mycat # shell # Linux # 架构 # 并发 # mysql # sql # golang # java # 工具 # spark # kafka # 人生
深入JVM(二)JVM概述
关于Mysql查询varchar和bigint类型不匹配的问题
  • 文章目录
  • 站点概览
Randy

Randy

技术可以暂时落后,但任何时候都要有上进的信念

80 日志
27 分类
31 标签
RSS
Github E-mail
Creative Commons
© 2021 备案号:沪ICP备19020689号-1
Randy的个人网站