V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JasonLaw
V2EX  ›  Java

关于 StackOverflowError 和 OutOfMemoryError 的疑惑

  •  
  •   JasonLaw · 2020-08-31 17:08:28 +08:00 · 2528 次点击
    这是一个创建于 1536 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在“深入理解 Java 虚拟机(第 2 版)- 2.4.2 虚拟机栈和本地方法栈溢出”中,它说:

    关于虚拟机栈和本地方法栈,在 Java 虚拟机规范中描述了两种异常:

    • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常。
    • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError 异常。

    关于“如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常”,我查阅了What is the maximum depth of the java call stack? - Stack Overflow,其实根本没有"maximum depth of the java call stack"这个东西。在memory leaks - What is a StackOverflowError? - Stack Overflow中,Varun 的回答说“If there is no space for a new stack frame then, the StackOverflowError is thrown by the Java Virtual Machine (JVM).”。

    关于“如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError 异常”,在java - JVM option -Xss - What does it do exactly? - Stack Overflow中,Adam Adamaszek 的回答说“OutOfMemoryError: unable to create new native thread (too many threads, each thread has a large stack)”。

    我的疑问

    1. 书中所说的内容是不是不太正确?或者说不太容易理解?

    2. 关于 StackOverflowError 和 OutOfMemoryError 的定义,"StackOverflowError: the stack size is greater than the limit, OutOfMemoryError: unable to create new native thread"是正确的。对吗?

    第 1 条附言  ·  2020-09-01 17:26:12 +08:00

    不太相关的更新:我决定选择Inside the Java Virtual Machine,抛弃深入理解Java虚拟机(第2版) (豆瓣)

    9 条回复    2020-09-01 22:21:00 +08:00
    hotpot6147
        1
    hotpot6147  
       2020-08-31 17:25:02 +08:00
    肯定是正确的啊

    举个栗子:
    假设你的 jvm 限制每个栈最大为 1MB,jvm 堆内存为 300MB

    如果你的程序里有很多很多线程,每个线程栈都在 1MB 以内,但是它们的合计大小超过了 300MB,这就会触发 OutOfMemoryError

    如果你的程序里线程的合计大小小于 300MB,但是有一个线程使用的栈超过了 1MB (比如写了个嵌套层级很深的递归方法)那么就会触发 StackOverflowError
    vincenttone
        2
    vincenttone  
       2020-08-31 20:13:25 +08:00   ❤️ 1
    JVM 手册里有写:
    The following exceptional conditions are associated with Java Virtual Machine stacks:
    • If the computation in a thread requires a larger Java Virtual Machine stack than is permitted, the Java Virtual Machine throws a StackOverflowError.
    • If Java Virtual Machine stacks can be dynamically expanded, and expansion is attempted but insufficient memory can be made available to effect the expansion, or if insufficient memory can be made available to create the initial Java Virtual Machine stack for a new thread, the Java Virtual Machine throws an OutOfMemoryError.

    一个是有栈但是不够用导致的,另一个就是申请栈内存(或者动态扩展)失败导致的。相当于一个触发来自栈管理器、一个触发来自内存管理器——所以异常的名字一个是关于栈的,另一个是关于内存的。
    JasonLaw
        3
    JasonLaw  
    OP
       2020-08-31 20:31:58 +08:00
    vincenttone 所说的内容来源于 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 中的 “2.5.2. Java Virtual Machine Stacks”
    CRVV
        4
    CRVV  
       2020-08-31 21:17:16 +08:00
    从回复来看,这个书只是翻译了 Java 文档而已。

    然后这个文档是 Java Virtual Machine Specifications,它是定义 JVM 的文档,而不是描述 Oracle JVM 工作方式的文档。
    这个文档当然没有写错,但它确实写成了一个不太好懂的方式。当然了这个文档有 600 多页,也不是给初学者看的。

    把有疑问的内容都粘过来,是这样的

    This specification permits Java Virtual Machine stacks either to be of a fixed size or to dynamically expand and contract as required by the computation. If the Java Virtual Machine stacks are of a fixed size, the size of each Java Virtual Machine stack may be chosen independently when that stack is created.

    A Java Virtual Machine implementation may provide the programmer or the user control over the initial size of Java Virtual Machine stacks, as well as, in the case of dynamically expanding or contracting Java Virtual Machine stacks, control over the maximum and minimum sizes.

    The following exceptional conditions are associated with Java Virtual Machine stacks:

    If the computation in a thread requires a larger Java Virtual Machine stack than is permitted, the Java Virtual Machine throws a StackOverflowError.

    If Java Virtual Machine stacks can be dynamically expanded, and expansion is attempted but insufficient memory can be made available to effect the expansion, or if insufficient memory can be made available to create the initial Java Virtual Machine stack for a new thread, the Java Virtual Machine throws an OutOfMemoryError.


    所以,
    这里有两种情况,分别是 fixed sized 的栈和可以 dynamically expanding 的栈。
    maximum depth of the java call stack
    对 fixed 的栈来说,那显然是它的初始大小,因为它不能变。
    对 dynamical 的栈来说,那显然是它的 maximum size,在第二段里提到了。

    后面那个 OutOfMemoryError 的前提是 dynamical 的栈,所以就是 expand stack 的时候,内存不够了报的错。

    Oracle JVM 的栈是 fixed sized,-Xss 是在设定栈的大小,也就是第二段里说的 initial size of Java Virtual Machine stacks,它和这个 OutOfMemoryError 毫无关系。
    lff0305
        5
    lff0305  
       2020-09-01 10:41:30 +08:00
    jvm 通过-xss 指定每个线程使用的栈的大小(类似的 createThread api 同样可以指定栈的大小).记得默认 java 栈大小是 512kb,C++是 2MB. 栈的大小决定了函数调用的深度(因为 Java 不像 c++。c++要在栈上传递值或者引用。Java 只需要传递引用。所以 jvm 选择了一个较小的栈).

    上文说的 outofmemory error 是针对一个虚拟机能创建的最多的线程数。因为栈是在 heap 上分配的,自然最大的线程数就约等于 xmx / xss 。
    创建了这么多线程,再创建新的线程就会出现 outofmemory error 因为没有剩余的 heap 保留来做线程栈
    JasonLaw
        6
    JasonLaw  
    OP
       2020-09-01 10:54:19 +08:00
    说句不好听的话,我感觉回复总是出现一些答非所问,还是希望大家认真阅读题目,然后再回答。如果是我自己没有描述清楚的话,可以直接在回复里面沟通。
    251
        7
    251  
       2020-09-01 20:41:27 +08:00
    @CRVV 说的对,是你自己没看懂而已。
    If there is no space for a new stack frame then, the StackOverflowError is thrown by the Java Virtual Machine (JVM).
    这种说话和"请求的栈深度大于虚拟机所允许的最大深度" 是一个意思而已。


    OutOfMemoryError: unable to create new native thread (too many threads, each thread has a large stack)”。
    这个只是 OutOfMemoryError 的其中一种情况。
    JasonLaw
        8
    JasonLaw  
    OP
       2020-09-01 21:11:08 +08:00
    @251 #7 我没有说 CRVV 说的有问题,很多回复说的都是对的。但是对的不代表回答了我的问题,我没有说它们的内容是错的,我说的是很多回复都是答非所问,基本并没有正面回答我的两个疑问,而我的疑问更多的是关于书本所说内容的正确性。

    如果你觉得“If there is no space for a new stack frame then, the StackOverflowError is thrown by the Java Virtual Machine (JVM).”跟“请求的栈深度大于虚拟机所允许的最大深度”相等的话,那我无话可说。
    251
        9
    251  
       2020-09-01 22:21:00 +08:00
    CRVV 已经正面回答你了,只是你自己没看懂,觉得答非所闻。

    相不相等要看本质。"If there is no space for a new stack frame then, the StackOverflowError is thrown by the Java Virtual Machine (JVM)" 本质是超过了设定的值,“请求的栈深度大于虚拟机所允许的最大深度”的本质一样也是超过了设定的值. 这个值就是虚拟机设置的 fixed sized ;
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3345 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 12:00 · PVG 20:00 · LAX 04:00 · JFK 07:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.