JVM-JVM体系结构(组成/加载/运行时数据区/执行引擎)

版权声明:中华人民共和国持有版权 https://blog.csdn.net/Fly_Fly_Zhang/article/details/89683477
本片博客只对整体框架做以介绍详细的类加载过程,类的初始化方式,双亲委派模型等细节,请点击此篇博客

计算机基础与Class文件加载机制(类加载时机/双亲委派模型/类加载过程/JIT即时编译器)

什么是JVM:

JVM是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM虚拟机包括一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使java程序只需生成在JVM上运行的字节码,就可以在多种操作系统上不加修改的运行(一次编译,到处运行)。JVM在执行字节码时,实际上还是把字节码解释成具体平台上的机器码执行

java组成: java由四方面组成:java语言,java类文件格式,JVM和java应用程序接口(java API)

在这里插入图片描述
java平台由JVM和java应用程序接口搭建,java语言则是进入这个平台的通道,用java编写的程序可以运行在这个平台上,这个平台结构示例图如下:运行器环境代表java平台 ,开发人员编写java代码(.java文件),然后将之编译成(.class文件) ,在然后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者被JIT(即时编译器)有选择地(热点代码)转换成机器码执行
在这里插入图片描述
JVM在它的生存周期有一个明确地任务,那就是运行java程序,因此当java程序启动时,就长生JVM的一个实例;当程序运行结束,该实例随之消亡。从java平台结构中可以看出,JVM处于核心位置,是程序与底层操作系统和硬件无关的关键。它下面是移植接口(两部分组成:适配器和java操作系统,其中依赖平台的部分称为适配器),JVM通过移植接口在具体的平台和操作系统上实现;
JVM的上方是java基本类库和扩展类库以及他们的API,利用java API编写的应用程序(application)和小程序(java applet)可以在任何java平台上运行而无需考虑底层平台,就是因为JVM实现了程序与操作系统的分离,从而实现java的跨平台性。

jVM基本概念:

1,基本概念:

JVM是可运行java代码的虚拟计算机,包括一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆,一个存储方法域。JVM是运行在操作系统之上的,它和硬件没有交互。

运行过程:

java源文件,通过编译器,产生class文件;字节码通过JVM中的解释器,编译成特定机器上的机器码。
java源文件---->编译器---->字节码文件(class)
字节码文件---->JVM---->机器码
每一种平台解释器是不同的,但是实现的虚拟机是相同的,当一个程序从开始运行,这时候JVM就开始实例化了,多个程序启动就会存在多个虚拟机实例。程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数据是不能共享的

jdk1.7以前基本使用sun的HotSpot, 后来sun被oracle收购,jdk1.8采用HotSpot和BEA公司的JRockit两个JVM中精华形成jdk1.8的JVM

JRE/JDK/JVM是什么关系:

  • JRE(javaRuntimeEnvironment,java运行环境): 也就是java平台。所有的java程序都要在jre下运行。普通用户想要运行已开发好的java程序,安装jre即可。
  • JDK(java Development KIt): 是程序开发者用来编译,调试java程序的java开发工具包。 JDK工具也是java程序,也需要jre才能运行。为了保持JDK的独立性和完整性,在JDK的安装过程中,jre也是安装的一部分。专门有一个jre目录来存放jre文件。
  • JVM(javaVirtualMachine,java虚拟机) 是jre的一部分,他是一个虚构出来的计算机,是通过在实际的计算机上仿真各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器,堆栈,寄存器等,还具有相应的指令系统。java最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。

JVM原理:

JVM是Java的核心和基础,在java编译器和os平台之间的虚拟处理器。它是利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台,可以在上面执行java字节码程序。
在这里插入图片描述
java编译器只面向JVM,生成JVM能理解的代码或者字节码文件。 java源文件经编译成字节码文件,通过JVM将每一条指令翻译成不同的平台机器码,通过特点平台运行。

JVM生命周期:

JVM实例通过调用某个初始类的main方法来运行一个java程序,这个方法必须是public static void (String [ ] args)并且能接受一个字符串数组作为参数 ,任何拥有这样一个main方法的类都可以作为java程序起点。
java程序初始类的main方法将作为该程序初始线程的起点,任何其他线程都是由这个初始线程启动的。
在JVM中有两种线程:守护线程和非守护线程 。守护线程通常由虚拟机自己使用,如执行垃圾回收任务的线程。 但是java程序也可以把自己创建的任何线程标记为守护线程(Thread类的setDaemon(boolean on) 方法,true设置为守护线程) 。主线程非守护线程。
只要还有任何非守护线程在运行,那么这个java程序也在继续运行,当该程序所有的用户线程都终止时(守护线程随着用户线程消亡而消亡),虚拟机将自动退出。若安全管理器允许,程序本身也能够通过调用Runtime类或者System类的exit()来退出。

JVM体系结构:

在这里插入图片描述

  • 类加载器(ClassLoader):用来加载class文件;
  • 执行引擎:执行字节码,或者执行本地方法;
  • 运行时数据区(JMM:方法区,堆,虚拟机找,本地方法栈,程序计数器)
    当JVM运行一个程序时,他需要内存来存储许多东西,如:字节码,从已加载文件中得到的其它信息(class对象,元数据等),程序创建的对象,传递给方法的参数,返回值,局部变量表等等。JVM虚拟机把这些都组织到几个运行时数据区,方便管理。
    某些运行时数据区是由程序中所有线程共享的,还有一些则只能由一个线程私有。每个JVM实例都只有一个方法区和一个堆区,他们是由该虚拟机所有的线程共享的。当JVM加载一个class文件时,它会从这个class文件包含的二进制中解析类型信息。然后把这些信息放到方法区。当程序运行时,JVM会把该程序在运行时创建的对象都放到堆中。

