Spring循环依赖与栈溢出问题详细分析
一、问题现象
- 错误信息:
StackOverflowError在类加载和反射阶段 - JVM参数:
-Xss228k(栈大小仅228KB) - 解决方案: 改为
-Xss1m(1MB) 后启动成功
二、Spring框架处理循环依赖的机制
2.1 三级缓存机制
Spring通过三级缓存来解决单例Bean的循环依赖问题:
// Spring容器中的三级缓存
// 第一级缓存:singletonObjects - 完全初始化好的单例Bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 第二级缓存:earlySingletonObjects - 提前暴露的早期Bean引用(未完全初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 第三级缓存:singletonFactories - 单例Bean的工厂对象(用于创建代理对象)
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
2.2 循环依赖解决流程
以 DaemonSetAgentInfoService (A) 和 MyAgentInfoService (B) 为例:
正常流程(无循环依赖):
1. 创建A → 实例化A → 填充属性 → 初始化A → 放入singletonObjects
2. 创建B → 实例化B → 填充属性(注入A)→ 初始化B → 放入singletonObjects
循环依赖流程(Spring三级缓存):
步骤1: 开始创建A
- 实例化A(调用构造函数)
- 将A的ObjectFactory放入singletonFactories(三级缓存)
步骤2: 填充A的属性
- 发现需要注入B
- 从缓存中查找B,未找到
步骤3: 开始创建B
- 实例化B(调用构造函数)
- 将B的ObjectFactory放入singletonFactories(三级缓存)
步骤4: 填充B的属性
- 发现需要注入A
- 从singletonFactories中获取A的ObjectFactory
- 调用ObjectFactory.getObject()获取A的早期引用(可能是代理对象)
- 将A放入earlySingletonObjects(二级缓存)
- 将A从singletonFactories中移除
步骤5: 完成B的初始化
- 初始化B
- 将B放入singletonObjects(一级缓存)
- 将B从earlySingletonObjects中移除
步骤6: 继续完成A的初始化
- 将B注入到A中
- 初始化A
- 将A放入singletonObjects(一级缓存)
- 将A从earlySingletonObjects中移除
2.3 为什么会出现栈溢出?循环依赖的真正影响
关键问题:循环依赖本身不会直接导致StackOverflowError
你的质疑是对的! 没有循环依赖时,如果类结构复杂、调用链深,也可能因为栈空间小触发StackOverflowError。
栈溢出发生的真正原因分析
从错误堆栈可以看到:
java.lang.ClassLoader.defineClass1 (Native Method)
java.lang.

最低0.47元/天 解锁文章
753

被折叠的 条评论
为什么被折叠?



