zoukankan      html  css  js  c++  java
  • jvm理论-class文件

    当JVM运行Java程序的时候,它会加载对应的class文件,并提取class文件中的信息存放在JVM的方法区内存中。

    Class文件组成

    1、Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按顺序紧凑排列在Class文件中,中间没有分隔符。所以Class文件中存储的内容几乎全部是程序运行的必要数据。

    2、当遇到占据8位字节以上空间的数据项时,会按照高位在前的方式, 分割成若干个8位字节进行存储。

    ClassFile {
        u4 magic;
        u2 minor_version;
        u2 major_version;
        u2 constant_pool_count;
        cp_info contant_pool[constant_pool_count – 1];
        u2 access_flags;
        u2 this_class;
        u2 super_class;
        u2 interfaces_count;
        u2 interfaces[interfaces_count];
        u2 fields_count;
        field_info fields[fields_count];
        u2 methods_count;
        method_info methods[methods_count];
        u2 attributes_count;
        attribute_info attributes[attributes_count];
    }

    Class文件数据类型

    无符号数

    1、基本数据类型,以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节。

    2、无符号数可以用来描述数字、索引引用、数量值和按照UTF8编码构成字符串值。

    1、表用来描述有层次关系的的复合数据结构。

    2、表是由无符号数或者其他表作为数据项构成的复合数据结构。

    3、所有表都以_info结尾。

    4、整个class文件本质上就是一张表。

    item1:magic(魔数)[u4]:0xCAFEBABE

    辨别class文件与非class文件

    item2:minor_version、major_version(次、主版本号) [u2,u2]

    随着Java技术的发展,class文件的格式会发生变化。版本号的作用在于使得虚拟机能够认识当前加载class的文件格式。从而准确的提取class文件信息。

    item3:常量池(constant_pool_count 、constance_pool)[u2,cp_info]

    常量池内容

    常量池存放两大类常量:字面量(Literal)和符号引用(Synbolic Reference)

    一、字面量

    1、文本字符串

    2、声明为final的常量值

    final修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量

    3、基本数据类型

    4、其他

    二、符号引用

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

    常量池结构

    JVM会将每一个常量构成一个常量表,每个常量表都有自己的入口地址。而实际上在JVM会将这些常量表存储在方法区中一块连续的内存空间中,因此class文件会根据常量表在常量池中的位置对其进行索引。比如常量池中的第一个常量表的索引值就是1,第二个就是2。有的时候常量表A需要常量表B的内容,则在常量表A中会存储常量表B的索引值x。而constant_pool_count就记录了有多少个常量表,或则所有多少个索引值。实际上,常量池中没有索引值为0的常量表,但这缺失的索引值也被记录在 constant_pool_count中,因此 constant_pool_count等于常量表的数量加1。

    常量池计数器(constant_pool_count):常量表(cp_info)数量+1。

    常量表(cp_info):constant_pool_count-1个。

    常量表结构(cp_info)

    每个常量表(cp_info) 都会对应记录着class文件中的某中类型的字面量

    这14种表有一个共同的特点,就是开始的第一位是一个u1类型的标志位(tag,就是上表中的标志这一列),代表当前这个常量属于哪种常量类型。

     

    字符串常量池:http://www.cnblogs.com/tenghoo/p/jvm_string.html

    基本类型常量池:http://www.cnblogs.com/tenghoo/p/jvm_primitive.html

    item4:访问标志(access_flags)[u2]  

    表明该class文件中定义的是类还是接口,访问修饰符是public还是缺省。类或接口是否是抽象的。类是否是final的。

    item5:类索引(this_class) [u2]、父类索引(super_class)[u2]、接口索引集合(inteface_count、interfaces )[u2,u2]

    1、Class文件通过这三项数据来确定这个类的继承关系。

    2、类索引用于确定这个类的全限定名。

    3、父类索引用于确定这个类的父类的全限定名。由于Java语言不允许多重继承,所以父类索引只有一个。

    4、除了java.lang.Object之外,所有的Java类都有父类,因此除了java.lang.Object外,所有Java类的父类索引都不为0。

    5、接口索引集合就用来描述这个类实现了哪些接口。

    6、类索引和父类索引用两个u2类型的索引值表示,它们各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串。

    7、对于接口索引集合,入口的第一项:u2类型的数据为接口计数器(interfaces_count),表示索引表的容量。如果该类没有实现任何接口,则该计数器值为0,后面接口的索引表不再占用任何字节。

    item6:字段表集合(field_count,fields) [u2,field_info]

    1、字段表(field_info)用于描述接口或者类中声明的变量。

    2、字段(field)包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。

    field_info表格式

    access_flags(2byte 访问修饰符)

    name_index(2byte 存储字段名的常量表在常量池中的索引)

    description_index(2byte 存储字段的所属类型的常量表在常量池中的索引)

    attribute_count(2byte 属性表的数量)

    attribute (属性)

    其中attribute是由多个attribute_info组成。而JVM规范定义了字段的三种属性:ConstanceValue、Deprecated和Synthetic。

    item7:方法表集合(method_count、methods) [u2,method_info] 

    method_count、methods 与字段类似,method_count表明类中方法的数量和每个方法的常量表的索引。methods表明了不同长度的method_info表的序列。

    method_info表格式

    access_flags(2byte 访问修饰符)

    name_index(2byte 存储方法名的常量表在常量池中的索引)

    description_index(2byte 存储方法的返回类型和参数类型的常量表在常量池中的索引)

    attribute_count(2byte 属性表的数量)

    attribute (属性)

    其中方法的属性JVM规定了四种:Code,Deprecated,Exceptions,Synthetic。

    简单名称:方法名如:fun,字段名如:str1

    全限定名:com/mobjia/clazz/TestClass

    描述符:

    描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。

    根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)以及代表无返回值的void类型都用一个大写字符来表示,而对象类型则用字符L加对象的全限定名来表示。

    数组类型:每一维度将使用一个前置的“[”字符来描述,如一个定义为“java.lang.String[][]”类型的二维数组,将被记录为:“[[Ljava/lang/String;”,一个整型数组“int[]”将被记录为“[I”。

    用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号“()”之内。

    void inc()的描述符为()V
    java.lang.String toString()的描述符()Ljava/lang/String;
    int indexOf(char[]source,int sourceOffset,int sourceCount,char[]target,int targetOffset,inttargetCount,int fromIndex)的描述符 ([CII[CIII)I

    item8:属性表集合(attribute_count、attributes) [u2,attribute_info] 

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

    与Class文件中其他的数据项目要求严格的顺序、长度和内容不同,属性表集合的限制稍微宽松了一些,不再要求各个属性表具有严格顺序,并且只要不与已有属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性。为了能正确解析Class文件,《Java虚拟机规范(第2版)》中预定义了9项虚拟机实现应当能识别的属性,而在最新的《Java虚拟机规范(Java SE 7)》版中,预定义属性已经增加到21项。

    对于每个属性,它的名称需要从常量池中引用一个CONSTANT_Utf8_info类型的常量来表示,而属性值的结构则是完全自定义的,只需要通过一个u4的长度属性去说明属性值所占用的位数即可。一个符合规则的属性表应该满足下表中所定义的结构。

    Class实例分析工具

    jvm class分析工具:

    WinHe十六进制编辑器

    https://sourceforge.net/projects/classeditor/files/

    http://xuantan.iteye.com/blog/2030651

    参考:

    http://blog.csdn.net/luanlouis/article/details/39960815

    http://blog.csdn.net/a616413086/article/details/52250638

    http://hxraid.iteye.com/blog/687660

  • 相关阅读:
    Matlab 绘制三维立体图(以地质异常体为例)
    Azure DevOps的variable group实现array和hashtable参数的传递
    Azure DevOps 利用rest api设置variable group
    Azure AADSTS7000215 其中一种问题的解决
    Power BI 实现实时更新Streaming Dataset
    AAD Service Principal获取azure user list (Microsoft Graph API)
    Matlab 沿三维任意方向切割CT图的仿真计算
    Azure Powershell script检测登陆并部署ARM Template
    Azure KeyVault设置策略和自动化添加secrets键值对
    Azure登陆的两种常见方式(user 和 service principal登陆)
  • 原文地址:https://www.cnblogs.com/tenghoo/p/jvm_class.html
Copyright © 2011-2022 走看看