JVM——Java虚拟机体系结构、生命周期简述

时间:2023-01-23 08:57:19

1. JVM 体系结构

JVM——Java虚拟机体系结构、生命周期简述

如图所示,JVM包括类装载器子系统、运行时数据区、执行引擎。类装载器子系统根据给定的权限的名来装入类型(类或者接口)。执行引擎负责执行那些包含在被装载类的方法中的指令。运行时数据区包含方法区、堆、Java栈、PC寄存器、本地方法栈。


类装载器子系统:在JVM中负责查找并装载类型的那部分被称为类装载器子系统。JVM中有两种类装载器:启动类装载器和用户自定义类装载器。类装载器必须严格按照如下顺序进行工作:

1)装载:查找并装载类型的二进制数据。

2)连接:执行验证,准备,以及解析。

  • 验证——确保被导入类型的正确性。
  • 准备——为类变量分配内存,并将其初始化为默认值。
  • 解析——把类型中的符号转换为直接引用。

3)初始化:把类型变量初始化为正确的初始值


方法区:在Java虚拟机中,关于被装载类型的信息存储在一个逻辑上被称为方法区的内存中,类中的类(静态)变量同样存储在方法区中。所有线程共享方法区,因此,它们对方法区的数据访问必须被设计为线程安全的。方法区的大小不必是固定的,虚拟机可以根据应用的需要动态调整,同样方法区也不必是连续的,也可以被垃圾收集。

JVM会在方法区中存储以下类型信息:

  • 这个类型的全限定名;
  • 这个类型的直接超类的全限定名(除非这个类型是java.lang.Object,没有超类);
  • 这个类型是类类型还是接口类型;
  • 这个类型的访问修饰符(public 、abstract或者final的某个子集);
  • 任何直接超类的全限定名的有序列表。

除了上面列出的基本类型信息外,JVM还得为每个被装载的类型存储以下信息:

  • 该类的常量池(常量池就是该类型所用常量的一个有序集合,包括直接常量和对其他类型、字段、方法的符号引用);
  • 字段信息;
  • 除了常量以外的所有类(静态)变量;
  • 一个到Class类的引用。


堆:一个Java虚拟机实例中只存在一个堆空间,因此所有线程都将共享这个堆。又由于一个Java程序独占一个JVM实例,因而每个Java程序都有它自己的堆空间——它们不会彼此干扰。但是,同一个Java程序的多个线程共享同一个堆空间。


程序计数器:每个线程都有自己的PC(程序计数器)寄存器,它线程启动时创建,大小是一个字长。当执行本地方法时,PC寄存器的内容是下一条将被执行指令的地址;当执行本地方法时,PC寄存器中的值是“undefined”。


Java栈:每当启动一个新的线程时,JVM都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态,JVM只会直接对栈执行两种操作:以帧为单位的压栈或出栈。每当调用一个Java方法时,虚拟机都会在该线程的Java栈中压入一个新帧,使用这个帧来存储参数、局部变量、中间运算结果等数据。Java栈上的所有数据都是此线程所独有的。


本地方法栈:本地方法本质上是依赖于实现的,JVM实现的设计者们可以自己决定使用怎样的方式来让Java程序调用本地方法。

执行引擎:在Java虚拟机规范中,执行引擎的行为使用指令集来定义,具体内容及实现有待研究。


2. JVM生命周期

当启动一个Java程序时,一个虚拟机实例就诞生了,当程序关闭退出时,这个虚拟机实例随之消亡。JVM实例通过main()方法来运行一个Java程序。而这个main()方法必须是共有的(public)、静态的(static)、返回void,并且接收一个字符串数组为参数。Java程序初始类中的main()方法,将作为改程序初始线程的起点,任何其他线程都是由这个初试线程启动的。

JVM内部有两种线程:守护线程与非守护线程。守护线程通常是由虚拟机自己使用的,比如垃圾回收线程。当该程序所有的非守护线程都终止时,JVM实例将自动退出。