第一章

JVM 架构  

栈架构的优点:跨平台性、指令集小、指令多;执行性能比寄存器差。

JVM的生命周期

虚拟机的启动

Java虚拟机的启动是通过引导类加载器(bootstrap class loader)创建的一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现指定的。

虚拟机的执行

程序开始执行时它才运行,程序结束时它便停止。
执行一个所谓的JAVA程序的时候,真真正正的执行的是一个叫做JAVA虚拟机的进程。

虚拟机的退出

  • 程序正常执行结束
  • 程序在执行中遇到异常或错误而终止
  • 某线程调用Runtime类或System类的exit方法,或Runtime类的halt方法,并且Java安全管理器也允许这次exit或halt操作。

JVM的发展历程

HotSpot

  • 不管是现在仍在广泛使用的JDK6,还是使用比例较多的JDK8中,默认的虚拟机都是HotSpot。
  • Sun/Oracle JDK 和 OpenJDK 的默认虚拟机。
  • 从服务器、桌面到移动端、嵌入式都有应用。
  • HotSpot指的是它的热点代码探测技术。
  • 通过计数器找到最具编译价值代码,触发即时编译或栈上替换。
  • 通过编译器与解释器协同工作,在最优化的程序响应时间与最佳执行性能中取得平衡。

JRockit

  • 专注于服务端。
  • 内部不包含解析器。
  • JRockit JVM是世界上最快的JVM。

J9

  • 是IBM公司的。
  • 与HotSpot接近。

Azul VM

  • 与特定硬件平台绑定、软硬配合的专有虚拟机。
  • 高性能java虚拟机中的战斗机。

Dalvik VM

  • 只能称作虚拟机,而不能称作“java虚拟机”。
  • 不能直接执行 java 的 class 文件。
  • 基于寄存器架构,不是jvm的栈架构。
  • 执行的是编译后的dex文件。
  • Graal VM
  • 在HotSpot VM基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用。

第二章

内存结构概述


类加载器与类的加载过程

类加载器子系统的作用

  • 类加载器子系统负责从文件系统或网络中加载Class文件,class文件在文件开头有特定的文件标识。
  • ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。
  • 加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量。(这部分常量信息是Class文件中常量池部分的内存映射)
流程 : 加载 -> 连接 -> 初始化

类加载的过程


加载阶段:

  1. 通过一个类的全限定名获取定义此类的二进制字节流。
  2. 将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构。
  3. 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

链接阶段:

验证
  • 目的在于确保Class文件的字节流中包含嘻嘻符合当前虚拟机的要求,保证加载类的正确性不会危害虚拟机自身安全。
  • 主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
准备   
  • 这类变量分配内存并且设置该类变量的默认初始值,即零值。
  • 这里不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化。
  • 这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量会随着对象一起分配到java堆中。
解析
  • 将常量池内的符号引用转换为直接引用的过程
  • 事实上,解析操作往往还伴随着JVM在执行完初始化之后再执行。
  • 符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《java虚拟机规范》的Class文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
  • 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。

初始化:

初始化阶段就是执行类构造器方法<clinit>()的过程。
此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。
构造方法中指令按语句在原文件中出现的顺序执行。
<clinit>() 不同于类的构造器。 (关联:构造器是虚拟机视角下的<init>())
若该类具有父类,JVM会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕。
虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁。(即一个类在多线程情况下只加载一次)