编程语言
首页 > 编程语言> > JVM基本概念,java虚拟机的原理

JVM基本概念,java虚拟机的原理

作者:互联网

JVM中发出的指令是经过操作系统,传递到硬件中。比如执行文件读写。

二、JVM的整体结构:

JVM的基本机构包括 1)类加载器 2)内存区(运行时数据区) 3)执行引擎 4)本地库接口

1)类加载器

ClassLoader 负责class文件的加载,class文件的开头有特定的标识。classloader只是负责class文件的加载,至于是否可以运行,要通过执行引擎去决定。

1、启动类加载器(Bootstrap) C++

2、扩展类加载器(Extension) Java /ext

3、应用程序类加载器(AppClassloader) 加载当前应用的classpath下的所有类。

用户自定义类加载器: (双亲委派模式)

java.lang.ClassLoader 的子类,用户可以自定义类的加载方式。

2)执行引擎 (Execution Engine)

执行引擎,负责解释命令,提交操作系统执行。

3)JVM内存区(运行时数据区):

1、方法区(Method Area)

方法区是被所有的线程共享。方法区存储了每个类的信息(类的名称,方法信息,字段信息)、静态变量,常量池,以及编译后的代码。

Hotspot虚拟机中,方法区是存在永久代中的。

方法区中存放的是类的字节码内存块。

类信息

修饰符(public final)

是类还是接口(class , interface)

类的全限定名(package.A.class)

类的直接父类的全限定名称(package.Parent.class)

类的直接父接口的全限定名数组(java/io/Serializable)

字段信息

修饰符:(private, public)

字段类型:(java.lang.String)

字段名称:(name)

类似:private String name;

方法信息

修饰符 (private, public static final)

返回值 (java/lang/String.class)

方法名称 (getMethod)

参数用的局部变量的大小以及操作栈的大小

异常表 (throws Exception)

方法体 (方法内容)的字节码

常量池

直接常量(各种基本数据类型的常量池) public final int age=12; public final String name=‘’;

方法名、方法描述符、类名、字段名,字段描述符的符号引用

静态变量

静态字段:public static String name=’’

一个到类加载器(classloader)的引用

存储了加载了自己的class(A.class)的ClassLoader的引用。ClassLoader的实例,存放在jvm的堆中。

虚拟机在加载class的时候,会在方法区字节码中存在一个指向加载自己的classloader的引用。

一个class对象的引用

ClassLoader加载这个类的时候,从磁盘上将a.class加载到jvm的方法区,方法区中的字节内存块,会在new 的时候使用。然后在内存 堆中生成一个a的字节码对象。在方法区的字节码中存在一个指向堆的class对象的引用。

一个指向被当前类的字节码实例的对象(堆空间)的引用。

方法表

这个类的所有的实例可能被调用的所有实例方法的直接引用。

2、Java栈区(VM stack)

(1)栈内存是线程独享的。

(2)是在创建线程的时候创建。他的生命周期,随线程的生命周期。线程结束,栈的内存也就释放了。生命周期跟线程一致。

(3)8种基本类型的变量,对象的引用变量,实例方法

(4)栈存储本地变量(输入参数,输出参数,方法内的局部变量) 栈操作(记录出栈,入栈的操作)

在启动一个线程的时候,jvm虚拟机就会为每一个线程分配一个栈空间,栈空间是有一个一个的栈帧组成的 (栈帧是栈空间的最小单元,一个栈帧代表一个执行的方法)。

当前线程当前执行的某个方法叫做当前方法,当前

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

方法使用的栈帧叫做当前栈帧,当前方法使用的类叫做当前类,当前类的常量池叫做当前常量池。线程在执行一个方法时会监控当前类以及当前类的常量池。

当线程调用一个java方法的时候,jvm就会在当前线程的栈中压入一个栈帧。执行这个方法时,用这个栈帧存储参数,局部变量,中间运算结果等。

