JVM的内存结构
1. 程序计数器(PC Register )寄存器
1.1 全称:Program Counter Register
1.2 作用
首先,java源代码 被 编译成 二进制的 字节码 (jvm指令) jvm跨平台就是这一套指令,linux 下,windows下指令都是一致的
指令 经过 解释器 把每一条指令 解释成 机器码,然后交给CPU执行,CPU只识别机器码
程序计数器作用就是: 在指令的执行过程中,记住下一条指令的执行地址
例如下面这一段字节码
假设代码现在执行到 地址为 7 的 这一行,指令 通过解释器 被解释成机器码 到 被CPU 执行的过程中,程序计数器 会将下一条指令地址 8 进行记录
当指令地址为 7 的指令执行完后,解释器会从 程序计数器获取下一条指令的执行地址,解释成机器码去给CPU执行,同时,在这个过程中,程序计数器 会将下一条指令地址 11 进行记录,重复这个过程,直到程序结束
程序计数器在物理上是通过寄存器实现的,寄存器是CPU读取速度最快的
1.3 特点
- 线程私有的,每一个线程都有自己的程序计数器
解释说明:
java支持多线程,多个线程运行时, CPU 中的调度器组件会对****每一个线程进行时间片的分配
时间片内代码没有执行完,就会将线程1的状态切换为暂存,
随后切换到线程2 ,去执行线程2的代码,同理,线程2的时间片使用完,就会回到线程1 执行未完成的代码
- 不会存在内存溢出的区域(堆,栈、方法区都会出现内存溢出)
2.虚拟机栈
2.1 栈的数据结构
栈数据结构 特点 图解:
特点:先进后出 (类似弹夹)
2.2 java中的虚拟机栈
2.2.1 定义
全称: Java Virtual Machine Stacks (java 虚拟机栈)
- 线程运行时的内存空间,一个线程运行需要一个栈,多个线程运行则需要多个虚拟机栈
- 虚拟机栈的组成: 一个虚拟机栈是由多个栈帧(Frame)组成
- 每个线程只能有一个活动栈帧(对应着当前正在执行的方法)
栈帧:每个方法运行时内存空间(方法内的方法参数,局部变量,返回地址等等都需要内存空间)
一个栈内一个栈帧
一个栈内多个栈帧
2.2.2 栈的演示
栈的演示
2.2.3 面试注意项
1. 垃圾回收不涉及栈内存
原因;栈内存是由一个个栈帧内存组成,栈帧内存在 每一次的方法调用结束后,会弹出栈,自动进行回收,不需要垃圾回收机制管理栈内存
2. 栈内存的分配并不是越大越好
官方说明文档 地址 :
https://docs.oracle.com/en/java/javase/11/tools/java.html#GUID-7F6FB589-CB59-4B94-BE01-02F48C400243
官方文档中,栈的默认大小在 Linux、macOS、Oracle 系统上都是1024KB
windows 上则是根据windows的虚拟内存来决定
解释说明:
栈内存划分越大,会影响线程数目,因为物理内存的大小是固定的
假设有100M,每一个线程需要的栈内存为1M,那么就可以有100个线程进行运作,
如果栈内存设置为4M,这样只能有25个线程进行运作
所以栈内存大小设置并非越大越好,应进行合理的设置,一般采用官方设定的默认大小就可以了
2.2.4 栈内存溢出
原因:
1. 栈帧过多导致栈帧溢出
一个方法的调用产生一个栈帧,会造成栈内存溢出的情况就是:递归操作,不停的反复调用
代码演示:
首先 通过 IDEA 先设置 栈的内存大小
private static int count;
public static void main(String[] args) {
try {
methodA();
}catch (Throwable e) {
e.printStackTrace();
System.out.println(count);
}
}
private static void methodA() {
count++;
methodA();
}
java.lang.StackOverflowError 异常:栈内存溢出
2 栈帧过大导致栈帧溢出
一个栈帧的大小直接超出栈内存的大小,这种情况不太容易会出现。,一个 1M的栈,一个int类型的局部变量才占4个字节,要很多很多局部变量才能占满整个栈,因此这种情况不常见。