Java 为什么Tomcat在使用共享库时启动速度会慢得多?
问题 我们有一个Java 为什么Tomcat在使用共享库时启动速度会慢得多?,java,tomcat,Java,Tomcat,问题 我们有一个tomcat7.0.35运行在25个webapp上,这些webapp都共享相同版本的相同库(保证这一点永远不会改变!)。我想不是每个项目都加载同一个库,而是将它们移动到${catalina.home}/lib,这样可以减少所需的内存负载 然而,这是有代价的:每个webapp的部署时间从5秒增加到2分钟,这是不可行的 我做了一些阅读(例如),不幸的是,我无法找到减少启动时间的方法。有没有办法解决这个问题 请求的分析信息 我在两个不同的场景中运行了jstack和jvisualvm,我
tomcat7.0.35
运行在25个webapp上,这些webapp都共享相同版本的相同库(保证这一点永远不会改变!)。我想不是每个项目都加载同一个库,而是将它们移动到${catalina.home}/lib
,这样可以减少所需的内存负载
然而,这是有代价的:每个webapp的部署时间从5秒增加到2分钟,这是不可行的
我做了一些阅读(例如),不幸的是,我无法找到减少启动时间的方法。有没有办法解决这个问题
请求的分析信息
我在两个不同的场景中运行了jstack
和jvisualvm
,我将在这里介绍一些发现(如果您需要其他信息,请告诉我):
org.apache.catalina.loader.WebappClassLoader.findResourceInternal
运行该方法的线程的jstack输出:
"localhost-startStop-1" daemon prio=10 tid=0x00007f17f8001800 nid=0x13b8 runnable [0x00007f183c800000]
java.lang.Thread.State: RUNNABLE
at java.text.MessageFormat.subformat(MessageFormat.java:1250)
at java.text.MessageFormat.format(MessageFormat.java:819)
at org.apache.naming.StringManager.getString(StringManager.java:145)
at org.apache.naming.resources.BaseDirContext.lookup(BaseDirContext.java:500)
at org.apache.naming.resources.ProxyDirContext.lookup(ProxyDirContext.java:310)
at org.apache.catalina.loader.WebappClassLoader.findResourceInternal(WebappClassLoader.java:3011)
at org.apache.catalina.loader.WebappClassLoader.findResource(WebappClassLoader.java:1262)
at org.apache.catalina.loader.WebappClassLoader.getResourceAsStream(WebappClassLoader.java:1499)
at org.apache.catalina.startup.ContextConfig.populateJavaClassCache(ContextConfig.java:2302)
at org.apache.catalina.startup.ContextConfig.populateJavaClassCache(ContextConfig.java:2294)
at org.apache.catalina.startup.ContextConfig.populateJavaClassCache(ContextConfig.java:2308)
at org.apache.catalina.startup.ContextConfig.populateJavaClassCache(ContextConfig.java:2291)
at org.apache.catalina.startup.ContextConfig.checkHandlesTypes(ContextConfig.java:2197)
at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2154)
at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:2034)
at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1990)
at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1976)
at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1326)
at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:882)
- locked <0x00000007f52f3ec0> (a org.apache.catalina.startup.ContextConfig)
at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:379)
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:5343)
- locked <0x00000007f52f3bb8> (a org.apache.catalina.core.StandardContext)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
- locked <0x00000007f52f3bb8> (a org.apache.catalina.core.StandardContext)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:902)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:879)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:636)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1236)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1877)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
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:745)
Locked ownable synchronizers:
- <0x00000007846dfc70> (a java.util.concurrent.ThreadPoolExecutor$Worker)
"localhost-startStop-1" daemon prio=10 tid=0x00007f6280001000 nid=0x6ba3 runnable [0x00007f62c4950000]
java.lang.Thread.State: RUNNABLE
at java.lang.String.intern(Native Method)
at java.util.jar.Attributes$Name.<init>(Attributes.java:466)
at java.util.jar.Attributes.putValue(Attributes.java:168)
at java.util.jar.Attributes.read(Attributes.java:421)
at java.util.jar.Manifest.read(Manifest.java:251)
at sun.security.util.SignatureFileVerifier.processImpl(SignatureFileVerifier.java:252)
at sun.security.util.SignatureFileVerifier.process(SignatureFileVerifier.java:239)
at java.util.jar.JarVerifier.processEntry(JarVerifier.java:307)
at java.util.jar.JarVerifier.update(JarVerifier.java:218)
at java.util.jar.JarFile.initializeVerifier(JarFile.java:345)
at java.util.jar.JarFile.getInputStream(JarFile.java:412)
- locked <0x00000007e8e9d890> (a sun.net.www.protocol.jar.URLJarFile)
at sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:162)
at java.net.URLClassLoader.getResourceAsStream(URLClassLoader.java:233)
at org.apache.catalina.loader.WebappClassLoader.getResourceAsStream(WebappClassLoader.java:1528)
at org.apache.catalina.startup.ContextConfig.populateJavaClassCache(ContextConfig.java:2302)
at org.apache.catalina.startup.ContextConfig.populateJavaClassCache(ContextConfig.java:2294)
at org.apache.catalina.startup.ContextConfig.populateJavaClassCache(ContextConfig.java:2308)
at org.apache.catalina.startup.ContextConfig.populateJavaClassCache(ContextConfig.java:2294)
at org.apache.catalina.startup.ContextConfig.checkHandlesTypes(ContextConfig.java:2197)
at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:2154)
at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:2034)
at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1990)
at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1976)
at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1326)
at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:882)
- locked <0x00000007858db6e0> (a org.apache.catalina.startup.ContextConfig)
at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:379)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:118)
at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:91)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5346)
- locked <0x00000007858db318> (a org.apache.catalina.core.StandardContext)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:153)
- locked <0x00000007858db318> (a org.apache.catalina.core.StandardContext)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:902)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:879)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:636)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1236)
at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1877)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
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:745)
Locked ownable synchronizers:
- <0x000000078460e618> (a java.util.concurrent.ThreadPoolExecutor$Worker)
WebappClassLoader首先搜索自己的类路径。因此,对于不在本地类路径中的每个类,都将查看所有jar文件。只有这样,它才会查看公共类加载器的委托 假设查看所有war的JAR需要x个时间量,而JAR的父类加载器需要y个时间量 平均来说,从正确的类加载器加载一个类将花费扫描它们所需时间的一半 从WebappClassLoader加载类将花费x/2的时间 从父类加载器加载类需要x+y/2 根据x和y,您可以更改类装入器的使用顺序。 如果y大于x,您应该保持默认设置,如果x大于,您可以尝试反转查找顺序。如果这样做,则从通用类加载器加载类将花费y/2时间,但从WebappClassLoader加载类将花费y+x/2时间 WebAppClassLoader对此设置有一个委托属性,如果将其设置为true,则可以实现此目的。
简而言之,请参见
conf/catalina.properties
shared.loader=shared/*.jar
换言之:
# List of comma-separated paths defining the contents of the "shared"
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
# the "common" loader will be used as Catalina's "shared" loader.
# Examples:
# "foo": Add this folder as a class repository
# "foo/*.jar": Add all the JARs of the specified folder as class
# repositories
# "foo/bar.jar": Add bar.jar as a class repository
# Please note that for single jars, e.g. bar.jar, you need the URL form
# starting with file:.
shared.loader=shared/*.jar
默认情况下,Tomcat6+具有层次结构
与5.5中的默认值进行比较
因此,通过启用共享位置(该位置将只包含您的JAR),并与Tomcat lib文件夹(该文件夹已经有21个JAR)分开,您至少可以获得更清晰的JAR视图。一个选项可能是在每个web应用程序中从扫描中排除一些JAR文件。这将阻止tomcat解析所有JAR,并且只查看与手头任务相关的JAR。@christopher:但是如果情况如上所述,web应用程序中只包含相同的共享库,这应该没什么区别。@oschlueter:你能在启动延迟期间获得一些堆栈跟踪(例如使用jstack)来查看发生了什么事情吗?使用探查器并找出花费时间的原因。在我看来,你应该在这里使用
shared.loader=
property-insidecatalina.properties
。尽管在第7版Tomcat的catalina.properties
中仍然存在此选项,但在提到的HOW-TO
中省略了此选项。关于它,您可以在以前的Tomcat 5.5文档中阅读:
Bootstrap
|
System
|
Common
/ \
Webapp1 Webapp2 ...
Bootstrap
|
System
|
Common
/ \
Catalina Shared
/ \
Webapp1 Webapp2 ...