Java虚拟机(一)体系结构和执行原理

时间:2022-12-23 16:21:19

前言
什么是Java虚拟机?

虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的Java虚拟机有自己完善的硬体架构,如处理器堆栈寄存器等,还具有相应的指令系统JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行


1.Java虚拟机概要
Java虚拟机是整个java语言编译代码的运行平台,可以把Java虚拟机看做一个抽象的计算机,它有各种指令集和各种运行时数据区域官方(Oracle)定义的java技术体系主要包括以下几个部分:java程序设计语言Java API类库第三方Java类库Class文件格式java虚拟机(不同平台的)可以把java程序设计语言java虚拟机和java API类库这三部分统称为JDK(Java Development Kit),JDK是java程序开发的最小环境另外,java API中的java SEAPI子集和Java虚拟机这俩部分统称为JRE(Java Rutiem),JRE是java运行的标准环境

1.1.Java程序设计语言:Java语言是一种目前正在全世界得到迅速传播与广泛应用的面向对象的计算机程序设计语言

1.2.JavaAPI类库:Java 的类库是 Java 语言提供的已经实现的标准类的集合,是 Java 编程的 API(Application Program Interface),它可以帮助开发者方便快捷地开发 Java 程序程序员在开发程序的时候,直接调用这些现成的类就可以了这些类根据实现的功能不同,可以划分为不同的集合,每个集合组成一个包,称为类库Java 类库中大部分都是由Sun 公司提供的,这些类库称为基础类库

1.3.第三方Java类库:Java是世界上最强大的编程语言之一,很多开发人员和大型企业都偏爱Java,并且在各种应用场景中使用它在使用过程中优秀的开发者根据不同的需求开发并封装了自己需要的库

1.4.Class文件格式:对于理解JVM和深入理解Java语言,学习并了解class文件的格式都是必须要掌握的功课原因很简单, JVM不会理解我们写的Java源文件,我们必须把Java源文件编译成class文件,才能被JVM识别,对于JVM而言, class文件相当于一个接口,理解了这个接口, 能帮助我们更好的理解JVM的行为;另一方面, class文件以另一种方式重新描述了我们在源文件中要表达的意思,理解class文件如何重新描述我们编写的源文件,对于深入理解Java语言和语法都是很有帮助的另外, 不管是什么语言,只要能编译成class文件,都能被JVM识别并执行,所以class文件不仅是跨平台的基础,也是JVM跨语言的基础,理解了class文件格式,对于我们学习基于JVM的其他语言会有很大帮助在整个Java体系结构中,Class文件处于中间的位置,对于理解整个体系有着承上启下的作用:

Java虚拟机(一)体系结构和执行原理
1.5.java虚拟机(不同平台的):从1996年Sun公司发布的JDK1.0中包含的Sun Classic VM到今天,出现和消亡了很多种虚拟机,Java虚拟机主流的是Oracle的HotSpot虚拟机。此外还有J9 VM 、Zing VM等其他厂商的虚拟机。

2.数据类型

Java虚拟机语言类型和Java语言类型类似,分俩类:基本类型和引用类型,Java虚拟机希望编译器在编译期间尽肯能的完成类型检查,使得虚拟机在运行期间无需进行类型检查操作。

3.运行时数据区域(Java虚拟机内存)

Java虚拟机在执行Java程序的过程中会把它管理的内存划分为不同的数据区域,根据《java虚拟机规范(Java SE7版)》的规定,这些数据区域分别为:程序计数器、Java虚拟机栈、本地方法栈、Java堆和方法区。

3.1.程序计数器(Program Counter register):也叫计算机寄存器,是一块较小的内存空间。在虚拟机概念模型中,字节码解释器工作时就是通过改变程序计数器来选取下一条需要执行的字节码指令,Java虚拟机的多线程是通过轮流切换并分配处理器执行时间的方式来实现的,在一个确定的时刻只有一个处理器执行一条线程中的指令,为了在线程切换后能恢复到正确的执行位置,每个线程都会有一个独立的程序计数器,因此,程序计数器是线程私有的。如果线程执行的方法不是Native方法,则程序计数器保存正在执行的字节码指令地址,如果是Native方法则程序计数器的值则为空(Undefined)。程序计数器是Java虚拟机规范中唯一没有规定任何OutOfMemoryError情况的数据区域。

