You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java代码触发StackOverflowError的原因困惑及原理求证

代码触发StackOverflowError的原因解析

Java对象初始化的正确顺序

执行new 类名()时,JVM的完整初始化流程是:

  1. 为新对象分配堆内存空间
  2. 给所有实例变量赋予默认初始值(引用类型为null,基本类型对应默认值,比如int为0)
  3. 执行实例变量的显式初始化代码(如private int age = 30;)和实例初始化块
  4. 调用类的构造函数执行剩余初始化逻辑

注意:编译器会把步骤3的代码合并到构造函数的最开头,也就是说,实例变量初始化属于构造函数执行流程的一部分,对应字节码中的<init>方法。

你的代码执行流程拆解

当main方法执行Human h = new Human();时:

  1. JVM分配堆内存,给当前Human实例的变量赋默认值:name=null,age=0,human=null
  2. 执行实例变量显式初始化:
    • 先执行age = 30;,把age的值设为30
    • 接着执行human = new Human();,这会再次触发new Human()的完整初始化流程
  3. 上述步骤会无限递归:每次new Human()都会走到实例变量初始化阶段,再次调用new Human(),永远无法执行到构造函数中的System.out.println("hello world");代码

核心疑问解答

  1. 为什么是StackOverflowError:
    每次执行new Human()都会触发构造函数(字节码<init>方法)的调用,实例变量初始化是构造函数流程的一部分。递归调用<init>方法会不断在调用栈中生成新的方法帧,栈的内存空间固定且有限,很快就会被填满,触发StackOverflowError。你没看到构造函数的输出,是因为递归发生在构造函数核心代码执行之前,但调用栈已经被递归的方法帧占满了。

  2. 为什么资料说构造函数调用导致溢出:
    你的误解在于认为实例变量初始化和构造函数完全分开,但实际上实例变量显式初始化被编译器合并到了构造方法中,递归的根源是每次构造函数执行时都会触发新的构造函数调用,所以这个结论是准确的。

  3. 为什么不是OutOfMemoryError:
    堆内存默认空间远大于栈空间,而且每次递归中创建的对象还没完成初始化(卡在了实例变量初始化的递归步骤),栈会先被耗尽,因此堆溢出不会先发生。

内容的提问来源于stack exchange,提问作者NinaJava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 23:35:33