java虚拟机栈的内存结构


[原创]个人理解,请批判接受,有误请指正。转载请注明出处: https://heyfl.gitee.io/JVM/JVM-Memory-Structure-Stack.html

学习JVM的可以去我的GitHub 上查看我的Xmind详细笔记 对整本《深入理解JVM》都有详尽的笔记,帮助理解

一、 前言

Java栈分为两种:

  1. Java虚拟机栈:
    • 描述java【方法】执行的【内存模型】
    • 每个线程进入每个方法对应一个栈帧
  2. 本地方法栈(本文不做描述)
    • 同虚拟机栈,区别在于:虚拟机栈服务于Java方法(字节码),本地方法栈服务于Native方法
      本文主要讲Java虚拟机栈的内存结构

一图胜千言

Java虚拟机栈



二、Java虚拟机栈的组成:

1. 栈帧

栈帧结构

每个方法对应一个栈帧,在线程运行到该方法时才创建,随着方法结束而销毁

2. 线程栈

线程栈结构

栈帧内存在线程内存上进行分配,每条线程能为栈帧分配的总大小最大值为-Xss
为了方便,我们这里 [把这条线程对应的内存] 称为 [线程栈]

3. Java虚拟机栈

Java虚拟机栈结构

Java虚拟机栈是描述java【方法】执行的【内存模型】–> 实际上它是 当前时刻[所有的线程栈]集合的统称


三、 Java虚拟机栈会出现的异常:

经过上面的描述,我们可以清晰地知道:

  1. 【Java虚拟机栈】由【当前所有的线程栈】组成
  2. 【每个线程栈由】由【这条线程所对应的所有栈帧】组成
    因此,可能会出现2种内存溢出的情况 (也可参考Oracle JavaSE8的JVM规范:The Structure of the Java Virtual Machine-Java Virtual Machine Stacks
  • [x] 线程栈溢出(Stack Overflow)
    • 某条线程对应的栈帧内存之和 超过线程栈内存的最大值-Xss
      根据栈帧的定义可以理解到:该问题主要是一条线程在某一时间点同时存在于多个方法中(网上称作方法深度过深,最常见的案例就是递归调用)
  • [x] 从Java虚拟机栈层面溢出(OOM)

    当前时刻[所有的线程栈]内存之和 超过 Java虚拟机栈所允许的最大值

    • 导致这个问题的原因是当前新建的线程数太多(当然,换句话也以说是当前计算机可分配给Java虚拟机的空间太少)
      每条线程占用的空间都未-Xss,新建多了也就OOM了;这种情况一般较少发生,主要有以下因素:
      • 系统对线程数上限的限制
      • 线程过多,会导致CPU崩溃,也就是说系统早就挂了
    • Java虚拟机栈所允许的最大值:JVM 可分配内存-其他JVM内存结构空间最大值(主要是堆)–>剩下的作为栈的总空间最大值(来自《深入理解Java虚拟机》P54)

四、验证

4.1. 线程栈溢出(StackOverFlow)

通过-Xss调整线程栈大小测试[线程栈最大占用内存为-Xss]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private static AtomicLong i = new AtomicLong(0);

public static void main(String[] args) throws InterruptedException {
//1. 测试错误StackOverFlow
testStackOverFlow();

//2.测试错误OOM
//testOOM();

}
/**
* StackOverFlow每次深度不一样是因为JIT优化
* -Djava.compiler=NONE禁用JIT优化后每次深度一样
*
*
* 测试参数:-Xss256k
*/
private static void testStackOverFlow() {
//new Thread(() -> {
StackErrorTest testData = new StackErrorTest();
try {

testData.test1();
} catch (Throwable e) {
System.out.println(Thread.currentThread().getName() + " " + i.get());

}
//}).start();
}

运行参数设置-Xss256k -Djava.compiler=NONE,多次运行输出结果一致:

1
main 1882

运行参数设置-Xss512k -Djava.compiler=NONE,多次运行输出结果一致:

1
main 4861

2.2.2 从Java虚拟机栈溢出(OOM)

  • 注意 Mac有单进程线程数量限制,16G的Mac电脑限制为5000,修改方式参考(不好意思 不能修改 )
  • 在一些配置较低的电脑可能会死机,请小心运行
  • 建议使用虚拟机运行
影响因素描述
系统限制系统允许进程的最大线程数
-Xss每条线程栈占用的内存
-Xmx堆空间最大值

详细验证可以参考这篇文章


---
作者

神奇宝贝大师

发布于

2019-02-19

更新于

2019-02-20

许可协议

评论