Tomcat 7.0.3x 啓動時遇到StackOverflowError導致失敗的解決辦法
之前使用tomcat7時遇到啓動報錯問題,日誌如下:
Nov 07, 2014 9:52:10 AM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: T:\svn_techsuite\devtools\eclipse-jee-kepler-SR2-win32-x86_64;;.
Nov 07, 2014 9:52:10 AM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:publishserver' did not find a matching property.
Nov 07, 2014 9:52:10 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Nov 07, 2014 9:52:10 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Nov 07, 2014 9:52:10 AM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 734 ms
Nov 07, 2014 9:52:10 AM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Nov 07, 2014 9:52:10 AM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.37
Nov 07, 2014 9:52:11 AM org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(T:\svn_techsuite\branches\techsuite_4.4_m050\techdev\projs\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\publishserver\WEB-INF\lib\servlet-api.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class
Nov 07, 2014 9:52:14 AM org.apache.catalina.core.ContainerBase startInternal
SEVERE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/publishserver]]
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:800)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/publishserver]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
... 7 more
Caused by: java.lang.IllegalStateException: Unable to complete the scan for annotations for web application [/publishserver]. Possible root causes include a too low setting for -Xss and illegal cyclic inheritance dependencies
at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2109)
at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:1981)
at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1947)
at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1932)
at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1326)
at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:878)
at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:369)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5179)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 7 more
Caused by: java.lang.StackOverflowError
at java.util.HashSet.<init>(HashSet.java:103)
at org.apache.catalina.startup.ContextConfig.populateSCIsForCacheEntry(ContextConfig.java:2252)
at org.apache.catalina.startup.ContextConfig.populateSCIsForCacheEntry(ContextConfig.java:2269)
at org.apache.catalina.startup.ContextConfig.populateSCIsForCacheEntry(ContextConfig.java:2269)
at org.apache.catalina.startup.ContextConfig.populateSCIsForCacheEntry(ContextConfig.java:2269)
at org.apache.catalina.startup.ContextConfig.populateSCIsForCacheEntry(ContextConfig.java:2269)
at org.apache.catalina.startup.ContextConfig.populateSCIsForCacheEntry(ContextConfig.java:2269)
......
Nov 07, 2014 9:52:14 AM org.apache.catalina.core.ContainerBase startInternal
SEVERE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1123)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.startup.Catalina.start(Catalina.java:684)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:800)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 7 more
Nov 07, 2014 9:52:14 AM org.apache.catalina.startup.Catalina start
SEVERE: Catalina.start:
org.apache.catalina.LifecycleException: Failed to start component [StandardServer[8005]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
at org.apache.catalina.startup.Catalina.start(Catalina.java:684)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:322)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:456)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardService[Catalina]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:732)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 7 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 9 more
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1131)
at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:302)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 11 more
Nov 07, 2014 9:52:14 AM org.apache.catalina.startup.Catalina start
INFO: Server startup in 3810 ms
使用tomcat 7.0.3x版本的同學可以發現tomcat啓動慢了不少,而且還可能遇到如下啓動時異常:
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.
tomcat7.0.3X版本支持servlet3.0的特性,比如說支持@WebServlet、@WebListener等等,要支持這些特性,tomcat就得去掃描所有的jar包裏面的每個類。這個異常表明在掃描jar包的時候,遞歸調用太深,導致棧溢出了,tomcat給了一個餿主意,讓你增大xss,這個還是不好,xss加大了,可用線程數就少了。
分析tomcat源代碼,發現它掃描的流程如下:
step-1.掃描所有jar包
step-2.通過查找jar包中META-INF/services/javax.servlet.ServletContainerInitializer文件內的定義,初始化ServletContainerInitializer實現
step-3.如果web.xml(在3.0+版本的根元素web-app)中配置了metadata-complete="true" 或者沒有找到ServletContainerInitializer實現,都不會繼續掃描jar包
所以綜合一下可以得出下面的解決辦法:
- 在web.xml中加入了metadata-complete="true"。在很多場景下,這個異常能夠避免。但是使用諸如 spring-web-3.1.0.RELEASE 或者 jersey-servlet-1.17.1 就杯具了,因爲jar包中定義了一個ServletContainerInitializer,還是導致了掃描jar包。
- 可以用另外的辦法來解決這個問題,我們讓tomcat不掃描指定的jar包,tomcat就要輕鬆得多了,org.apache.tomcat.util.scan.StandardJarScanner中定義了defaultJarsToSkip,有了這個東東,我們就可以跳過某些jar包。如果你不想使用servlet3.0 annotation支持,在tomcat的catalina.properties配置文件中tomcat.util.scan.DefaultJarScanner.jarsToSkip的最後加一個",jersey-*"或者",spring-*",這樣就不會掃描所有的jar包了。這個副作用是一旦這樣設置後,所有部署在該Tomcat下的應用均受到影響。
- 將jar包中含有META-INF/services/javax.servlet.ServletContainerInitializer的定義去掉。這種方式有一定的副作用,會造成該jar不能在其他地方通用,但好處是隻影響該web應用而不會影響到Tomcat的配置(假如該Tomcat下有多個Web應用同時發佈)
- 將jar包中含有META-INF/services/javax.servlet.ServletContainerInitializer中所定義的實現類改寫,使用相同的包路徑和類名,但是在實現那個onStartup方法時不做任何事情,這樣因爲改寫類放在WEB-INF/classes中,它會在jar之前加載完成,所以也就屏蔽了jar中原有的類實現邏輯。這種方式的好處是既不用修改Tomcat配置,也不用修改jar文件,而且只會影響到本身的這個Web應用。
最後強調一點:
tomcat在處理掃描是還有個小bug,比如我遇到了
SEVERE: Unable to process Jar entry [__MACOSX/cn/****/._HandlerFactory.class] from...
這是tomcat在掃描到以.class爲後綴的文件後,就想當然認爲是java的字節碼class文件,很明顯,此文件都不是java類文件。tomcat不應該根據後綴爲.class就一定是java類文件。