zoukankan      html  css  js  c++  java
  • 第六章 类文件结构(1)

    代码编译的结果从本地机器码变成字节码,是存储格式发展的一小步,确实编程语言发展的一大步。

    6.1 概述

      计算机只认识0和1,所以我们写的程序被编译成二进制格式才能被计算机执行;但是由于虚拟机以及大量建立在虚拟机之上的程序语言蓬勃发展,把我们编写的程序编译成本地机器码已经不是唯一选择,越来越多的程序语言选择了与操作系统、和机器指令集无关的、平台中立的的格式作为程序编译后的存储格式

    6.2 无关性的基石

      如果只有x86一种指令集,只有windows一种操作系统,那么就不会有Java语言的出现。

      “与平台无关”的理想最终只有实现在操作系统以上的应用层。

      各种不同平台的Java虚拟机,以及所有平台都统一支持的程序存储格式-----字节码(Byte Code) 是构成平台无关的基石。

      虚拟机不仅平台无关,还有一种是语言无关性,时至今日,商业企业和开源机构已经在Java语言之外发展出一大批运行在Java虚拟机上的语言,如Kotlin、Clojure、Groovy、JRuby、JPython、Scala.

      实现语言无关性的基础仍然是虚拟机和字节码存储格式。Java虚拟机不与任何语言绑定(包括java),它只与“Class文件”这种特定的二进制文件格式关联,Class文件中包含了java虚拟机指令集、符号表以及若干其他辅助信息。作为一个通用的、与机器无关的执行平台,任何其他语言的实现者都可以将Java虚拟机作为他们语言的运行基础,以Class文件作为他们的产品的交付媒介。Java编译器可以把Java代码编译成Class文件,JRuby等其他语言的编译器一样可以把他们的源码编译成Class文件。虚拟机丝毫不关心Class的来源是什么语言。

      

     6.3 Class类文件的结构

      Java技术能够一致保持着非常良好的向后兼容性,Class文件结构的稳定功不可没。

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

      Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型:“无符号数”和”表“,

    • “无符号数”:属于基本的数据类型,以u1、u2、 u4、 u8分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
    • ”表“:是由多个无符号数或者其他表作为数据项构成的复合数据类型,为了便于区分,所有表的命名都习惯以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上也可以看作是一张表。

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

      Class文件没有任何分隔符,所以其数据项的顺序和数量甚至是数据存储的字节序(Big-Endian)这样的细节,都是被严格限定的,哪个字节代表什么含义,长度是多少,先后顺序如何,都不可以改变。

      6.3.1 魔数与Class文件的版本

        每个Class文件的头4个字节被称为魔数,它的唯一作用是确定整个文件是否为一个能被虚拟机接受的Class文件。

        紧接着魔数的4个字节存储的是Class文件的版本号。        

        下面时间操作以下,按照16进制的方式查一下如下代码生成的CLass文件:

    package jvm;
    
    public class TestClass {
        private int m;
    
        public TestClass() {
        }
    
        public int inc() {
            return this.m + 1;
        }
    }
    

      

     文件开头4个字节是十六进制表示是0xCAFEBABE, 代表版本号的第5和第6个字节为0x0000,主版本号是0x0034。

     6.3.2 常量池

      紧接着主次版本号之后的是常量池入口,其是Class文件里的资源仓库,它是Class文件结构中与其他项目关联最多的数据,通常也是占据class文件空间最大的数据项目之一,另外它也是Class文件中第一个出现的表类型数据项目。

      由于常量池中常量的数量不是固定的,所以在常量池入口需要放置一个u2类型的数据,代表常量池容量计数值,与java语言不同,这个容量计数是从1而非0开始的。常量池容量(0x00000008)为16进制数0x0016,即10进制的22,这代表常量池中有21项常量,索引值为1 ~ 21。

      常量池中主要存放两大类常量:字面量和符号引用,字面量比较接近Java语言层面的常量概念,如文本字符串、被声明为final的常量值等,而符号引用则属于编译原理方面的概念,主要包括下面几类常量:

    • 被模块导出或者开放的包(Package)
    • 类和接口的全限定名(Fully Qualified Name)
    • 字段的名称和描述符(Descriptor)
    • 方法的名称和描述符
    • 方法句柄和方法类型
    • 动态调用点和动态常量

      Java代码在进行Javac编译的时候,并不像C/C++那样有”连接“这一步骤,而是在虚拟机加载Class文件的时候进行动态连接,也就是说,Class文件不会保存各个方法、字段最终在内存中的布局信息,这些字段、方法的符号引用不经过虚拟机在运行期间转化的话,无法得到真正的内存入口地址,也就是无法直接被虚拟机使用的。当虚拟机做类加载时,将会从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址中

      常量池中每一项常量都是一个表,截至JDK 13, 常量表中分别有17种不同类型的常量。

      JDK的工具javap可以帮助分析class文件:

      

     
  • 相关阅读:
    sql 数据库 初级 个人学习总结(一)
    parentViewController
    关于iOS9之后的loadViewIfNeeded
    判断版本号
    MagicalRecord(简化CoreData操作)
    coreData
    PureLayout(轻量级自动布局)
    MJRefresh(上拉加载下拉刷新)
    MJExtension(JSON到数据模型的自动转换)
    BaceModel
  • 原文地址:https://www.cnblogs.com/liufei1983/p/13723711.html
Copyright © 2011-2022 走看看