tomcat启动时检测到循环继承而栈溢出的问题:Caused by: java.lang.IllegalStateException: Unable to complete the scan for annotations for web application [/test] due to a StackOverflowError. Possible root causes include
最近在公司更新一个老项目的时候,发现部署项目后tomcat报错,错误如下:
Caused by: java.lang.IllegalStateException:
Unable to complete the scan for annotations for web application [/test]
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.jaxen.util.AncestorAxisIterator->
org.jaxen.util.AncestorOrSelfAxisIterator->
org.jaxen.util.AncestorAxisIterator]
at org.apache.catalina.startup.ContextConfig.checkHandlesTypes(ContextConfig.java:2112)
at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2059)
at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:1934)
at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1900)
at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1885)
at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1317)
at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:876)
at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:374)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5355)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
这是在tomcat解析servlet3注释时进行类扫描的过程,发现了两个类的继承关系存在循环继承的情况而导致了栈溢出。
排查了一下,是因为应用所依赖的 dom4j-1.1.jar 里存在 AncestorAxisIterator 和子类 AncestorOrSelfAxisIterato。
% javap org.jaxen.util.AncestorAxisIterator
Compiled from "AncestorAxisIterator.java"
public class org.jaxen.util.AncestorAxisIterator extends org.jaxen.util.StackedIterator {
protected org.jaxen.util.AncestorAxisIterator();
public org.jaxen.util.AncestorAxisIterator(java.lang.Object, org.jaxen.Navigator);
protected java.util.Iterator createIterator(java.lang.Object);
}
% javap org.jaxen.util.AncestorOrSelfAxisIterator
Compiled from "AncestorOrSelfAxisIterator.java"
public class org.jaxen.util.AncestorOrSelfAxisIterator extends org.jaxen.util.AncestorAxisIterator {
public org.jaxen.util.AncestorOrSelfAxisIterator(java.lang.Object, org.jaxen.Navigator);
protected java.util.Iterator createIterator(java.lang.Object);
}
同时应用所依赖的 sourceforge.jaxen-1.1.jar 里面也存在这两个同名类,但继承关系正好相反:
% javap org.jaxen.util.AncestorAxisIterator
Compiled from "AncestorAxisIterator.java"
public class org.jaxen.util.AncestorAxisIterator extends org.jaxen.util.AncestorOrSelfAxisIterator {
public org.jaxen.util.AncestorAxisIterator(java.lang.Object, org.jaxen.Navigator);
}
% javap org.jaxen.util.AncestorOrSelfAxisIterator
Compiled from "AncestorOrSelfAxisIterator.java"
public class org.jaxen.util.AncestorOrSelfAxisIterator implements java.util.Iterator {
public org.jaxen.util.AncestorOrSelfAxisIterator(java.lang.Object, org.jaxen.Navigator);
public boolean hasNext();
public java.lang.Object next();
public void remove();
}
简单的说,在第1个jar里存在B继承自A,在第2个jar里存在同名的A和B,但却是A继承自B。其实也能运行的,只是可能出现类加载时可能加载的不一定是你想要的那个,但tomcat做类型检查的时候把这个当成了一个环。
在ContextConfig.processAnnotationsStream方法里,每次解析之后要对类型做一次检测,然后才获取注释信息:
ClassParser parser = new ClassParser(is, null);
JavaClass clazz = parser.parse();
checkHandlesTypes(clazz);
...
AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
...
再看这个用来检测类型的checkHandlesTypes方法里面:
populateJavaClassCache(className, javaClass);
JavaClassCacheEntry entry = javaClassCache.get(className);
if (entry.getSciSet() == null) {
try {
populateSCIsForCacheEntry(entry); // 这里
} catch (StackOverflowError soe) {
throw new IllegalStateException(sm.getString(
"contextConfig.annotationsStackOverflow",context.getName(),
classHierarchyToString(className, entry)));
}
}
每次新解析出来的类(tomcat里定义了JavaClass来描述),会被populateJavaClassCache放入cache,这个cache内部是个Map,所以对于key相同的会存在把以前的值覆盖了的情况,这个“环形继承”的现象就比较好解释了。
Map里的key是String类型即类名,value是JavaClassCacheEntry类型封装了JavaClass及其父类和接口信息。我们假设第一个jar里B继承自A,它们被放入cache的时候键值对是这样的:
"A" -> [JavaClass-A, 父类Object,父接口]"
"B" -> [JavaClass-B, 父类A,父接口]
然后当解析到第2个jar里的A的时候,覆盖了之前A的键值对,变成了:
"A" -> [JavaClass-A, 父类B,父接口]
"B" -> [JavaClass-B, 父类A,父接口]
这2个的继承关系在这个cache里被描述成了环状,然后在接下来的populateSCIsForCacheEntry方法里找父类的时候就绕不出来了,最终导致了栈溢出。
这个算是cache设计不太合理,没有考虑到不同jar下面有相同的类的情况。问题确认之后,让应用方去修正自己的依赖就可以了,但应用方说之前在7026的时候,是可以正常启动的。这就有意思了,接着一番排查之后,发现在7026版本里,ContextConfig.webConfig的时候先判断了一下web.xml里的版本信息,如果版本>=3才会去扫描类里的servlet3注释信息。
// Parse context level web.xml
InputSource contextWebXml = getContextWebXmlSource();
parseWebXml(contextWebXml, webXml, false);
if (webXml.getMajorVersion() >= 3) {
// 扫描jar里的web-fragment.xml 和 servlet3注释信息
...
}
而在7054版本里是没有这个判断的。搜了一下,发现是在7029这个版本里去掉的这个判断。在7029的changelog里:
As per section 1.6.2 of the Servlet 3.0 specification and clarification from the Servlet Expert Group, the servlet specification version declared in web.xml no longer controls if Tomcat scans for annotations. Annotation scanning is now always performed – regardless of the version declared in web.xml – unless metadata complete is set to true.
之前对servlet3规范理解不够清晰;之所以改,是因为在web.xml里定义的servlet版本,不再控制tomcat是否去扫描每个类里的注释信息。也就是说不管web.xml里声明的servlet版本是什么,都会进行注释扫描,除非metadata-complete属性设置为true(默认是false)。所以在7029版本之后改为了判断 webXml.isMetadataComplete() 是否需要进行扫描注释信息。
tomcat启动时检测到循环继承而栈溢出的问题:Caused by: java.lang.IllegalStateException: Unable to complete the scan for annotations for web application [/test] due to a StackOverflowError. Possible root causes include的更多相关文章
- Caused by: java.lang.IllegalStateException: Unable to complete the scan for annotations for web application [/Cppcc] due to a StackOverflowError. Possible root causes include a too low setting for -Xs
解决办法:(1)修改D:\Java\apache-tomcat-7.0.88\conf\catalina.properties (122line) (2)如org.apache.catalina.st ...
- Unable to complete the scan for annotations for web application [/wrs] due to a StackOverflowError. Possible root causes include a too low setting for -Xss and illegal cyclic inheritance dependencies.
tomcat启动报错:Jul 20, 2018 11:48:37 AM org.apache.catalina.core.ContainerBase addChildInternalSEVERE: C ...
- 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.jaxen.util.AncestorAxisIt
七月 31, 2019 4:39:01 下午 org.apache.catalina.startup.VersionLoggerListener log信息: Server version: Apac ...
- myeclipse 无法启动 java.lang.IllegalStateException: Unable to acquire application service. Ensure that the org.eclipse.core.runtime bundle is resolved and started (see config.ini).
把myeclipse10 按照目录完整拷贝到了另外一台电脑, 另外的目录 原安装目录 D\:\soft\i\myeclipse10 新安装目录 E\:\soft\myeclipse10 双击启动失败, ...
- 【spring boot】【elasticsearch】spring boot整合elasticsearch,启动报错Caused by: java.lang.IllegalStateException: availableProcessors is already set to [8], rejecting [8
spring boot整合elasticsearch, 启动报错: Caused by: java.lang.IllegalStateException: availableProcessors ], ...
- springboot测试启动报错java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test
springboot测试启动报错: java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you ne ...
- due to a StackOverflowError. Possible root causes include a too low。。
我们可以用另外的办法来解决这个问题,我们让tomcat不扫描指定的jar包,tomcat就要轻松得多了,org.apache.tomcat.util.scan.StandardJarScanner中定 ...
- SpringBoot测试类启动错误 java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test
报错 java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @Cont ...
- tomcat启动报错:java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException:
tomcat日志: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start com ...
随机推荐
- linux ioctl 接口
大部分驱动需要 -- 除了读写设备的能力 -- 通过设备驱动进行各种硬件控制的能力. 大 部分设备可进行超出简单的数据传输之外的操作; 用户空间必须常常能够请求, 例如, 设 备锁上它的门, 弹出它的 ...
- WNMP nginx+php5+mysql测试环境安装(Windows7)(二)
3. 安装Zend Optimizer Zend Optimizer对那些在被最终执行之前由Run-Time Complier产生的代码进行优化,提高PHP应用程序的执行速度.一般情况下,执行使用Ze ...
- dotnet 通过 WMI 获取指定进程的输入命令行
本文告诉大家如何使用 WMI 通过 Process 获取这个进程传入的命令行 使用下面代码,使用 Win32_Process 拿到所有的进程,通过 WHERE 判断当前的进程,然后拿到进程传入的命令 ...
- 今天IT告告诉我,我电脑上的java jdk属性收费滴!需卸载
敲着代码,IT突然跑来说,你电脑的Jdk版本属于收费版,目前需要卸载!啊哦...手贱!每次有更新我都更新了,Java要收费老早之前耳闻了,但是俺很少做java,一般都在.Net,所以忽略鸟.. 于是G ...
- 一培训机构设计的学习android课程内容:供大家参考
转自:http://www.cnblogs.com/csj007523/archive/2011/06/16/2082682.html 一培训机构设计的学习android课程内容:供大家参考 第一阶段 ...
- Servlet request 面试题
使用request获得请求行:String getmethod():获得请求的资源:String getcontextpath():----web应用名称request是一个域对象request完成请 ...
- mysql主从之双主配置
mysql双主配置 mysql双主其实就是互相同步,互为主从 任意一台都能够执行插入动作 生产环境用得非常少,因为还是担心数据一致的问题 生产环境一般来说主从已经够用 172.19.132.121的配 ...
- HDU3394 Railway 题解(边双连通分量)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3394 题目大意: 给定一个无向图,如果从一个点出发经过一些点和边能回到该点本身,那么一路走过来的这些点 ...
- 开箱即用~基于.NET Core的统一应用逻辑分层框架设计
目前公司系统多个应用分层结构各不相同,给运维和未来的开发带来了巨大的成本,分层架构看似很简单,但保证整个研发中心都使用统一的分层架构就不容易了. 那么如何保证整个研发中心都使用统一的分层架构,以达到提 ...
- python I/O编程
1.文件读写 使用open打开文件,f=open('/user/test.txt','r'),r表示可读 如果文件不存在,则抛出IOError 文件打开,则用read()方法进行读取 最后关闭用clo ...