zoukankan      html  css  js  c++  java
  • 深入理解JVM——读书笔记(虚拟机字节码执行引擎)

    类文件结构

    Class文件的结构

    • class文件是以一组8个字节为基础单位的二进制流,中间没有添加任何分隔符,从数据结构上看,class文件是一个类C++结构体的数据结构。

    • class文件只有两种数据类型:无符号整数,表。表可以嵌套表与无符号整数,无符号整数由u1,u2,u4,u8来表示。*_info表示表类数据类型。

    • 整个Class文件可以被视作下表,按照下表进行严格排列

    • magic

      • 魔数,用来标识文件类型。类似于扩展名。Class文件的魔数是0xCAFEBABE
    • minor_version

      • 次版本号,一般为0,预留给大版本之间的“功能预览”小版本
    • major_version

      • 主版本号,从45开始,从JDK1.1之后每个Java版本,主版本号+1。JDK1.1用的是45.0~45.3
      • Java虚拟机只能运行比它版本号低的Class文件,JDK8的主版本号是52
    • constant_pool

      • 容量(大小)为constant_pool_count-1

      • 常量池存放两大类常量:字面量、符号引用

        • 字面量:文本字符串、被声明为final的常量值等
        • 符号引用
          • 被模块导出或者开放的包(Package)
          • 类和接口的全限定名
          • 字段的名称和描述符(描述符是特定规则的字符串,用来描述数据类型、参数列表、返回值)
          • 方法的名称和描述符
          • 方法句柄和方法类型
          • 动态调用点和动态常量
        • 常量池中的每一个常量都是一个表,现在共有17种不同结构的常量类型。但是它们的起始字段都是u1的标志位,代表属于哪种常量(类似Class文件开头的魔数)

    • access_flags

      • 访问标志,识别类或者接口层次的信息

    • this_class、super_class、interfaces

      • 类索引、父索引、接口索引集合
    • fields

      • 类级别变量和实例级别变量(就是类属性和对象属性),但是不包括局部变量
      • image-20200812150635252
      • fields修饰了变量的类型、并发可见性、作用域、可否被序列化、字段名称等
    • methods

      • 与fields类似,这里只特别指出方法体里面的代码被存储在自带属性表的code属性中
    • attributes

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

      • Code属性

        • Java程序方法体里面的代码经过Javac编译器处理之后,最终变为字节码指令存储在Code属性
        • code_length的实际上限不超过2个字节长,所以太长的jsp可能会编译失败
        • Javac把对this关键字的访问转变成一个普通方法参数的访问
      • ConstantValue属性

        • 通知虚拟机自动为静态变量赋值

    字节码指令

    • 操作码长度为一个字节,所以最多不超过256条操作指令

    • 大部分的指令都包含数据类型,比如:iconst,lconst,fconst,但是由于操作指令容量有限,所以很显然

      一些指令对应了多种数据类型,比如byte,short就会被自动符号扩展为intboolean和char零位扩展为int

    • 具体的指令集在这里省略,具体可以参见《Java虚拟机规范》

    公有实现与私有化

    • 将输入的Java虚拟机代码在加载时或执行时翻译成另一种虚拟机的指令集
    • 将输入的Java虚拟机代码在加载时或执行时翻译成宿主机处理程序的本地指令集(即时编译器代码生成技术)

    虚拟机类加载机制

    • 类加载

      • 描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制
    • 类的生命周期

      • 加载

        • 通过全限定类名来获得二进制字节流
        • 将字节流转化为方法区的运行时数据结构
        • 在内存中生成一个代表这个类的Java.lang.Class对象,作为方法区这个类的各种数据的访问入口
      • 验证

        • 确保Class文件的字节流包含的信息符合《Java虚拟机规范》,进行字节码级别的校验
      • 准备

        • 为类中定义的变量(静态变量)分配内存并设置类变量初始值的阶段。
      • 解析

        • 将常量池内的符号引用替换成直接引用的过程(也就是把类外的引用,从符号级别变到地址级别)
          • 这一步可以理解成C++的“链接"
      • 初始化

        • 通过程序编码制定的主观计划去初始化类变量和其他资源
        • 类构造器<clinit>()
          • <clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作静态语句块(static{}块)中的语句合并产生的
          • Java虚拟机会保证在子类的<clinit>()方法执行前,父类的<clinit>()方法已经执行完毕。因此在Java虚拟机中第一个被执行的<clinit>()方法的类型肯定是java.lang.Object
          • 接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成<clinit>()方法;
          • 执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法,因为只有当父接口中定义的变量被使用时,父接口才会被初始化
    • 类加载器

      • 类加载器实现了类加载阶段中的“整个加载阶段“,自定义类加载器可以灵活处理了“如何得到二进制流”这一步

      • 通过继承抽象类ClassLoader并实现loadClass方法来自定义类加载器

        • 本质是解析某种数据源,得到byte[],通过内置函数defineClass(),完成从byte[]到class对象的转化。
      • 都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间

      • 双亲委派模型

        • 在Java虚拟机的角度来看,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现[插图],是虚拟机自身的一部分;另外一种就是其他所有的类加载器,这些类加载器都由Java语言实现,并且全都继承自抽象类java.lang.ClassLoader
      • 双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此

      • 上下文加载器实现”向下加载“

    虚拟机字节码执行引擎

    • 运行时栈帧结构
      • JVM以方法作为最基本的执行单元
        • 每一个方法从调用开始至执行结束的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程
        • 每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息
        • 一个栈帧需要分配多少内存,并不会受到程序运行期变量数据的影响,而仅仅取决于程序源码和具体的虚拟机实现的栈内存布局形式(编译期确定栈帧大小)
        • 执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作
      • 局部变量表
        • 一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量
      • 操作数栈
        • 当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作。
        • 简单来说,运算过程中产生的中间数据、结果都存放在操作数栈上
      • 动态连接
        • 每个栈帧都包含一个指向运行时常量池[插图]中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接
      • 方法返回地址
    • 方法调用
      • 方法调用并不等同于方法中的代码被执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还未涉及方法内部的具体运行过程
      • 分派
        • 静态分派(编译期决定)
          • 所有依赖静态类型来决定方法执行版本的分派动作,都称为静态分派。最典型应用表现就是方法重载
        • 动态分派(运行期决定)
          • 重写
    • 动态类型语言
      • 运行时确定类型,并且忽视了运行时确定的类型与变量静态类型不一致时的”冲突“
      • “变量无类型而变量值才有类型”
    • 基于栈的字节码解释执行引擎
  • 相关阅读:
    Spring中配置和读取多个Properties文件
    Eclipse 常用快捷键
    static 静态导包
    CDN 备胎技巧
    org.apache.commons.lang3.ArrayUtils 学习笔记
    深入浅出 妙用Javascript中apply、call、bind
    URL详解与URL编码
    Chrome DevTools good good study day day up
    Java反射机制
    第一篇:《UNIX 环境高级编程》编译环境的搭建
  • 原文地址:https://www.cnblogs.com/backkom-buaa/p/13501826.html
Copyright © 2011-2022 走看看