java中的方法以两种方式结束,一种正常返回return,一种通过抛出异常。两种方式返回时,jvm就会将当前栈帧弹出释放掉,上一个栈帧就是当前栈帧(栈帧中会记录上一个栈帧的地址以及下一个栈帧的地址)。

栈空间是线程独有的,其他线程是不能访问的。

JVM栈空间包含三部分(局部变量表,操作数栈,帧数据区)

局部变量表:

包括参数和局部变量以及中间变量。

局部变量是一个以字长为单位的数组,数组长度从0开始。类型 short, byte, char在存储之前要转换成int值。而double, long类型的值占两个字长,一个字长占4个字节。

调用一个方法时,从他的类型信息中得到此方法(栈帧)局部变量区和操作数栈的大小,并根据此分配内存。

如上图,将参数以及局部变量压入局部变量表(数组)中,如果在执行过程中产生中间变量,同样也压入局部变量数组中。

特别注意:如果是非静态方法,局部变量数组中的0位是当前对象的引用

操作数栈:

操作数栈也被分配位一个以字长为单位的数组。和局部变量数组不同的是不是通过索引来进行访问的,而是通过入栈和出栈进行访问。

如下举例:

(1)先把 a=100压入栈。

(2)再把 b=98压入栈。

(3)执行a+b,将a,b两个数值出栈,并把执行结果198压入栈。

(4)把中间变量执行结果存入局部变量数组中。

动态链接:

栈帧 内部包含一个 指向运行时常量池的引用,用来支持当前方法的执行。

class文件中的常量存在大量的符号引用(标识常量),这些引用在类加载阶段,或者在第一次使用的时候转换成直接引用(指向数据所存的堆中的地址的指针),这种转换称为静态链接。另外一部分在运行期间才转换成直接引用,这一部分叫做动态链接。

返回地址区(也称作帧数据区?????):

主要为了存放方法方法返回的信息。

1、执行引擎在执行过程中,遇到方法返回的字节码指令,这时候就有可能将返回值返回给上层的调用者(调用栈帧),具体是否有返回以及返回的类型,根据方法返回的字节码来决定。这种退出为正常退出。

2、执行引擎执行的过程中,遇到异常,并在在方法处无法处理(在异常处理匹配表中找不到匹配的异常处理器),这种情况是不会有返回值给上层,这种情况为异常退出。

3、无论是正常退出,还是异常退出,都需要回到调用栈帧继续执行,方法返回时需要在栈帧中保存一些信息(包含正常返回信息,异常返回信息)帮助上层帧恢复执行状态。

4、如果方法有返回值时,更新调用栈帧的操作数栈以及局部变量的表,进行压栈和出栈的操作。并更新pc寄存器的地址指向,执行方法调用的下一条指令。

调用栈以及被调用栈的引用

3、堆区(Heap)

堆在逻辑上分为:新生代,老年代,永久代(方法区,元空间)

1、JVM分为堆内存和非堆内存,堆内存分为老年代和年轻代,堆内存分为新生代(YoungGen)和老年代(OldGen)。非堆只有一个永久代(Permanent Generation)。

2、年轻代又分为新生区(Eden伊甸园区)和幸存区(Survivor)。幸存区又是由FromSpace和ToSpace组成。 Eden区占主要容量,Survivor占少量。默认比例是8:1:1

3、堆内存的用途:存放对象,垃圾收集器就是收集的这些对象,然后根据GC算法进行回收。

4、永久代,非堆内存:也称为方法区,存储类的元信息(类定义,属性,常量池,方法定义等)。

堆内存描述:

在JDK1.8中废除了永久代的概念,而使用元空间替代(MetaSpace),元空间和永久代类似,都是方法区的存储空间。他们最大的区别是元空间不在JVM中,而是使用的本地内存。

元空间有两个参数:

标签:java,虚拟机,局部变量,class,线程,JVM,方法,栈帧,加载
来源: https://blog.csdn.net/m0_64867003/article/details/121727870