JVM 面试题笔记

JVM 全局构架图如下,包含 JVM 的全部内容 1. JVM 的内存区域是如何划分的? JVM 内存当中,运行时数据的区域包含堆、方法区(元空间)、虚拟机栈、本地方法栈、程序计数器。另外,其他的内存区域属于本地内存,本地内存就包括直接内存,直接内存是非运行时数据区的一部分。 其中,堆和方法区(元空间) 是线程共享的,虚拟机栈、本地方法栈、程序计数器都是线程私有的。 【注意】 JVM 规范对于运行时数据区域的规定是很宽松的。就拿堆来说,堆可以是连续空间,也可以是不连续空间。堆的大小可以固定,也可以再运行时按照需求进行扩展。虚拟机实现者可以使用任何垃圾回收算法管理堆,甚至完全不进行垃圾回收也是可以的。 下面逐一介绍不同部分的功能和作用 首先是线程私有的部分,程序计数器、虚拟机栈、本地方法栈。 【程序计数器】 定义:程序计数器就是记录当前所执行的字节码的行号。字节码解释器通过改变程序计数器的值,来选取下一条需要执行的字节码指令。它的生命周期随着线程的创建而创建,随着线程的结束而死亡。 作用:分支、循环、跳转、异常处理、线程恢复这些功能都需要依赖程序计数器完成。比如线程切换上下文中,从 A线程 先切换到 B线程,然后从 B线程 恢复到 A线程 的过程当中,为了 A线程 能够恢复到正确的执行位置,就需要读取 A线程 程序计数器的值,来确认切换线程前执行的位置在哪里。 为什么是线程私有:因为线程切换的过程当中,每个线程都需要一个程序计数器来记录自己的程序执行位置。 【注意】 程序计数器是唯一一个不会出现 OOM 内存不足的内存区域,因为他就只是存储一个值。 【虚拟机栈】 定义:虚拟机栈按照先进后出的方案存储的是非本地方法的调用对应的栈帧。每一次方法调用的时候会被压入栈中,方法结束的时候会被弹出栈中。栈帧中包含局部变量表、操作次数栈(存储操作数和临时计算结果)、动态链接、方法的返回地址。虚拟机的生命周期随着线程的创建而创建,随着线程的结束而死亡。 作用:存储非本地方法的栈帧,支持方法的调用和返回。当栈深度超过虚拟机允许的最大深度,会抛出 StackOverflowError 栈溢出异常。如果虚拟机栈无法动态扩展或申请到足够的内存,会抛出 OutOfMemoryError 内存不足异常。 栈帧中包含局部变量表、操作次数栈(存储操作数和临时变量)、动态链接、方法的返回地址。其中,局部变量表存放的是所有的局部变量,或者其出对应的地址(数组)。动态链接就是当前类常量池的引用。 局部变量表:存放方法参数传入的形参值,方法内的局部变量、方法的 this 引用。内部结构主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针) 操作数栈:主要作为方法调用的中转站使用,用于存放方法执行过程中产生的中间计算结果。另外,计算过程中产生的临时变量也会放在操作数栈中。 动态链接:主要作用是实现在当前方法中调用其他方法。 Class 文件的常量池里面保存有大量的符号引用,比如方法的符号引用。 当一个方法需要调用其他方法,需要将常量池中只想方法的符号引用转换为在内存地址当中的直接引用。动态链接的作用就是把常量池的符号引用转换为内存当中的直接引用。 【本地方法栈】 定义:本地方法栈和虚拟栈的结构是一样的。不同的是,本地方法栈是 JVM 调用 Native 方法的时候才会用到的,虚拟机栈是执行 Java 自身的方法 (也就是字节码) 会用到的。 作用:存储本地方法 (Native) 栈帧,支持方法的调用和返回。 【注意】 在 HotSpot 虚拟机当中,将本地方法栈和虚拟机栈合二为一了。 ...

March 22, 2025 · 4 min · SwimmingLiu