欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

虚拟机的结构及功能

程序员文章站 2022-05-14 17:17:15
...

虚拟机的结构及功能

大家都知道一个java虚拟机运行就是一个进程,只支持java程序。
那么java代码的运行流程是什么呢?

一 、 java虚拟机编译java代码生成class文件,

二、 进入类装载器子系统中

1、每一个Java虚拟机都由一个类加载器子系统(class loader subsystem),负责加载程序中的类型(类和接口),并赋予唯一的名字。每一个Java虚拟机都有一个执行引擎(execution engine)负责执行被加载类中包含的指令。JVM的两种类装载器包括:启动类装载器和用户自定义类装载器,启动类装载器是JVM实现的一部分,用户自定义类装载器则是Java程序的一部分,必须是ClassLoader类的子类。

三、 进入到运行时数据区

运行时数据区包括:

1、方法区:

a、持久带

这个区域会存储包括类定义、结构、字段、方法(数据及代码)以及常量在内的类相关数据。它可以通过-XX:PermSize及-XX:MaxPermSize来进行调节。如果它的空间用完了,会导致java.lang.OutOfMemoryError: PermGenspace的异常。而JDK8开始,持久代已经被彻底删除了,取代它的是另一个内存区域也被称为元空间。

b、存放数据

方法区存储的是每个class的信息:

1.类加载器引用(classLoader)

2.运行时常量池
所有常量、字段引用、方法引用、属性

3.字段数据
每个方法的名字、类型(如类的全路径名、类型或接口) 、修饰符(如public、abstract、final)、属性

4.方法数据
每个方法的名字、返回类型、参数类型(按顺序)、修饰符、属性

5.方法代码
每个方法的字节码、操作数栈大小、局部变量大小、局部变量表、异常表和每个异常处理的开始位置、结 束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引

①方法区是线程安全的。由于所有的线程都共享方法区,所以,方法区里的数据访问必须被设计成线程安全的。例如,假如同时有两个线程都企图访问方法区中的同一个类,而这个类还没有被装入JVM,那么只允许一个线程去装载它,而其它线程必须等待。

②方法区的大小不必是固定的,JVM可根据应用需要动态调整。同时,方法区也不一定是连续的,方法区可以在一个堆(甚至是JVM自己的堆)中*分配。

③方法区也可被垃圾收集,当某个类不在被使用(不可触及)时,JVM将卸载这个类,进行垃圾收集
代码缓存 个缓存区域是用来存储编译后的代码。编译后的代码就是本地代码(硬件相关的),它是由JIT(Just In Time)编译器生成的,这个编译器是Oracle HotSpot JVM所特有的。

2:堆:

1.堆里面存放的内容主要还是new出来的对象和一些数组信息。

2.java的虚拟机不需要知道从堆内存里面存放多少空间大小的变量信息,也不需要知道每个对象的生命周期,所以一般程序运作的灵活性很高。

3.堆区里面存放了大量的对象很信息,所以也成为了gc重点回收的一个区域模块,所以当大量内存被占用的时候,gc的垃圾回收就会成为整个系统的性能瓶颈。于是随着jdk的不断更新,新的技术也对于jvm的内存分配这一块进行一定的优化改善,实现了off-heap。

3:java栈:

局部变量表
操作数栈
动态链接
返回地址

局部变量表:

这里面的作用主要是存储一系列的变量信息,而且这些变量都是以数字数组的形式来存储的,一般而言byte,short,char,类型的数据在存储的时候会变为int类型,boolean类型也是存储为数字类型,long,double则是转换为双字节大小的控件存储在栈里面。

操作数栈:

关于这个小编也是有点不太了解,看了几篇博客之后也只是知道这个东西是可以将指令在栈里面进行push和pop操作,也是一个数字数组类型。

动态链接:

动态链接的作用主要还是提供栈里面的对象在进行实例化的时候,能够查找到堆里面相应的类地址,并进行引用。这一整个过程,我们称之为动态链接。

返回地址:

某个子方法执行完毕之后,需要回到主方法的原有位置继续执行程序,方法出口主要就是记录该信息

4、本地方法栈:

本地方法栈的功能?
    为线程私有,功能和虚拟机栈非常类似。线程在调用本地方法时,来存储本地方法的局部变量表,本地方法的操作数栈等等信息。
什么是本地方法?为什么要使用本地方法?
    简单地讲,一个本地方法是这样一个方法:该方法的实现由非java语言实现,比如C语言实现。很多其它的编程语言都有这一机制,比如在C++中,你可以告知C++编译器去调用一个C语言编写的方法。

    我们知道java是高级编程语言,当对一些底层的如操作系统或某些硬件交换信息时,我们使用java来编程实现起来不容易,再者使用java来编程效率也很低下。这就不得不需要调用本地方法来解决这一问题。
本地方法是如何工作的?
    就是当一个线程调用一个本地方法时,本地方法又回调虚拟机中的另一个Java方法。这幅图展示了java虚拟机内部线程运行的全景图。一个线程可能在整个生命周期中都执行Java方法,操作他的Java栈;或者他可能毫无障碍地在Java栈和本地方法栈之间跳转。
    该线程首先调用了两个Java方法,而第二个Java方法又调用了一个本地方法,这样导致虚拟机使用了一个本地方法栈。图中的本地方法栈显示为 一个连续的内存空间。假设这是一个C语言栈,期间有两个C函数,他们都以包围在虚线中的灰色块表示。第一个C函数被第二个Java方法当做本地方法调用, 而这个C函数又调用了第二个C函数。之后第二个C函数被第二个Java方法当做本地方法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过 本地方法接口回调了一个Java方法(第三个Java方法)。最终这个Java方法又调用了一个Java方法。

5:程序技术寄存器:

记录程序运行到了哪一行。

四、执行引擎(执行引擎和本地方法区是同级的)

1、执行引擎
2、本地方法接口

五、执行到系统中

注:执行引擎和本地方法接口是同等级的。

虚拟机优化主要是针对方法区和堆得GC调优,基本上可以认为是清理垃圾。