JAVA系统启动栈内存溢出-StackOverflowError
线上服务器启动报错日志如下:
Caused by: java.lang.IllegalStateException: Unable to complete the scan for annotations for web application [] due to a StackOverflowError. Possible root causes include a too low setting for -Xss and illegal cyclic inheritance dependencies. The class hierarchy being processed was [org.bouncycastle.asn1.ASN1EncodableVector->org.bouncycastle.asn1.DEREncodableVector->org.bouncycastle.asn1.ASN1EncodableVector]
at org.apache.catalina.startup.ContextConfig.checkHandlesTypes(ContextConfig.java:2066)
at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2012)
at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:1961)
at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1936)
at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1897)
看日志提示是-Xss参数设置过低引起的栈内存溢出,先解释下-Xss参数。
JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右
一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。
但是根据-Xss参数调整栈内存大小之后,再重新启动还是会StackOverflowError。所以栈内存设置过小不是根本原因。
再看下这个错误信息 StackOverflowError,抛出这个错误表明应用程序因为深递归导致栈被耗尽了。
StackOverflowError 是 VirtualMachineError 的扩展类,VirtualMachineError 表明 JVM 中断或者已经耗尽资源,无法运行。
而且,VirtualMachineError 类扩展自 Error 类,这个类用于指出那些应用程序不需捕获的严重问题。因为这些错误是在可能永远不会发生的异常情况下产生,所以方法中没有在它的 throw 语句中声明。
Java 里的 StackOverflowError
Java 应用程序唤起一个方法调用时就会在调用栈上分配一个栈帧, 这个栈帧包含引用方法的参数,本地参数,以及方法的返回地址。
这个返回地址是被引用的方法返回后程序能够继续执行的执行点。如果没有一个新的栈帧所需空间,Java 虚拟机就会抛出 StackOverflowError。
最常见的可能耗光 Java 应用程序的栈的场景是程序里的递归。递归时一个方法在执行过程中会调用自己。 递归被认为是一个强大的多用途编程技术,为了避免出现 StackOverflowError,使用时必须特别小心。
如何处理 StackOverflowError
最简单的解决方案是仔细检查输出信息中的栈路径,查明模式重复的代码行号。这些行号对应的代码被递归调用了。确认这些行后,你必须小心的检查你的代码,弄清楚为什么递归永远不结束。
如果你确认递归实现是正确的,为了允许大量的调用,你可以增加栈的大小。依赖于安装的 Java 虚拟机,默认的线程栈大小可能是 512KB 或者 1MB。你可以使用 -Xss 标识来增加线程栈的大小。这个标识即可以通过项目的配置也可以通过命令行来指定。
-Xss 参数的格式:-Xss<size>[g|G|m|M|k|K]
在看系统启动报错日志信息
The class hierarchy being processed was [org.bouncycastle.asn1.ASN1EncodableVector->org.bouncycastle.asn1.DEREncodableVector->org.bouncycastle.asn1.ASN1EncodableVector]
根据这段提示信息是ASN1EncodableVector 依赖了DEREncodableVector,DEREncodableVector后面又依赖了ASN1EncodableVector ,造成了死循环,导致栈内存消耗殆尽。
然后根据这个类名去项目中搜索查看类结构,根据类名搜索结果如下:
搜索到了两个一模一样的类,由于之前项目启动是没有问题的,而itext-asian-5.2.0.jar 也是最近添加的jar包依赖,所以猜测可能是两个jar包有相同包名的类,导致了死循环依赖,所以直接出现了栈内存溢出问题。
找到了相同的类,那就可以先把项目中没有使用到的bcprov-jdk15-1.43.jar进行排除掉,然后上线,系统启动正常,原来的栈内存溢出问题没有出现。
具体bcprov-jdk15-1.43.jar ASN1EncodableVector 源码如下:
package org.bouncycastle.asn1;
import org.bouncycastle.asn1.DEREncodableVector;
public class ASN1EncodableVector extends DEREncodableVector {
public ASN1EncodableVector() {
}
}
具体itext-asian-5.2.0.jar ASN1EncodableVector 源码如下:
package org.bouncycastle.asn1;
import org.bouncycastle.asn1.ASN1EncodableVector;
public class DEREncodableVector extends ASN1EncodableVector {
/** @deprecated */
public DEREncodableVector() {
}
}
通过源码可以看到ASN1EncodableVector 和DEREncodableVector互为父类和子类,造成了死循环继承。 所以只需要将没有使用的一方jar包依赖排除即可
网友评论