zoukankan      html  css  js  c++  java
  • Java虚拟机-类文件结构

    类文件结构

    Class类文件的结构

    任何一个Class文件都对应着唯一一个类或者接口的定义信息,但是类或者接口并不一定都要定义在文件里(例如类也可以通过类加载器直接生成)。Class文件是一组以8位字节为基础单位的二进制流,各项数据项目严格按照顺序紧凑地排列在Class文件中。Class文件格式采用类似C语言结构体的伪结构来存储数据,包括两种数据类型:无符号数和表。
    无符号数属于基本的数据类型,以u1,u2,u4,u8来代表1,2,4,8个字节的无符号数。可以用来描述数字、索引引用、数量值或者按照utf-8编码构成字符串值。
    表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性的以“_info”结尾。表用于描述有层次关系的符合结构数据。

    魔数与Class文件的版本

    Class文件的头4个字节称之为魔数(Magic Number),用于确认这个文件是否为一个能被虚拟机接受的Class文件。Class文件的魔数值为:0xCAFEBABE(咖啡宝贝?),这个魔数在Java还是“Oak”的时候就被确认了。
    之后4个字节存储的是Class文件的版本号,第5第6个字节是次版本号(Minor Version),第7第8是主版本号(Major Version)。Java的版本号是从45开始的,1.1之后大版本发布主版本号向上加1,高版本JDK能向下兼容但不能运行以后版本的Class文件。

    常量池

    主次版本号之后时常量池,Class文件之中的资源仓库,关联其他项目最多的数据类型,也是占用Class文件空间最大的项目之一。由于常量数量不固定,所以常量池的入口需要防止一项u2类型数据,代表常量池容量计数值(constant_pool_count)。
    常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。字面量比较接近与Java语言层面的常量概念,如文本字符串、声明为final的常量值等。符号引用则属于编译原理方面的概念,包括下面三类常量:

    • 类和接口的全限定名(Fully Qualified Name)
    • 字段的名称和描述符(Descriptor)
    • 方法的名称与描述符

    Java代码在Javac编译的时候,不像C或C++有连接这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。虚拟机运行时,从常量池获得对应的符号引用,在类创建时或运行时解析、翻译到具体的内存地址之中。

    访问标志

    常量池之后紧接着两个字节代表访问标志(access_flag),用于识别一些类或者接口层次的访问信息。包括:是类还是接口,是否public,是否abstract,是否final等。

    标志名称 标志值 含义
    ACC_PUBLIC 0x0001 是否为public
    ACC_FINAL 0x0010 是否为final
    ACC_SUPER 0x0020 是否允许使用invokespecial字节码指令的新语意,由于该语意在JDK1.0.2发生过改变,所以之后编译出来的类这个标志必须为真
    ACC_INTERFACE 0x0200 是否为接口
    ACC_ABSTRACT 0x0400 是否为抽象类
    ACC_SYNTHETIC 0x1000 是否由编译器自动产生的
    ACC_ANNOTATION 0x2000 是否为注解类
    ACC_ENUM 0x4000 是否为enum

    例如public的普通类,所以ACC_PUBLIC ACC_SUPE标志为真,其他标志为假。因此access_flags的值为: 0x0001|0x0020=0x0021
    前5个是Java虚拟机规范定义标志,1.5之后增加了后面的三种。

    类索引、父类索引和接口索引集合

    类索引(this_class)父类索引(super_class)是u2类型数据,接口索引集合(interfaces)时一组u2类型的数据集合。按照顺序排在访问标志之后。

    类型 名称 数量
    u2 类索引 1
    u2 父类索引 1
    u2[n] 接口索引 n

    字段表集合

    字段表(field_info)用于描述接口或者类中声明的变量。字段包括类级变量以及实例级别变量,但不包括方法内部声明的局部变量。修饰符使用标志量表示,字段名称类型使用常量池中的常量来描述。
    access_flag的含义

    标志名称 标志值 含义
    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 0x1000 是否由编译器自动产生的
    ACC_ENUM 0x4000 是否为enum
    类型 名称 数量
    u2 access_flag 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_count

    方法表集合

    方法表(method_info)用于描述接口或者类中声明的方法。
    因为volatile关键字和transient关键字不能修饰方法,所以方法表的访问标志中没有了 ACC_VOLATILE标志和ACC_TRANSIENT标志。与之相对的,synchronized、native、strictfp 和abstract关键字可以修饰方法,所以方法表的访问标志中增加了ACC_SYNCHRONIZED、 ACC_NATIVE、ACC_STRICTFP和ACC_ABSTRACT标志。

    标志名称 标志值 含义
    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 0x0080 是否接受不定参数
    ACC_NATIVE 0x0100 是否为native
    ACC_ABSTRACT 0x0400 是否为abstract
    ACC_STRICTFP 0x0800 是否为strictfp
    ACC_SYNTHETIC 0x1000 是否由编译器自动产生的
    类型 名称 数量
    u2 access_flag 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_count

    属性表集合

    属性表(attribute_info)之前反复出现,在Class文件、字段表、方法表都可以携带字节的属性表集合,用于描述某些场景专有信息。
    顺序、长度、内容的要求不像前面那么严格。
    不要求各个属性表具有严格顺序,并且只要不与已有属性名重复,任何人 实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不 认识的属性。

    属性名称 使用位置 含义
    Code 方法表 Java代码编译成的字节码指令
    ConstantValue 字段表 final关键字定义的常量值
    Deprecated 类、方法表、字段表 声明为deprecated的方法和字段
    Exceptions 方法表 方法抛出的异常
    EnclosingMethod 类文件 仅当一个类为局部类或者匿名类才有这个属性,用于标识这个类所在的外围方法

    ...

    属性表结构:

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

    完整结构描述

    表用于描述有层次关系的符合结构的数据,整个Class文件本质上就是一张表,其构成成分就是如下的数据项:

    数据类型 名称 数量
    u2 attribute_name_index 1
    u4 attribute_length 1
    u2 max_stack 1
    u2 max_locals 1
    u4 code_length 1
    u1 code code_length
    u2 exception_table_length 1
    exception_info exception_table exception_table_length
    u2 attribute_count 1
    attribute_info attributes attribute_count

    实例

    为了能够更好的理解,我们拿一个实际的类文件来分析一下。

    源码

    package com.software5000.base.jsql;
    
    public class TestClass {
        public String strField;
    
        public int intField;
    
        public String getStrField() {
            return strField;
        }
    
        public void setStrField(String strField) {
            this.strField = strField;
        }
    
        public int getIntField() {
            return intField;
        }
    
        public void setIntField(int intField) {
            this.intField = intField;
        }
    }
    
    

    这是一个很简单的测试类,两个属性,以及对应的getter/setter方法。

    Class文件

    Class文件

    分析

    针对类文件以及前面的概念对比分析

    魔数、Class版本

    CAFEBABE - 魔数
    00000034 - 版本 52 :jdk 1.8

    常量池长度

    0020 - 32-1 长度31个常量项

    常量池内容

    01——
    0A tag表示 constant_methodref_info
    0005 index no. 5 contant_class_info
    001B index no. 27 constant_nameandtype

    02——
    09 tag constant_fieldref_info
    0004 index no.4 constant_class_info
    001C index no.28 constatn_nameandtype

    03——
    09 tag constant_fieldref_info
    0004 index no.4 constant_class_info
    001D index no.29 constatn_nameandtype

    04——
    07 tag constant_class_info
    001E index no.30

    05——
    07 tag constant_class_info
    001F index no.31

    06——
    01 tag constant_utf8_info
    0008 length 8 byte
    7374 7246 6965 6C64 strField

    07——
    01 tag constant_utf8_info
    0012 length 18 byte
    4C6A 6176 612F 6C61 6E67 2F53 7472 696E 673B Ljava/lang/String;

    08——
    01 tag constant_utf8_info
    0008 length 8 byte
    696E 7446 6965 6C64 intField

    09——
    01 tag constant_utf8_info
    0001 length 1 byte
    49 I

    10——
    01 tag constant_utf8_info
    0006 length 6 byte
    3C69 6E69 743E

    11——
    01 tag constant_utf8_info
    0003 length 3 byte
    2829 56 ()V

    12——
    01 tag constant_utf8_info
    0004 length 4 byte
    436F 6465 Code

    13——
    01 tag constant_utf8_info
    000F length 15 byte
    4C69 6E65 4E75 6D62 6572 5461 626C 65 LineNumberTable

    14——
    01 tag constant_utf8_info
    0012 length 18 byte
    4C6F 6361 6C56 6172 6961 626C 6554 6162 6C65 LocalVariableTable

    15——
    01 tag constant_utf8_info
    0004 length 4 byte
    7468 6973 this

    16——
    01 tag constant_utf8_info
    0026 length 38 byte
    4C63 6F6D 2F73 6F66 7477 6172 6535 3030 302F 6261 7365 2F6A 7371 6C2F 5465 7374 436C 6173 733B Lcom/software5000/base/jsql/TestClass;

    17——
    01 tag constant_utf8_info
    000B length 11 byte
    67 6574 5374 7246 6965 6C64 getStrField

    18——
    01 tag constant_utf8_info
    0014 length 20 byte
    2829 4C6A 6176 612F 6C61 6E67 2F53 7472 696E 673B ()Ljava/lang/String;

    19——
    01 tag constant_utf8_info
    000B length 11 byte
    7365 7453 7472 4669 656C 64 setStrField

    20——
    01 tag constant_utf8_info
    0015 length 21 byte
    284C 6A61 7661 2F6C 616E 672F 5374 7269 6E67 3B29 56 (Ljava/lang/String;)V

    21——
    01 tag constant_utf8_info
    000B length 11 byte
    6765 7449 6E74 4669 656C 64 getIntField

    22——
    01 tag constant_utf8_info
    0003 length 3 byte
    2829 49 ()I

    23——
    01 tag constant_utf8_info
    000B length 11 byte
    7365 7449 6E74 4669 656C 64 setIntField

    24——
    01 tag constant_utf8_info
    0004 length 4 byte
    2849 2956 (I)V

    25——
    01 tag constant_utf8_info
    000A length 10 byte
    536F 7572 6365 4669 6C65 SourceFile

    26——
    01 tag constant_utf8_info
    000E length 14 byte
    5465 7374 436C 6173 732E 6A61 7661 TestClass.java

    27——
    0C tag constant_Name-andtype-info
    000A index no.10
    000B index no.11

    28——
    0C tag constant_Name-andtype-info
    0006 index no.6
    0007 index no.7

    29——
    0C tag constant_Name-andtype-info
    0008 index no.8
    0009 index no.9

    30——
    01 tag constant_utf8_info
    0024 length 36 byte
    5636F 6D2F 736F 6674 7761 7265 3530 3030 2F62 6173 652F 6A73 716C 2F54 6573 7443 6C61 7373 com/software5000/base/jsql/TestClass

    31——
    01 tag constant_utf8_info
    0010 length 16 byte
    6A61 7661 2F6C 616E 672F 4F62 6A65 6374 java/lang/Object

    访问标志

    0021 访问标志 (ACC_PUBLIC,ACC_SUPER两个标志位为真)

    类索引、父类索引、接口索引

    0004 类索引 no.4
    0005 父类索引 no.5
    0000 接口索引集合 0

    字段表集合

    0002 fields_counts 2个字段表数据

    No.1 field
    0001 access_flag public
    0006 index no.6 name_index
    0007 index no.7 descriptor_index
    0000 attributes_count

    No.2 field
    0001 access_flag public
    0008 index no.8 name_index
    0009 index no.9 descriptor_index
    0000 attributes_count

    方法表集合

    0005 methods_count 5个方法

    No.1 method
    0001 access_flag public
    000A index no.10 name_index
    000B index no.11 descriptor_index ()V
    0001 attributes_count 1个
    000C attributes_info LineNumberTable

    剩余的待进一步补充。。。

    总结

    Class文件的结构理解,说实话对于编码或者性能优化等方面没有什么特别大的帮助,但是能够帮我们更好的理解Java及其设计的思想。我们从Class文件的结构设计中也能够学习到一些模式,这些东西可能会在后续的研发过程提供一些解决问题或者设计方案的思路。
    这才是最重要的。

  • 相关阅读:
    Centos7部署Django
    CentOS7 常用命令
    window安装django-auth-ldap
    解决group by分组默认获取id最小的一条数据
    js基础之if判断
    java操作word转pdf多选框问题(linux服务器下)
    震惊,男默女泪,使用nginx代理,并进行ip拦截
    高德地图在marker里设置自定义属性
    angular项目启动错误
    本地连接虚拟机内的kafka遇到的问题
  • 原文地址:https://www.cnblogs.com/pluto4596/p/11870986.html
Copyright © 2011-2022 走看看