目的:为了保证程序能够连续地执行下去,处理器必须具有某些手段来确定下一条指令的地址,而程序计数器正是起到这种作用。


3.2.Java虚拟机栈(JavaVirtual Machine Stacks):每一条Java虚拟机线程都有一个线程私有的Java虚拟机栈。它的生命周期与线程相同,与线程是同时创建的。Java虚拟机栈存储线程中Java方法调用的状态,包括局部变量、参数、返回值以及运算的中间结果等。一个Java虚拟机栈包含了多个栈帧,一个栈帧用来存储局部变量表、操作数栈、动态链接、方法出口等信息。当线程调用一个Java方法时,虚拟机压入一个新的栈帧到该线程的Java栈中,当该方法执行完成,这个栈帧就从Java栈中弹出。我们平常所说的栈内存(Stack)指的就是Java虚拟机栈。

Java虚拟机规范中定义了两种异常情况:

1.如果线程请求分配的栈容量超过Java虚拟机所允许的的最大容量,Java虚拟机会抛出*Error

2.如果Java虚拟机栈可以动态扩展(大部分Java虚拟机都可以动态扩展),但是扩展时无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的Java虚拟机栈,则会抛出OutOfMemoryError异常。


3.3.本地方法栈:Java虚拟机实现可能要用到C Stacks来支持Native语言,这个C Stacks就是本地方法栈(Native Method Stack)。它与Java虚拟机栈类似,只不过本地方法栈是用来支持Native方法服务。如果Java虚拟机不支持Native方法,并且也不依赖于C Stacks,可以无需支持本地方法栈。在Java虚拟机规范中对本地方法栈的语言和数据结构等没有强制规定,因此具体的Java虚拟机可以*实现它,比如HotSpot VM将本地方法栈和Java虚拟机栈合二为一。与Java虚拟机栈类似,本地方法栈也会抛出 *ErrorOutOfMemoryError异常。

3.4.Java堆:是被所有线程共享的运行时内存区域。Java堆用来存放对象实例,几乎所有的对象实例都在这里分配内存。Java堆存储的对象被垃圾收集器管理,这些受管理的对象无需也无法显示的销毁。从内存回收的角度,Java堆可以粗略的分为新生代和老年代。从内存分配的角度Java堆中可能划分出多个线程私有的分配缓冲区。不管如何划分,Java堆存储的内容是不变的,进行划分是为了能更快的回收或者分配内存。Java堆的容量可以时固定的,也可以动态的扩展。Java堆的所使用的内存在物理上不需要连续,逻辑上连续即可。

Java虚拟机规范中定义了一种异常情况:如果在堆中没有足够的内存来完成实例分配,并且堆也无法进行扩展时,则会抛出OutOfMemoryError异常。

3.5.方法区(MEthod Area):是被所有线程共享的运行时内存区域。用来存储已经被java虚拟机加载的类的结构信息。

包括:运行时常量池、字段和方法信息、静态变量等数据。方法区是Java堆的逻辑组成部分,它一样在物理上不需要连续,并且可以选择在方法区中不实现垃圾收集。方法区并不等同于永久代,只是因为HotSpot VM使用永久代来实现方法区,对于其他的Java虚拟机,比如J9JRockit,并不存在永久代概念。

Java虚拟机规范中定义了一种异常情况:如果方法区的内存空间不满足内存分配需求时,Java虚拟机会抛出OutOfMemoryError异常。

运行时常量池

  运行时常量池(Runtime Constant Pool)是方法区的一部分。在2.1 Class文件格式这一小节中我们得知,Class文件不仅包含了类的版本、接口、字段和方法等信息,还包含了常量池,它用来存放编译时期生成的字面量和符号引用,这些内容会在类加载后存放在方法区的运行时常量池中。运行时常量池可以理解为是类或接口的常量池的运行时表现形式。

Java虚拟机规范中定义了一种异常情况:


当创建类或接口时,如果构造运行时常量池所需的内存超过了方法区所能提供的最大值,Java虚拟机会抛出OutOfMemoryError异常。


参考资料:

《深入理解Java虚拟机》

Java虚拟机规范(JavaSE7)

深入理解Java虚拟机JVM高级特性与最佳实践