在这里插入图片描述
每当一个新线程被创建时,他都将得到自己的PC寄存器(程序计数器)以及一个栈如果线程正在执行的是一个java方法(非本地方法),那么PC寄存器的值将总是指向下一条将被执行的指令 ,而它的java栈则总是存储该线程中java方法调用的状态**(局部变量,被调用时传进来的参数,返回值,以及运算的中间结果等)** 。而本地方法调用的状态,则是以某种依赖于具体实现的方法存储在本地方法栈中,也可能是在寄存器或者其它某些相关与特定实现相关的内存区中。

JVM如何工作:

在这里插入图片描述
如上图所示,JVM主要分为三个主要子系统:

  • 类装载系统(Class Loader Subsystem):
  • 运行时数据区(Runtime Data Area):
  • 执行引擎(Execution Exgine):
1,类装载系统:

java的动态类加载功能由类加载子系统实现,它可以装载,链接,还有当它在运行时(非编译)第一次引用类时,进行初始化类文件。

1-1:装载:

这个组建的功能就是加载类。总共有三种类加载器:

  • Bootstrap ClassLoader: $JAVA_HOME的jre/bin/rt.jar 中加载类。具有最高的优先级;
  • Extension ClassLoader: 负责加载$JAVA_HOME中jre/bin/ext/*.jar 的类。
  • Application ClassLoader: 复杂加载classpath路径下的类。
    上面的类加载器在加载类文件时,将遵循Delegation Hierarchy Algorithm算法
1-2 链接Linking
  • 验证Verify : 字节码验证器将检验生成的字节码是否正确,如果验证失败,我们将得到验证错误。
  • 准备Prepare: 对所有静态变量,内存将被分配并分配默认值;
  • 解析Resolve: 所有符号内存引用都替换为来自方法区的原始引用。
1-3初始化initialization:

这是类加载阶段的最后一个阶段,这时所有的静态变量都将赋予原始值(注意,不是默认值),静态代码块将被执行。

2, Runtime Data Area(运行时数据区JMM):

运行时数据区域分五部分:

2-1 方法区域(Method Area):

所有类级别数据都将存储在这里,包括静态变量。每个JVM实例只有一个共享的方法区域。

2-2 堆区域(Heap Area):

所有对象及其对应的实例变量和数组都将存储在这里。每个JVM实例只有一个堆区域。由于方法和堆区是线程共享内存,所以堆区存储的数据并不是线程安全的。

2-3栈区域(Stack Area):

对于每个线程,将创建一个单独运行时栈。对于每个方法调用,将在堆栈中创建一个条目,称为堆栈帧(Stack Frame)。所有本地变量都将在栈内存中创建。栈区域是线程安全的,因为它不是共享资源。

  • 栈框架分为三个子实体:
  • 局部变量数据(Local Variable Array): 与方法相关的局部变量表以及相应的值存储在这里。
  • 操作数栈(Operand stack): 如果需要执行任何中间操作,操作数堆栈作为运行时工作区来执行操作。
  • 框架数据(Frame data): 与方法对应的所有符号都存储在这里。异常处理中,catch块信息将在框架数据中维护
2-4,程序计数器(PC Registers):

每一个线程都有单独的PC寄存器,在执行指令后,保存当前执行指令的地址,PC寄存器将随下一条指令进行更新,也就是说PC寄存器总是指向当前正在运行的指令地址。

2-5,本地方法栈(Native Method stacks):

本地方法栈保存本地方法信息。为每一个线程将单独创建一个本地方法栈。

3,执行引擎:

分配给运行时数据区的字节码将由执行引擎读取字节码并逐个执行。

  1. 解释器(Interpreter): 解释器解释字节码的速度很快,但执行缓慢。缺点是: 当一个方法被多次调用时,每次都需要新的解释。
  2. 即时编译器(JIT Compiler): JIT编译器消除了解释器的缺点。执行引擎使用解释器来帮助转换字节码,但是发现热点代码(重复执行代码) ,它将使用JIT,将热点代码编译成本机机器码 。机器码直接用于重复的方法调用,从而提高系统性能。
  3. Intermediate Code generator: 生成中间代码;
  4. Code Optimizer: 负责优化上面生成的中间代码。
  5. Target Code Generator: 负责机器代码或本机代码。
  6. Profiler: 一种特殊组件,负责查找hotSpots(热点) Code ,即是否被多次调用。
  7. 垃圾回收: 收集和删除未引用的对象。可以通过System.gc() 来主动触发垃圾收集(但不会立即执行,等待安全点),JVM的垃圾回收 收集已创建的对象。
  • Java Native Interface(JNI): JNI将与本地方法库交互并为执行引擎提供所需的本地库(本地方法栈 需要与之作用)
  • Native Method Libraries: 它是执行引擎所需的本地库的集合。

猜你喜欢

转载自blog.csdn.net/Fly_Fly_Zhang/article/details/89683477