diff --git a/src/interview/x-interview.md b/src/interview/x-interview.md index 99b8fee..9f9f180 100644 --- a/src/interview/x-interview.md +++ b/src/interview/x-interview.md @@ -4453,3 +4453,83 @@ scatter-gather: on 2. 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。 3. 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载; 4. 若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。 + +### 5.2 内存结构 + +#### 说说JVM内存整体的结构?线程私有还是共享的? + +JVM 整体架构,中间部分就是 Java 虚拟机定义的各种运行时数据区域。 + +![jvm-memory-structure](https://b2files.173114.xyz/blogimg/2025/03/349ba45d008c3979602df9157b9597d7.jpg) + +Java 虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机启动而创建,随着虚拟机退出而销毁。另外一些则是与线程一一对应的,这些与线程一一对应的数据区域会随着线程开始和结束而创建和销毁。 + +- **线程私有**:程序计数器、虚拟机栈、本地方法区 +- **线程共享**:堆、方法区, 堆外内存(Java7的永久代或JDK8的元空间、代码缓存) + +#### 什么是程序计数器(线程私有)? + +PC 寄存器用来存储指向下一条指令的地址,即将要执行的指令代码。由执行引擎读取下一条指令。 + +- **PC寄存器为什么会被设定为线程私有的?** + +多线程在一个特定的时间段内只会执行其中某一个线程方法,CPU会不停的做任务切换,这样必然会导致经常中断或恢复。为了能够准确的记录各个线程正在执行的当前字节码指令地址,所以为每个线程都分配了一个PC寄存器,每个线程都独立计算,不会互相影响。 + +#### 什么是虚拟机栈(线程私有)? + +主管 Java 程序的运行,它保存方法的局部变量、部分结果,并参与方法的调用和返回。每个线程在创建的时候都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次 Java 方法调用,是线程私有的,生命周期和线程一致。 + +- **特点?** + +1. 栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器 +2. JVM 直接对虚拟机栈的操作只有两个:每个方法执行,伴随着**入栈**(进栈/压栈),方法执行结束**出栈** +3. 栈不存在垃圾回收问题 +4. 可以通过参数`-Xss`来设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度 + +- **该区域有哪些异常**? + +1. 如果采用固定大小的 Java 虚拟机栈,那每个线程的 Java 虚拟机栈容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过 Java 虚拟机栈允许的最大容量,Java 虚拟机将会抛出一个 **StackOverflowError** 异常 +2. 如果 Java 虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那 Java 虚拟机将会抛出一个**OutOfMemoryError**异常 + +- **栈帧的内部结构?** + +1. 局部变量表(Local Variables) +2. 操作数栈(Operand Stack)(或称为表达式栈) +3. 动态链接(Dynamic Linking):指向运行时常量池的方法引用 +4. 方法返回地址(Return Address):方法正常退出或异常退出的地址 +5. 一些附加信息 + +![jvm-stack](https://b2files.173114.xyz/blogimg/2025/03/d56db4f1e02265ca20870fa5ca5dbc11.jpg) + +#### Java虚拟机栈如何进行方法计算的? + +以如下代码为例: + +```java +private static int add(int a, int b) { + int c = 0; + c = a + b; + return c; +} +``` + +可以通过jsclass 等工具查看bytecode + +![img](https://b2files.173114.xyz/blogimg/2025/03/f3b9c1353fdd2e9f9e7723f1ff6f8f25.png) + +压栈的步骤如下: + +```java +0: iconst_0 // 0压栈 +1: istore_2 // 弹出int,存放于局部变量2 +2: iload_0 // 把局部变量0压栈 +3: iload_1 // 局部变量1压栈 +4: iadd //弹出2个变量,求和,结果压栈 +5: istore_2 //弹出结果,放于局部变量2 +6: iload_2 //局部变量2压栈 +7: ireturn //返回 +``` + +如果计算100+98的值,那么操作数栈的变化如下图 + +![img](https://b2files.173114.xyz/blogimg/2025/03/a968e2c20822af3419cceea3fe681b9e.png)