java - 为什么 Tomcat 在使用共享库时启动速度会慢很多?

标签 java tomcat

问题

我们有一个 Tomcat 7.0.35 运行 25 个 web 应用程序,它们都共享相同版本的相同库(保证这永远不会改变!)。我想不是每个项目都加载同一个库,我可以将它们移动到 ${catalina.home}/lib 从而减少所需的内存负载。

然而这是有代价的:每个 webapp 的部署时间从 5 秒增加到 2 分钟,这是不可行的!

我做了一些阅读(例如 Class Loader HOW-TO ),不幸的是我无法找到减少启动时间的方法。有没有办法解决这个问题?

要求的分析信息

我在两个不同的场景中运行了 jstackjvisualvm,我将在这里展示一些发现(如果您需要更多信息,请告诉我):

  • 每个项目特定的库似乎都加载了: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)
    

  • 项目之间共享的库似乎加载了:
    在 org.apache.catalina.loader.WebappClassLoader.getResourceAsStream 会导致难以置信的部署时间。 JVisualVM 在项目部署期间显示此方法的“自用时间”超过 98%。
    运行方法的线程的 jstack 输出:

    "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 文件。只有这样它才会查看公共(public)类加载器的委托(delegate)。

    假设查看所有 war 的 jar 需要 x 量的时间,对于 jar 它的父类加载器需要 y 量的时间。

    平均而言,从正确的类加载器加载一个类将花费一半的时间来扫描它们。

    从 WebappClassLoader 加载一个类需要 x/2 时间。

    从父类加载器加载一个类需要 x + y/2。

    根据 x 和 y,您可以更改类加载器的使用顺序。 如果 y 大于 x,你应该保持默认设置,如果 x 大于你可以尝试反转查找顺序。如果这样做,从公共(public)类加载器加载一个类将花费 y/2 时间,但从 WebappClassLoader 加载一个类将花费 y + x/2。

    WebbappClassLoader 有一个委托(delegate)属性,将其设置为 true 应该可以解决问题。 见 Common Attributes

    关于java - 为什么 Tomcat 在使用共享库时启动速度会慢很多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23780201/

    相关文章:

    java.lang.NoClassDefFoundError : Could not initialize class com. ibm.mq.jms.MQQueueConnectionFactory 错误

    java - 用于单个应用程序的 REST 和 SOAP Web 服务

    java - 模拟加权随机数 - Java

    java - 如何在 AD 用户帐户中设置 userAccountControl 属性

    java - 以编程方式停止 H2 数据库

    java - 如何使用 log4j 在我的项目目录中写入文件?

    tomcat - Jersey Servlet - 设置根 uri 内容的最佳方式?

    java - Netbeans 的项目属性中缺少包装

    java - 从不同的远程源读取: how to optimize threads?

    java - 使用 Java SimpleDateFormat 查找最大日期