JVM-基于Hotspot

发布于:2025-04-17 ⋅ 阅读:(22) ⋅ 点赞:(0)

前言

Java虚拟机(Java Virtual Machine简称JVM)是运行所有Java程序的抽象计算机,是Java语言的运行环境,其主要任务为将字节码装载到内部,解释/编译为对应平台上的机器指令执行。

Java虚拟机规范定义了一个抽象的——而非实际的——机器或处理器。这个规范描述了一个指令集,一组寄存器,一个堆栈,一个“垃圾堆”,和一个方法区。一旦一个Java虚拟机在给定的平台上运行,任何Java程序(编译之后的程序,称作字节码)都能在这个平台上运行。Java虚拟机(JVM)可以以一次一条指令的方式来解释字节码(把它映射到实际的处理器指令),或者字节码也可以由实际处理器中称作just-in-time的编译器进行进一步的编译。

JVM的内存结构



我们可以把运行时数据区分为线程私有共享数据区两大类

  • 线程私有的数据区包含 程序计数器、虚拟机栈、本地方法栈,即为本地区(native area)

  • 线程共享的数据区包含Java堆、方法区,在方法区内有一个常量池。

本地区(native area)

程序计数器(PC Register)

记录正在执行的虚拟机字节码的地址。和计算机组成原理中提到的程序计数器PC概念类似,是线程私有的,用来记录当前执行的字节码位置。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址;或者,如果是在执行本地方法,则是未指定值(undefined)。

程序计数器是唯一一个不会发生OOM的区域。

虚拟机栈(JVM Stack)

也就是我们常常所说的栈。

方法执行的内存区,每个方法执行时会在虚拟机栈中创建栈帧,用于存储局部变量表(局部变量表需要的内存在编译期间就确定了所以在方法运行期间不会改变大小),操作数栈,动态链接,方法出口等信息。每一个方法从调用开始至执行完成的过程,就对应着栈帧在虚拟机栈中从入栈到出栈的过程。

这个区域有两种异常情况:

  • StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度

  • OutOfMemoryError:虚拟机栈扩展到无法申请足够的内存时

本地方法栈(Native Method Stack)

本地方法栈则为虚拟机使用到的Native方法提供内存空间。

栈帧

每一个栈帧都包括了局部变量表,操作数栈,动态连接,方法返回地址和一些额外的附加信息。

在编译代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到了方法表的Code属性中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体虚拟机的实现。

一个线程中的方法调用链可能会很长,很多方法都同时处理执行状态。对于执行引擎来讲,活动线程中,只有虚拟机栈顶的栈帧才是有效的,称为当前栈帧(Current Stack Frame),这个栈帧所关联的方法称为当前方法(Current Method)。执行引用所运行的所有字节码指令都只针对当前栈帧进行操作。栈帧的概念结构如下图所示:

方法区(method area)

方法区属于是 JVM 运行时数据区域的一块逻辑区域,是各个线程共享的内存区域。

当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

可以这样理解,方法区存的是类的模版。比如说方法,其实就是行为,一个类的行为都是一致的,所以存在方法区;而变量,就是数据,每一个对象的数据都是不一样的,所以存在堆里。

符号引用和直接引用
  • 符号引用:字符串,能根据这个字符串定位到指定的数据,比如java/lang/StringBuilder,包含三种:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。

  • 直接引用:内存地址

虚拟机栈的动态链接就是将符号引用(这些符号引用的集合就是常量池)转换为直接引用(符号引用对应的具体信息,这些具体信息的集合就是运行时常量池,存在方法区中)的过程。

常量池

常量池表(Constant Pool Table)

我们写的每一个Java类被编译后,就会形成一份class文件(每个class文件都有一个class常量池)。 class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。

  • 字面量,即通过字面我们就能知道其值的含义。包括:1.文本字符串 2.八种基本类型的值 3.被声明为final的常量等;

  • 符号引用包括:1.类和方法的全限定名 2.字段的名称和描述符 3.方法的名称和描述符。

常量池表会在类加载后存放到方法区的运行时常量池中。

运行时常量池(Runtime Constant Pool)

jvm在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将 class常量池 中的内容存放到 运行时常量池 中,由此可知,运行时常量池 也是每个类都有一个。

在上面我也说了,class常量池 中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。而经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询 字符串常量池 ,以保证 运行时常量池所 引用的字符串与 字符串常量池 中所引用的是一致的。

字符串常量池(String Pool)

字符串常量池存的是 引用值,而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。

是在类加载完成,经过验证,准备阶段之后 在 堆 中生成字符串对象实例,然后 将该字符串对象实例的 引用值 存到 String Pool 中


网站公告

今日签到

点亮在社区的每一天
去签到