Подтвердить что ты не робот

Почему Tomcat начинает работать медленнее с разделяемыми библиотеками?

Проблема

у нас есть Tomcat 7.0.35, работающий с 25 веб-серверами, которые используют одни и те же библиотеки с одинаковыми версиями (гарантировано, что это никогда не изменится!). Я решил, что вместо каждого проекта, загружающего одну и ту же библиотеку, я мог бы просто переместить их на ${catalina.home}/lib, что приведет к желаемому уменьшению загрузки памяти.

Однако это происходит по цене: время для каждого развертываемого webapp увеличивается с 5 секунд до 2 минут, что не является жизнеспособным!

Я прочитал (например, класс Loader HOW-TO), к сожалению, мне не удалось найти способ уменьшить время запуска. Есть ли способ решить эту проблему?

Запрошенная информация профилирования

Я запускал 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)

Библиотеки, которые совместно используются между проектами, загружаются: at org.apache.catalina.loader.WebappClassLoader.getResourceAsStream, что вызывает невероятное время развертывания. JVisualVM отображает 98% + "Self time" для этого метода во время развертывания проекта.
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)
4b9b3361

Ответ 1

WebappClassLoader сначала ищет свой путь к классам. Таким образом, для каждого класса не в локальном пути к классам будут рассмотрены все файлы jar. Только тогда он будет рассматривать делегата для обычного загрузчика классов.

Скажите, что, глядя на все боевые банки, требуется х количество времени, поскольку для банок его родительский загрузчик классов требуется y времени.

В среднем загрузка класса из правильного загрузчика классов займет половину времени, затрачиваемого на их сканирование.

Загрузка класса из WebappClassLoader займет x/2 раза.

Загрузка класса из родительского загрузчика классов займет x + y/2.

В зависимости от x и y вы можете изменить порядок использования загрузчиков классов. Если y больше x, вы должны сохранить настройку по умолчанию, если x больше, чем вы можете попытаться отменить порядок поиска. Если вы это сделаете, то загрузка класса из обычного загрузчика классов займет y/2 раз, но класс из WebappClassLoader примет y + x/2.

У WebbappClassLoader есть свойство delegate для этого параметра, поэтому true должен делать трюк. См. Общие атрибуты

Ответ 2

Вкратце: добавьте значение для shared.loader в 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 "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

Tomcat 6+ по умолчанию имеет иерархию http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html

  Bootstrap
      |
   System
      |
   Common
   /     \
Webapp1   Webapp2 ...

сравнить с по умолчанию в 5.5 http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html

 Bootstrap
      |
   System
      |
   Common
  /      \
Catalina Shared
         /   \
    Webapp1  Webapp2 ... 

Таким образом, включив общее местоположение, которое будет содержать только ваши банки, и отдельно от папки Tomcat lib, у которой уже есть 21 баночка, вы можете получить хотя бы более чистый обзор банок.