zoukankan      html  css  js  c++  java
  • 【JVM学习笔记】字节码文件结构

    https://www.cnblogs.com/heben/p/11468285.html  比这篇笔记更好一点

    新建一个Java类

    package com.learn.jvm;
    
    public class MyTest1 {
        private int a = 1;
    
        public MyTest1() {
        }
    
        public int getA() {
            return this.a;
        }
    
        public void setA(int a) {
            this.a = a;
        }
    }

    在控制台使用javap -c进行反解析

    D:workspace-learncommon-learnlearn-classloader	argetclassescomlearnjvm>javap -c MyTest1
    警告: 二进制文件MyTest1包含com.learn.jvm.MyTest1
    Compiled from "MyTest1.java"
    public class com.learn.jvm.MyTest1 {
      public com.learn.jvm.MyTest1();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: aload_0
           5: iconst_1
           6: putfield      #2                  // Field a:I
           9: return
    
      public int getA();
        Code:
           0: aload_0
           1: getfield      #2                  // Field a:I
           4: ireturn
    
      public void setA(int);
        Code:
           0: aload_0
           1: iload_1
           2: putfield      #2                  // Field a:I
           5: return
    }

    在控制台使用javap -verbose进行反解析

    D:workspace-learncommon-learnlearn-classloader	argetclassescomlearnjvm>javap -verbose MyTest1
    警告: 二进制文件MyTest1包含com.learn.jvm.MyTest1
    Classfile /D:/workspace-learn/common-learn/learn-classloader/target/classes/com/learn/jvm/MyTest1.class
      Last modified 2019-9-4; size 473 bytes
      MD5 checksum 8dc78fb3801af3d26bc3befec9b7c5ed
      Compiled from "MyTest1.java"
    public class com.learn.jvm.MyTest1
      SourceFile: "MyTest1.java"
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #4.#20         //  java/lang/Object."<init>":()V
       #2 = Fieldref           #3.#21         //  com/learn/jvm/MyTest1.a:I
       #3 = Class              #22            //  com/learn/jvm/MyTest1
       #4 = Class              #23            //  java/lang/Object
       #5 = Utf8               a
       #6 = Utf8               I
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               Lcom/learn/jvm/MyTest1;
      #14 = Utf8               getA
      #15 = Utf8               ()I
      #16 = Utf8               setA
      #17 = Utf8               (I)V
      #18 = Utf8               SourceFile
      #19 = Utf8               MyTest1.java
      #20 = NameAndType        #7:#8          //  "<init>":()V
      #21 = NameAndType        #5:#6          //  a:I
      #22 = Utf8               com/learn/jvm/MyTest1
      #23 = Utf8               java/lang/Object
    {
      public com.learn.jvm.MyTest1();
        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: iconst_1
             6: putfield      #2                  // Field a:I
             9: return
          LineNumberTable:
            line 8: 0
            line 9: 4
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0      10     0  this   Lcom/learn/jvm/MyTest1;
    
      public int getA();
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: getfield      #2                  // Field a:I
             4: ireturn
          LineNumberTable:
            line 12: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0       5     0  this   Lcom/learn/jvm/MyTest1;
    
      public void setA(int);
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0
             1: iload_1
             2: putfield      #2                  // Field a:I
             5: return
          LineNumberTable:
            line 16: 0
            line 17: 5
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0       6     0  this   Lcom/learn/jvm/MyTest1;
                   0       6     1     a   I
    }

    能够看到,字节码文件中是有常量池 Constant pool

    注意,以上反解析得到的并不是真正的class文件的二进制内容,而是解析的结果,可以通过其他相关工具对真正的二进制内容进行查看,将会看到类似下图的结果,其中左边是真正的二进制内容

    上图左侧是按照单字节分组显示的,比如CA是一个字节,FE是一个字节。(注:一个字节是8位,而一个16进制数占4位,所以一个字节是两个十六进制数组成的)

    要点:

    1. 使用java -verbose 命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息
    2. 魔数:所有.class字节码文件的前四个字节都是魔数,,魔数值为固定值:0xCAFEBABE
    3. 魔数之后的4个字节代码版本号,前2个字节代表次版本号(minor version),后2个字节代表主版本号(major version),以上图为例,次版本号为0,主版本号为52,所以,该文件的版本号是1.8.0
    4. 常量池(constant pool):紧接着主版本号之后的就是常量池入口。一个Java类定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是Class文件的资源仓库,比如说Java类中定义的方法与变量信息,都是存储在常量池中。常量池中主要存储两类常量:字面量与符号应用。字面量如文本字符串,Java中声明为final的常量值等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等
    5. 常量池的总体结构:Java类所对应的常量池主要由“常量池数量”与“常量池数组”这两部分共同构成。常量池数量紧跟在主版本号后面(也就是第9个字节开始),占据2个字节;常量池数组则紧跟在常量池数量之后。常量池数组与一般的数组不同的是,常量池数组中不同的原素的类型、结构都是不同的,长度当然也就不同,但是,每一种元素的第一个数据都是一个u1类型,该字节是个标志位,占据1个字节。JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型。以上图为例,第9到第10个字节连起来时 0018,即十进制数字的24,意即后面有24个常量。值得注意的是,常量池数组中元素的个数 = 常量池数 - 1(其中0暂时不使用),目的是满足某些常量池索引值的数据在特定情况下需要表达“不引用任何一个常量池”的含义;根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量池数组(也叫常量表)中,这个常量就对应null值,所以常量池的索引从1而非0开始。以上图为例显示有24个常量,但是根据 java -verbose的输出结果,显示只有23个常量。
    6. 在JVM规范种,每个变量/字段都有描述信息,描述信息的主要作用是描述字段的数据类型、方法的参数列表(包括数量、类型与顺序)与返回值。根据描述符规则,基本数据类型和代表无返回值的void类型都用一个大写字符来表示,而对象类型则使用字符L加对象的全限定名称来表示。为了压缩字节码文件的体积,对于基本数据类型,JVM都只使用一个大写字母,如下所示:B - byte ,C - char,D - double,F - float, I - int,J - long,S - short,Z - boolean,V - void,L - 对象类型,如Ljava/lang/String;
    7. 对于数组类型来说,每一个维度使用一个前置的[来表示,如int[]被记录为[I,String[][]被记录为[[Ljava/lang/String;
    8. 用描述符来描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序放在一组()之类,如方法String getRealNameByIdAndName(int id, String name),表示为(I,Ljava/lang/String;)Ljava/lang/String;

    附《Class文件结构中常量池中11种数据类型的结构总表》

     U1表示长度为一个字节,U2表示长度为2个字节。上面的表中描述了11种数据类型的结构,其实在jdk1.7之后又增加了3种(CONSTANT_MethodHandle_info, CONSTANT_MethodType_info以及CONSTANT_InvokeDynamic_info)。所以加上上面的11种,一共是14种

    然后对常量数组种的元素逐一分析:

    第一个常量,组成如下:

    U1 类型的 0A 对应值为10的 CONSTANT_Methodref_info 

    U2 类型的 00 04,换算成十进制是4 

    U2 类型的 00 14,换算成十进制是20

    Class字节码中有两种数据类型

    • 字节数据直接量:这是基本的数据类型。共细分为u1,u2,u4,u8四种,分别代表连续的1个字节,2个字节,4个字节,8个字节组成的整体数据。
    • 表(数组):表是由多个基本数据或其他表,按照既定顺序组成的大的数据集合。表是有结构的,它的结构体现在:组成表的成分所在的位置和顺序都是已经严格定义号的。

    Access_Flag访问标志

    访问标志信息包括该Class文件是类还是接口,是否被定义为public,是否是abstract,如果是类,是否被声明为final

    上图缺少了0x0002代表 ACC_PRIVATE

     在本文的例子中,访问标志的值是 00 21,对应于上图就是 ACC_PUBLIC | ACC_SUPER (即这两者的并集)

    字段表集合

    字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。

     

     本文例子中,字段表集合是0001打头的,代表有1个字段。后面的内容就是该字段的描述信息

    下图看起来更清晰

    其中 attribute_info结构如下

     

     

     

    这个工具比javap -verbose更全面一些,有idea插件可供下载

    LineNumberTable的结构:

    其他:

    在Java代码的每个非静态方法中都可以使用this指针,实际上从字节码角度来看,this是作为每个方法的第一个参数传递给了方法来实现的。

  • 相关阅读:
    IIS Express中添加MIME
    js立即执行函数IIFE(Immediatelyinvokedfunctionexpression)的几种写法
    笑话一则
    字符串操作类 NET代码积累之一
    UrlRewriter使用详解
    压缩和解压缩的方法 from Byte[]
    C#日期格式化
    JavaScript模拟进度条
    让visual studio 2005 自动为类加版权
    godaddy免费空间完美安装部署dedecms
  • 原文地址:https://www.cnblogs.com/heben/p/11460391.html
Copyright © 2011-2022 走看看