zoukankan      html  css  js  c++  java
  • JVM虚拟机

    概述

    Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎都是程序运行的必要数据。当遇到需要占用8位字节以上空间的数据项时,会按照高位在前的方式分割成若干个8位字节进行存储。

    Class文件格式中只有两种数据类型:无符号数

    • 无符号数属于最基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
    • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,以“_info”结尾。表用来描述有层次关系的符合结构的数据。

    整个Class文件本质上就是一张表。

    无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,称这一系列连续的某一类型的数据位某一类型的集合。

    class文件的组织结构

    1. 魔数
    2. 本文件的版本信息
    3. 常量池
    4. 访问标志
    5. 类索引
    6. 父类索引
    7. 接口索引集合
    8. 字段表集合
    9. 方法表集合
    10. 属性表集合

    1. 魔数

    每个Class文件的头4个字节称为魔数(Magic Number),唯一作用就是确定这个文件是否为一个能被虚拟机接受的Class文件。

    魔数的表示是用16进制的数:0xCAFEBABE 来表示。


    2. 本文件的版本信息

    在魔数后面的4个字节存储的就是Class文件的版本号:

    • 第5、第6个字节是次版本号(Minor Version)
    • 第7、第8个字节是主版本号(Major Version)

    Java版本号的计算

    Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布主版本号向上加1。

    高版本的JDK能向下兼容以前版本的Class文件,但不能兼容以后版本的Class文件,即使文件格式并未发生变化,虚拟机也拒绝执行超过其版本号的Class文件。


    3. 常量池

    3.1. 常量池的概念

    常量池可以理解为Class文件之中的资源仓库,他是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在Class文件中第一个出现的表类型数据项目。

    常量池中主要存放两大类常量:

    • 字面量:接近于Java语言层面的常量概念,如文本字符串、声明为final的常量
    • 符号引用:就是我们定义的各种名字,包括下面三类:
      • 类和接口的全限定名
      • 字段的名称和描述符
      • 方法的名称和描述符

    3.2. 常量池的特点

    1. 常量池长度不固定

      常量池的大小是不固定的,因此常量池开头放置一个u2类型的无符号数,用来存储当前常量池的容量。JVM根据这个值就知道常量池的头尾来。

      这个无符号数从1开始,不是通常的从0开始

    2. 常量池中的常量由二维表来表示

      常量池开头有个常量池容器计数器,接下来就全是一个个常量了,只不过常量都是由一张张二维表构成,除了记录常量的值以外,还记录当前常量的相关信息。

    3. Class文件之中的资源仓库

    4. Class文件结构中与其他项目关联最多的数据类型

    5. 占用Class文件空间最大的数据项目之一

    3.3. 常量池中常量的类型

    刚才介绍了,常量池中的常量大体上分为:字面值常量 和 符号引用。在此基础上,根据常量的数据类型不同,又可以被细分为14种常量类型。这14种常量类型都有各自的二维表示结构。每种常量类型的头1个字节都是tag,用于表示当前常量属于14种类型中的哪一个。

    以CONSTANT_Class_info常量为例,它的二维表示结构如下:
    CONSTANT_Class_info表

    类型 名称 数量
    u1 tag 1
    u2 name_index 1

    tag表示当前常量的类型(当前常量为CONSTANT_Class_info,因此tag的值应为7,表示一个类或接口的全限定名);

    name_index表示这个类或接口全限定名的位置。它的值表示指向常量池的第几个常量。它会指向一个CONSTANT_Utf8_info类型的常量,它的二维表结构如下:
    CONSTANT_Utf8_info表:

    类型 名称 数量
    u1 tag 1
    u2 length 1
    u1 bytes length

    CONSTANT_Utf8_info表示字符串常量;

    tag表示当前常量的类型,这里应该是1;

    length表示这个字符串的长度;

    bytes为这个字符串的内容(采用缩略的UTF8编码)


    4. 访问标志

    访问标志(access_flags)占用两个字节,这个标志用于识别一些类或者接口层次的访问信息:

    • 这个Class是类还是接口
    • 是否定位为public类型
    • 是否定义为abstract类型
    • 是否被声名为final
    • .......

    访问标志中一共有16个标志位可用,当前只用了8个,其他要求一律为0。


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

    类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,接口索引集合(interfaces)是一组u2类型的数据的集合。

    Class文件由这三项数据来确定这个类的继承关系:

    • 类索引确定这个类的全限定名

    • 父类索引用于确定这个类的父类的全限定名

      java.lang.Object外,所有Java类的父类索引都不为0。

    • 接口索引集合描述这个类实现了哪些接口

      被实现的接口按接口顺序从左到右排列在接口索引集合中。

    类索引、父类索引和接口索引集合都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的所引致指向一个类型为CONSTANT_Class_info的类描述符常量,该常量的bytes字段记录了本类、父类的全限定名。

    由于一个类的接口可能有好多个,因此需要用一个集合来表示接口索引,它在类索引和父类索引之后。这个集合头两个字节表示接口索引集合的长度,接下来就是接口的名字索引。


    6. 字段表集合

    字段表用于描述接口或者类中生命的变量。字段包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。

    类型 名称 数量
    u2 access_flags 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_count
    • access_flags

      字段的访问标志。

    • name_index

      本字段名称的索引,指向一个CONSTANT_Class_info类型的常量,存储了本字段的名字等信息。

    • descriptor_index

      字段和方法的描述符,用于描述本字段在Java中的数据类型等信息。

    • attributes_count

      属性表集合的长度。

    • attributes

      属性表集合。

    全限定名

    全限定名就是把类全名中的.替换成了/而已,为了使连续的多个全限定名之间不产生混淆,在使用时最后一般会加入一个;来表示全限定名结束。

    简单名称

    简单名称就是指没有类型和参数修饰的方法或者字段名称。

    描述符

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

    在描述符中:

    • 基本数据类型以及代表无返回值的void类型都用一个大写字符来表示。
    • 对象类型则用字符L加对象的全限定名来表示。
    标识字符 含义 标识字符 含义
    B 基本类型byte J 基本类型long
    C 基本类型char S 基本类型short
    D 基本类型double Z 基本类型boolean
    F 基本类型float V 特殊类型void
    I 基本类型int L 对象类型,如Ljava/lang/Object
    1. 对于数组类型,每一唯独将使用一个前置的“[”字符来描述,如一个定义为java.lang.String[][]类型的二维数组,将被记录为:[[Ljava/lang/String]],一个整形数组int[]将被记录为:[I

    2. 对于描述方法,按照“先参数列表,后返回值的顺序描述”,参数列表按照参数的严格顺序放在一组小括号“()”内。例如:void inc()的描述符为:()V。方法java.lang.String.toString()的描述符为:()Ljava/lang/String

      方法int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset, int targetCount, int fromIndex)的描述符为:([CII[CIII)I


    7. 方法表集合

    在class文件中,所有的方法以二维表的形式存储,每张表来表示一个函数,一个类中的所有方法构成方法表的集合。
    方法表的结构和字段表的结构一致,只不过访问标志和属性表集合的可选项有所不同。

    类型 名称 数量
    u2 access_flags 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_count

    方法中的Java代码,经过编译器编译成字节码指令后,存放在方法属性表集合中一个名为“Code”的属性里面,属性表作为Class文件格式中最具拓展性的一种数据项目,在第8节中讲。


    8. 属性表集合

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

    属性表集合对于各个属性表的顺序不再做严格要求,只要不与已有属性名重复即可。任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性。

    为了正确解析Class文件,虚拟机与定义了一些虚拟机实现应当能识别的属性,对于这些属性,它的名称需要从常量池中引用一个CONSTANT_Utf9_info类型的常量来表示,而属性值的结构则是完全自定义的。

    一个符合规则的属性表应该有如下结构:

    类型 名称 数量
    u2 attribute_name_index 1
    u4 attribute_length 1
    u1 info attribute_length
  • 相关阅读:
    Hihocoder 1275 扫地机器人 计算几何
    CodeForces 771C Bear and Tree Jumps 树形DP
    CodeForces 778D Parquet Re-laying 构造
    CodeForces 785E Anton and Permutation 分块
    CodeForces 785D Anton and School
    CodeForces 785C Anton and Fairy Tale 二分
    Hexo Next 接入 google AdSense 广告
    如何统计 Hexo 网站的访问地区和IP
    Design and Implementation of Global Path Planning System for Unmanned Surface Vehicle among Multiple Task Points
    通过ODBC接口访问人大金仓数据库
  • 原文地址:https://www.cnblogs.com/yisany/p/10609195.html
Copyright © 2011-2022 走看看