美文网首页
JVM系列之class文件剖析

JVM系列之class文件剖析

作者: 阿伦故事2019 | 来源:发表于2019-08-12 00:16 被阅读0次

    英属哥伦比亚大学(加拿大)校训:“这是你的思想之地。”


    帝都几天的阴雨连绵,心中的热情似乎也被浇灭了,总是感觉有气无力,真的挺渴望阳光的味道。再无力,该做的事还得干,世间的事不可能每件都喜欢,把每件不讨厌的市干习惯了就是会产生依赖感,进而喜欢,这就跟国人夫妻结婚生子过日子一个道理。
    本篇是JVM系列开篇,本想搞个深度的剖析,但类文件解析本是繁琐的事情,这几天加班加上心情烦躁,未能全面地梳理,但会尽本人之全力呈现一篇值得阅读的文章。
    本篇从以下类文件结构/常量池结构/字段表结构/方法表结构等展示讲解,最后以一个demo进行说明,涉及的东西比较多,会拎出重点的进行讲解。

    一 class文件结构

    我们知道咱们正常搬砖的地方是.java文件,执行javac命令就会编译成.class字节码文件,这样jvm才能正常解析执行,今天要聊的正是字节码文件。一个class文件由虚拟机指令/符号表/其他辅助信息组成,其数据结构就是无符号数和表。
    无符号数:指的是全部二进制位均表示数值,无符号位,第一个"0"或"1"来表示正负;无符号数在class文件中是基本数据类型,均是非负数,用来描述数字、索引引用、数量值、UTF-8编码构成字符串等,包含u1 u2 u4 u8四种,分别代表1个字节、2个字节、4个字节和8个字节。
    表:指的是由多个无符号数或其他表作为数据项构成的复合数据类型,以_info结尾,主要用来表示常量池、字段表、方法表等。
    附上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_class 1
    接口索引集合 u2 interfaces interfaces_count
    字段计数器 u2 fields_count 1
    字段表集合 field_info fields fields_count
    方法计数器 u2 methods_count 1
    方法表集合 method_info methods methods_count
    属性计数器 u2 attributes_count 1
    属性表集合 attribute_info attributes attributes_count

    二 常量池

    常量池代表class文件中的仓库资源,主要存放两大类常量:字面量和符号引用。
    字面量:表示固定值,可以当作常量,通俗理解为等号右边的内容;
    符号引用:包含三类常量,分别是类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
    常量池有11种不同的表结构,每一项的第一位都是是一个u1类型的标志位(tag),代表常量类型,结构表单(因简书markdown不支持table标签,无法合并单元格,在此只能用图片替代)如下:

    类型 标志 描述
    CONSTANT_Utf8_info 1 utf8编码字符串
    CONSTANT_Integer_info 3 整型字面量
    CONSTANT_Float_info 4 浮点型字面量
    CONSTANT_Long_info 5 长整型字面量
    CONSTANT_Double_info 6 双精度浮点型字面量
    CONSTANT_Class_info 7 类或接口的符号引用
    CONSTANT_String_info 8 字符串型字面量
    CONSTANT_Fieldref_info 9 字段的符号引用
    CONSTANT_Methodref_info 10 类中方法的符号引用
    CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用
    CONSTANT_NameAndType_info 12 字段或方法的部分符号引用
    CONSTANT_MethodHandle_info 15 标识方法句柄
    CONSTANT_MethodType_info 16 标识方法类型
    CONSTANT_InvokeDynamic_info 18 表示一个动态方法调用点
    常量池结构表1.png 常量池结构表2.png

    三 访问标记

    紧接着常量池之后的两个字节(u2)代表访问标志(access_flags),用于识别一些类或接口层次的访问信息,表单如下:

    名称 说明
    ACC_PUBLIC 0X0001 public类型
    ACC_FINAL 0X0010 声明final,只有类可设置
    ACC_SUPER 0X0020 使用invokespecial指令新语义,编译后类的这个标识为真
    ACC_INTERFACE 0X0200 接口
    ACC_ABSTRACT 0X0400 接口或抽象类此值为真,其他为假
    ACC_SYNTHETIC 0X1000 标识此类并非为用户代码生成
    ACC_ANNOTATION 0X2000 注解
    ACC_ENUM 0X4000 枚举

    四 索引顺序

    在访问标记之后,有三项来确定该类的继承关系,按顺序依次为:
    类索引(this):描述类的全限定名
    父类索引(super):描述父类的全限定名
    接口索引集合(interfaces):描述类实现的接口集合。

    五 字段表集合

    在接口索引集合之后是字段计数器,用来标识有多少个字段,紧接着就是字段表集合,字段表用来描述接口或者类中声明的变量。它包括类级变量和实例级变量,但不包括局部变量以及从父类和接口中继承而来的字段。
    字段表的格式如下:

    名称 类型 数量 描述
    access_flags u2 1 字段修饰符
    name_index u2 1 字段和方法名称在常量池中的引用
    descriptor_index u2 1 字段和方法描述符在常量池中的引用
    attribute_count u2 1 字段额外信息属性的个数
    attributes attribute_info attribute_count 字段的额外信息属性

    字段修饰符如下:

    名称 数值 含义
    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 0X0100 是否由编译器自动生成
    ACC_ENUM 0X0400 是否枚举

    全限定名
    类全路径中的"."替换为"/",如:org/xwz/demo/jvm/classfile/Test;
    简单名称
    指的是没有类型和修饰符的字段或者方法名称;
    描述符
    描述字段的数据类型、方法的参数列表和返回值,其中基本类型字段的描述符用一个大写字母来标识,对象类型用"L"加上类全限定名来标识。
    字段描述符如下:

    描述符 含义
    B 基本类型byte
    C 基本类型char
    D 基本类型double
    F 基本类型float
    I 基本类型int
    J 基本类型long
    S 基本类型short
    Z 基本类型boolean
    V 特殊类型void
    L 对象类型,如Ljava/lang/Object
    [ 数组类型,如[java/lang/String

    六 方法表集合

    类文件对方法描述与对字段描述几乎采用完全一致的方式,其结构也完全一致,不同之处在于访问标志有所区别,如方法具有字段不具备的特有修饰,像native/synchronized等,同理字段也具备特别的修饰像volatile等。
    方法表访问标志如下:

    名称 数值 含义
    ACC_PUBLIC 0X0001 是否public
    ACC_PRIVATE 0X0002 是否private
    ACC_PROTECTED 0X0004 是否protected
    ACC_STATIC 0X0008 是否static
    ACC_FINAL 0X0010 是否final
    ACC_SYNCHRONIZED 0X0020 是否synchronized
    ACC_BRIDGE 0X0040 是否由编译器产生的桥接方法
    ACC_VARARGS 0X080 是否接受不定参数
    ACC_NATIVE 0X0100 是否native
    ACC_ABSTRACT 0X0400 是否abstract
    ACC_STRICTFP 0X0800 是否strictfp
    ACC_SYNTHETIC 0X1000 是否由编译器自动产生

    七 属性表集合

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

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u4 1
    info u1 attribute_length

    属性列表如下:

    名称 位置 含义
    Code 方法表 编译后的字节码指令
    ConstantValue 字段表 final定义的常量值
    Deprecated 类、字段表、方法表 声明deprecated的方法和字段
    Exceptions 方法表 方法抛出的异常
    EnclosingMethod 类文件 标识这个类所在的外围方法,仅当一个类为局部类或者匿名类时才拥有此属性
    InnerClasses 类文件 内部类列表
    LineNumberTable Code属性 Java源码的行号与字节码指令的对应关系
    LocalVariableTable Code属性 方法的局部变量描述
    StackMapTable Code属性 JDK1.6中新增的属性,供新的类型检查验证器(Type Checker)检查和处理目标方法的局部变量和操作数栈所需要的类型是否匹配
    SourceFile 类文件 记录源文件名称
    Signature 类、方法表、字段表 JDK1.5中新增的属性,用于支持泛型下的方法签名,在Java语言中,任何类、 接口、初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)或参数化类型(Parameterized Types),则Signature属性会为它记录泛型签名信息。由于Java的泛型采用擦除法实现,在为了避免类型信息被擦除后导致签名混乱,需要这个属性记录泛型中的相关信息。
    SourceDebugExtension 类文件 JDK1.6中新增的属性,用于存储额外的调试信息
    Synthetic 类、方法表、字段表 标识方法或字段为编译器自动生成的
    LocalVariableTypeTable JDK1.5中新增的属性,使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加
    RuntimeVisibleAnnotations 类、方法表、字段表 JDK1.5新增的属性,为动态注解提供支持,用于注明哪些注解是运行时(实际上运行时就是进行反射调用)可见的
    RuntimeInvisibleAnnotations 类、方法表、字段表 JDK1.5新增的属性,用于指明哪些注解是运行时不可见的
    RuntimeVisibleParameterAnnotations 方法表 JDK1.5新增的属性,作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法参数
    RuntimeInvisibleParameterAnnotations 方法表 JDK1.5新增的属性,作用与RuntimeInvisibleAnnotations属性类似,只不过作用对象为方法参数
    AnnotationDefault 方法表 JDK1.5新增的属性,用于记录注解类元素的默认值
    BootstrapMethods 类文件 JDK1.7中新增的属性,用于保存invokedynamic指令引用的引导方法限定符

    下面说下属性表中的重点属性:
    Code属性
    方法体中的代码经过Javac编译处理后,最终变为字节码指令,存储在方法表的属性集合Code属性中,但并非所有方法表都有Code属性,如抽象类或接口。
    Code属性表结构如下:

    名称 类型 数量 含义
    attribute_name_index u2 1 指向CONSTANT_Utf8_info类型常量的值固定为"Code"
    attribute_length u4 1 标识属性值的总长度
    max_stack u2 1 代表了操作数栈(Operand Stacks)深度的最大值
    max_locals u2 1 代表了局部变量所表示的存储空间
    code_length u4 1 字节码指令长度
    code u1 code_length 存储编译后产生的字节码指令
    exception_table_length u2 1
    exception_table exception_info exception_table_length
    attribute_count u2
    attributes attribute_info attribute_count

    Exceptions属性
    在方法表中与Code属性平级的一项属性,作用是列举出方法中可能抛出的受检查异常(Checked Exceptions)。
    Exceptions属性表结构如下:

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u4 1
    number_of_exceptions u2 1
    exception_index_table u2 number_of_exceptions

    LineNumberTable属性
    描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系,可以在编译的时候分别使用-g:none和-g:lines选项来取消或者要求生成这项信息。
    LineNumberTable属性表结构如下:

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u4 1
    line_number_table_length u2 1
    line_number_table line_number_info line_number_table_length

    ** LocalVariableTable属性**
    描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。
    LocalVariableTable属性表结构如下:

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u4 1
    local_varible_table_length u2 1
    local_variable_table local_variable_info local_varible_table_length

    局部变量表结构如下:

    名称 类型 数量 含义
    start_pc u2 1 代表局部变量的生命周期开始的字节码偏移量
    length u2 1 代表局部变量的生命周期作用范围覆盖的长度
    name_index u2 1 指向常量池中CONSTANT_Utf8_info型常量的索引,代表局部变量的名称
    descriptor_index u2 1 指向常量池中CONSTANT_Utf8_info型常 量的索引,代表局部变量的描述符
    index u2 1 局部变量在栈帧局部变量表中Slot的位置,当这个变量数据类型是64位类型时(double和long),它占用的Slot为index和index+1两个

    SourceFile属性
    记录生成这个Class文件的源码文件名称,sourcefile_index数据项是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源码文件的文件名。
    SourceFile属性表结构如下:

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u4 1
    sourcefile_index u2 1

    ConstantValue属性
    作用是通知虚拟机自动为静态变量赋值,只有被static关键字修饰的常量(类变量)才可以使用这项属性。目前Sun Javac编译器的选择是:如果同时使用final和static 来修饰一个变量,并且这个变量的数据类型是基本类型或者String的话,就生成ConstantValue属性来进行初 始化,如果这个变量没有被final修饰,或者并非基本类型及字符串,则将会选择在<clinit>方法中进行初始化。
    ConstantValue属性表结构如下:

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u4 1
    constantvalue_index u2 1

    InnerClasses属性
    用于记录内部类与宿主类之间的关联关系,"number_of_classes"代表需要记录多少个内部类信息。
    InnerClasses属性表结构如下:

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u4 1
    number_of_classes u2 1
    inner_classes inner_classes_info number_of_classes

    inner_classes_info表结构如下:

    名称 类型 数量 含义
    inner_class_info_index u2 1 指向常量池中CONSTANT_Class_info型常量的索引,代表内部类的符号引用
    outer_class_info_index u2 1 指向常量池中CONSTANT_Class_info型常量的索引,代表宿主类的符号引用
    inner_name_index u2 1 代表内部类的名称
    inner_class_access_flags u2 1 内部类的访问标志

    内部类访问标志如下:

    名称 数值 含义
    ACC_PUBLIC 0X0001 是否public
    ACC_PRIVATE 0X0002 是否private
    ACC_PROTECTED 0X0004 是否protected
    ACC_STATIC 0X0008 是否static
    ACC_FINAL 0X0010 是否final
    ACC_INTERFACE 0X0040 是否interface
    ACC_ABSTRACT 0X080 是否abstract
    ACC_SYNTHETIC 0X0100 是否非用户代码产生
    ACC_ANNOTATION 0X0400 是否是一个注解
    ACC_ENUM 0X0800 是否是一个枚举

    Deprecated和Synthetic属性
    两者都属于标志类型的布尔属性,只存在有和没有的区别,没有属性值的概念。Deprecated代表已经不再推荐使用;Synthetic代表字段或者方法并不是由Java源码直接产生的,而是由编译器自行添加的。
    其属性表结构如下:

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u2 1

    StackMapTable属性
    在JDK1.6发布后增加到Class文件规范中,它是一个复杂的变长属性,位于Code属性的属性表中,会在虚拟机类加载的字节码验证阶段被新类型检查验证器(Type Checker)使用,目的在于代替以前比较消耗性能的基于数据流分析的类型推导验证器;一个方法的Code属性最多只能有一个StackMapTable属性。
    StackMapTable属性表结构如下:

    名称 类型 数量
    attribute_name_index u2 1
    attribute_length u4 1
    number_of_entries u2 1
    stack_map_frame stack_map_frame entries 1

    费了九牛二虎之力,终于把理论部分整理完毕,是时候该整个demo让咱们一起玩玩真实的。说句实话,能把上面的部分看完,看到这里的是真英雄,会有一种“众里寻他千百度,蓦然回首那人却在灯火阑珊处”的感觉。
    demo会分三个部分展示,其一是Java源文件;jdk提供的用于分析class文件的工具javap命令,用"javap -v xxx.class"打开查看字节码情况;其三使用文档工具打开字节码文件的16进制查看,Windows推荐notepad++(需要安装插件),Mac推荐sublime,这里使用sublime,具体演示情况请下阅。
    demo展示Java源文件如下:

    package org.xwz.xwzdemo.jvm.classfile;
    
    import java.io.Serializable;
    
    /**
     * 解析class文件的演示类
     * 1/继承一个类,实现两个接口
     * */
    public class ReadClass extends Exception implements Runnable,Serializable {
    
        //常量信息
        private final static String XWZ_NAME = "阿伦故事";
    
        //public修饰int
        public int xwz_age = 29;
    
        //transient修饰boolean
        transient boolean isMale = true;
    
        //private变量
        private String address;
    
        //静态代码块
        static {
            long girlFriends = 8;
        }
    
        //有参构造
        ReadClass(String address){
            this.address = address;
        }
    
        //类方法
        static void initXwz(){
            System.out.println("---initXwz method---");
        }
    
        //普通方法
        public void whoAmI(){
            System.out.println("---who am i---");
        }
    
        //实现方法
        @Override
        public void run() {
            System.out.println("---this is run method");
        }
    }
    

    javap展示字节码文件如下:

    Classfile /Users/bjqxdn0921/xwz-test/xwz-demo/src/main/java/org/xwz/xwzdemo/jvm/classfile/ReadClass.class
      Last modified 2019-8-13; size 846 bytes
      MD5 checksum f534aebea36571f07dcc45a7b348cc1b
      Compiled from "ReadClass.java"
    public class org.xwz.xwzdemo.jvm.classfile.ReadClass extends java.lang.Exception implements java.lang.Runnable,java.io.Serializable
      minor version: 0
      major version: 52
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #12.#34        // java/lang/Exception."<init>":()V
       #2 = Fieldref           #11.#35        // org/xwz/xwzdemo/jvm/classfile/ReadClass.xwz_age:I
       #3 = Fieldref           #11.#36        // org/xwz/xwzdemo/jvm/classfile/ReadClass.isMale:Z
       #4 = Fieldref           #11.#37        // org/xwz/xwzdemo/jvm/classfile/ReadClass.address:Ljava/lang/String;
       #5 = Fieldref           #38.#39        // java/lang/System.out:Ljava/io/PrintStream;
       #6 = String             #40            // ---who am i---
       #7 = Methodref          #41.#42        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #8 = String             #43            // ---this is run method
       #9 = Long               8l
      #11 = Class              #44            // org/xwz/xwzdemo/jvm/classfile/ReadClass
      #12 = Class              #45            // java/lang/Exception
      #13 = Class              #46            // java/lang/Runnable
      #14 = Class              #47            // java/io/Serializable
      #15 = Utf8               XWZ_NAME
      #16 = Utf8               Ljava/lang/String;
      #17 = Utf8               ConstantValue
      #18 = String             #48            // 阿伦故事
      #19 = Utf8               xwz_age
      #20 = Utf8               I
      #21 = Utf8               isMale
      #22 = Utf8               Z
      #23 = Utf8               address
      #24 = Utf8               <init>
      #25 = Utf8               (Ljava/lang/String;)V
      #26 = Utf8               Code
      #27 = Utf8               LineNumberTable
      #28 = Utf8               whoAmI
      #29 = Utf8               ()V
      #30 = Utf8               run
      #31 = Utf8               <clinit>
      #32 = Utf8               SourceFile
      #33 = Utf8               ReadClass.java
      #34 = NameAndType        #24:#29        // "<init>":()V
      #35 = NameAndType        #19:#20        // xwz_age:I
      #36 = NameAndType        #21:#22        // isMale:Z
      #37 = NameAndType        #23:#16        // address:Ljava/lang/String;
      #38 = Class              #49            // java/lang/System
      #39 = NameAndType        #50:#51        // out:Ljava/io/PrintStream;
      #40 = Utf8               ---who am i---
      #41 = Class              #52            // java/io/PrintStream
      #42 = NameAndType        #53:#25        // println:(Ljava/lang/String;)V
      #43 = Utf8               ---this is run method
      #44 = Utf8               org/xwz/xwzdemo/jvm/classfile/ReadClass
      #45 = Utf8               java/lang/Exception
      #46 = Utf8               java/lang/Runnable
      #47 = Utf8               java/io/Serializable
      #48 = Utf8               阿伦故事
      #49 = Utf8               java/lang/System
      #50 = Utf8               out
      #51 = Utf8               Ljava/io/PrintStream;
      #52 = Utf8               java/io/PrintStream
      #53 = Utf8               println
    {
      public int xwz_age;
        descriptor: I
        flags: ACC_PUBLIC
    
      transient boolean isMale;
        descriptor: Z
        flags: ACC_TRANSIENT
    
      org.xwz.xwzdemo.jvm.classfile.ReadClass(java.lang.String);
        descriptor: (Ljava/lang/String;)V
        flags:
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Exception."<init>":()V
             4: aload_0
             5: bipush        29
             7: putfield      #2                  // Field xwz_age:I
            10: aload_0
            11: iconst_1
            12: putfield      #3                  // Field isMale:Z
            15: aload_0
            16: aload_1
            17: putfield      #4                  // Field address:Ljava/lang/String;
            20: return
          LineNumberTable:
            line 23: 0
            line 13: 4
            line 15: 10
            line 24: 15
            line 25: 20
    
      public void whoAmI();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #6                  // String ---who am i---
             5: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 28: 0
            line 29: 8
    
      public void run();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
             3: ldc           #8                  // String ---this is run method
             5: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
             8: return
          LineNumberTable:
            line 33: 0
            line 34: 8
    
      static {};
        descriptor: ()V
        flags: ACC_STATIC
        Code:
          stack=2, locals=2, args_size=0
             0: ldc2_w        #9                  // long 8l
             3: lstore_0
             4: return
          LineNumberTable:
            line 20: 0
            line 21: 4
    }
    SourceFile: "ReadClass.java"
    

    sublime展示字节码文件如下:

    cafe babe 0000 0034 0036 0a00 0c00 2209
    000b 0023 0900 0b00 2409 000b 0025 0900
    2600 2708 0028 0a00 2900 2a08 002b 0500
    0000 0000 0000 0807 002c 0700 2d07 002e
    0700 2f01 0008 5857 5a5f 4e41 4d45 0100
    124c 6a61 7661 2f6c 616e 672f 5374 7269
    6e67 3b01 000d 436f 6e73 7461 6e74 5661
    6c75 6508 0030 0100 0778 777a 5f61 6765
    0100 0149 0100 0669 734d 616c 6501 0001
    5a01 0007 6164 6472 6573 7301 0006 3c69
    6e69 743e 0100 1528 4c6a 6176 612f 6c61
    6e67 2f53 7472 696e 673b 2956 0100 0443
    6f64 6501 000f 4c69 6e65 4e75 6d62 6572
    5461 626c 6501 0006 7768 6f41 6d49 0100
    0328 2956 0100 0372 756e 0100 083c 636c
    696e 6974 3e01 000a 536f 7572 6365 4669
    6c65 0100 0e52 6561 6443 6c61 7373 2e6a
    6176 610c 0018 001d 0c00 1300 140c 0015
    0016 0c00 1700 1007 0031 0c00 3200 3301
    000e 2d2d 2d77 686f 2061 6d20 692d 2d2d
    0700 340c 0035 0019 0100 152d 2d2d 7468
    6973 2069 7320 7275 6e20 6d65 7468 6f64
    0100 276f 7267 2f78 777a 2f78 777a 6465
    6d6f 2f6a 766d 2f63 6c61 7373 6669 6c65
    2f52 6561 6443 6c61 7373 0100 136a 6176
    612f 6c61 6e67 2f45 7863 6570 7469 6f6e
    0100 126a 6176 612f 6c61 6e67 2f52 756e
    6e61 626c 6501 0014 6a61 7661 2f69 6f2f
    5365 7269 616c 697a 6162 6c65 0100 0ce9
    98bf e4bc a6e6 9585 e4ba 8b01 0010 6a61
    7661 2f6c 616e 672f 5379 7374 656d 0100
    036f 7574 0100 154c 6a61 7661 2f69 6f2f
    5072 696e 7453 7472 6561 6d3b 0100 136a
    6176 612f 696f 2f50 7269 6e74 5374 7265
    616d 0100 0770 7269 6e74 6c6e 0021 000b
    000c 0002 000d 000e 0004 001a 000f 0010
    0001 0011 0000 0002 0012 0001 0013 0014
    0000 0080 0015 0016 0000 0002 0017 0010
    0000 0004 0000 0018 0019 0001 001a 0000
    003d 0002 0002 0000 0015 2ab7 0001 2a10
    1db5 0002 2a04 b500 032a 2bb5 0004 b100
    0000 0100 1b00 0000 1600 0500 0000 1700
    0400 0d00 0a00 0f00 0f00 1800 1400 1900
    0100 1c00 1d00 0100 1a00 0000 2500 0200
    0100 0000 09b2 0005 1206 b600 07b1 0000
    0001 001b 0000 000a 0002 0000 001c 0008
    001d 0001 001e 001d 0001 001a 0000 0025
    0002 0001 0000 0009 b200 0512 08b6 0007
    b100 0000 0100 1b00 0000 0a00 0200 0000
    2100 0800 2200 0800 1f00 1d00 0100 1a00
    0000 2100 0200 0200 0000 0514 0009 3fb1
    0000 0001 001b 0000 000a 0002 0000 0014
    0004 0015 0001 0020 0000 0002 0021 
    

    相关文章

      网友评论

          本文标题:JVM系列之class文件剖析

          本文链接:https://www.haomeiwen.com/subject/dpnidctx.html