StackOverflowError java 栈内存溢出异常定位分析

现象

线上正常运行程序,本地测试加了log.warn(xxxx)日志后直接报StackOverflowError 异常。
error页面

web.xml 中配置的error_page页面,后台代码并没有具体报出来。

分析

栈内存是线程私有,尝试是否能够从线程堆栈信息查处具体原因

# 查看java进程
jps -l                                                
76017 sun.tools.jps.Jps
48469 
59846 org.apache.catalina.startup.Bootstrap
75358 org.jetbrains.jps.cmdline.Launcher
# dump出线程队栈信息
jstack -l 59846 >> /Users/xxx/Desktop/dump.log

可惜占内存溢出异常时间太短,很难在正在异常时间正好dump出线程堆栈信息

尝试加大栈内存,延长栈异常时间
重新启动项目加上jvm参数,这里将栈内存增加到100M,同时加上打印GC详情信息

-XX:+PrintGCDetails  -Xss100M

重新运行异常代码,日志如下,可以看到栈内存溢出前,一直尝试做fullgc,持续了大约5~8秒

[GC (Allocation Failure) [PSYoungGen: 889056K->81675K(953344K)] 2710703K->1903340K(3749888K), 0.1361416 secs] [Times: user=0.06 sys=0.55, real=0.14 secs] 
[GC (Allocation Failure) [PSYoungGen: 629515K->281504K(972800K)] 2451180K->2179106K(3769344K), 0.1584534 secs] [Times: user=0.21 sys=0.55, real=0.15 secs] 
[GC (Allocation Failure) [PSYoungGen: 829344K->282720K(952320K)] 2726946K->2450456K(3748864K), 0.2698855 secs] [Times: user=0.36 sys=1.07, real=0.27 secs] 
[GC (Allocation Failure) [PSYoungGen: 837728K->290944K(966144K)] 3005464K->2729323K(3762688K), 0.2007395 secs] [Times: user=0.65 sys=0.64, real=0.20 secs] 
[Full GC (Ergonomics) [PSYoungGen: 290944K->0K(966144K)] [ParOldGen: 2438379K->955318K(2184704K)] 2729323K->955318K(3150848K), [Metaspace: 103144K->103144K(1142784K)], 0.8445731 secs] [Times: user=1.65 sys=1.29, real=0.85 secs] 
[GC (Allocation Failure) [PSYoungGen: 555008K->291040K(974336K)] 1510326K->1246358K(3159040K), 0.1659465 secs] [Times: user=0.90 sys=0.12, real=0.16 secs] 
[GC (Allocation Failure) [PSYoungGen: 870624K->304192K(982528K)] 1825942K->1533716K(3167232K), 0.2576112 secs] [Times: user=0.93 sys=0.72, real=0.25 secs] 
[GC (Allocation Failure) [PSYoungGen: 883776K->304384K(993280K)] 2113300K->1820275K(3177984K), 0.2969893 secs] [Times: user=1.07 sys=0.67, real=0.30 secs] 
[GC (Allocation Failure) [PSYoungGen: 895744K->309952K(993792K)] 2411635K->2112280K(3178496K), 0.3110348 secs] [Times: user=1.20 sys=0.67, real=0.31 secs] 
[Full GC (Ergonomics) [PSYoungGen: 309952K->0K(993792K)] [ParOldGen: 1802328K->2060148K(2796544K)] 2112280K->2060148K(3790336K), [Metaspace: 103144K->103144K(1142784K)], 1.0683581 secs] [Times: user=3.92 sys=0.06, real=1.07 secs] 
2022-07-17  20:51:27.481 [http-nio-8081-exec-3] ERROR 

在异常溢出前继续dump出线程堆栈信息,如下图,这是就可以清楚的定位到哪个对象产生的问题,Order.toString()和
MException.toString()两个类一直交替打印,明显就是对象循环依赖,toString() 方法无限被调用,导致栈内存溢出。
toString() 方法是log.warn() 调用的,toString()方法是lombok @Data 自动生成的。


JNI global references: 43150

2022-07-17 19:27:32
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.311-b11 mixed mode):

"http-nio-8081-exec-7" #63 daemon prio=5 os_prio=31 tid=0x00007ff72518b000 nid=0x15843 runnable [0x0000000365a7c000]
   java.lang.Thread.State: RUNNABLE
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.Order.toString(Order.java:19)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.MException.toString(MException.java:11)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at java.util.AbstractCollection.toString(AbstractCollection.java:462)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.Order.toString(Order.java:19)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.MException.toString(MException.java:11)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at java.util.AbstractCollection.toString(AbstractCollection.java:462)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.Order.toString(Order.java:19)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.MException.toString(MException.java:11)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at java.util.AbstractCollection.toString(AbstractCollection.java:462)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.Order.toString(Order.java:19)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.MException.toString(MException.java:11)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at java.util.AbstractCollection.toString(AbstractCollection.java:462)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
	at com.oncodsp.boss.pojo.Order.toString(Order.java:19)
	at java.lang.String.valueOf(String.java:2994)
	at java.lang.StringBuilder.append(StringBuilder.java:131)
...

解决办法

1)重写对象的toString()
2)避免对象的循环依赖,本次选择的方法,从业务角度分析,Order中包含了MException后,没必要再MException在里包含Order信息

折腾了很久,上面的查找方法很麻烦,重新想一下栈内存溢出后台为啥没有打印具体信息,结果发现代码被try catch了,catch里捕捉的是Exception 对象,查看一下StackOverflowError 类,该类属于Error子类,不属于Exception子类。所以将捕捉异常范围再大一点,直接捕获Throwable 异常,则可以直接后台打印出异常信息

异常类

idea错误日志

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值