如果它们位于两个 Web 应用程序中,Tomcat 是不是会将相同的库文件加载到内存中两次?

Posted

技术标签:

【中文标题】如果它们位于两个 Web 应用程序中,Tomcat 是不是会将相同的库文件加载到内存中两次?【英文标题】:Does Tomcat load the same library file into memory twice if they are in two web apps?如果它们位于两个 Web 应用程序中,Tomcat 是否会将相同的库文件加载到内存中两次? 【发布时间】:2010-09-21 01:02:48 【问题描述】:

我在tomcat/webapps 文件夹下有两个应用程序。

tomcat/webapps/App1
tomcat/webapps/App2

两个应用程序共享相同的库。例如存储在tomcat/webapps/App1/WEB-INF/lib中。

两个库是否在内存中加载了两次?

我应该把这些共享库放在tomcat/server/lib吗?

【问题讨论】:

【参考方案1】:

如您所见here,Tomcat 在您的服务器上为每个 webapp 创建一个类加载器。 因此,如果你有 webapp1 和 webapp2 共享同一个库,那么这个库确实会被加载两次。

如果该库由运行在您的 Tomcat 服务器上的所有个 Web 应用程序共享,您最终可以将该库放置在公共目录 (tomcat-dir/common/lib) 中。

【讨论】:

我在服务器内部没有公共目录,我只有类和 lib 目录。我应该在里面创建一个共同点吗? 我编辑了我的帖子,表明要使用的好目录是 [tomcat-installation-directory]/common/lib 显然可以 [并推荐] 将 jdbc 驱动程序部署到共享 FWIW ***.com/questions/6981564/… 更新: 在较新版本的 Tomcat 中,默认情况下没有捆绑“common”文件夹。 “Common”类加载器现在在catalina.properties 文件中配置为使用Tomcat 的“base”或“home”文件夹中名为lib 的文件夹。此外,正如您在catalina.properties 中看到的,“服务器”类加载器和“共享”类加载器都使用相同的“base”或“home”中的lib 文件夹。如果您想要单独的文件夹,请创建文件夹,然后通过编辑 catalina.properties 将 Tomcat 指向该方向。【参考方案2】:

我不建议将 jar 文件放在共享文件夹中。例如,假设您将来需要部署第三方应用程序,该应用程序在 WEB-INF 文件夹中有更新版本的 jar 文件。对于这个应用程序,jar 的类将被加载两次(即使它们具有相同的名称),一个来自共享文件夹,另一个来自 web 应用程序文件夹。这种情况可能会导致 bug 很难找到。

如果 jar 文件位于 web 应用程序文件夹中,则它们由单独的类加载器加载,并且不会相互干扰。

【讨论】:

我非常同意。将库放在 commons 目录中可能很危险,只有在您可以控制部署哪些 webapps 以及每个 webapp 使用的库版本是什么时才必须使用... 你能详细说明一下被加载两次吗?共享的会“覆盖” WEB-INF 文件夹中的那些吗? 最好将这种情况视为“未定义”。您将拥有同一个类的两个实例,每个实例由不同的类加载器加载。这通常意味着问题。 @rogerdpack:有一些关于如何工作的规则,但它们很复杂,最重要的是取决于类的加载方式(这又取决于 Tomcat 配置和 Tomcat 版本)。因此,正如 kgiannakakis 指出的那样,实际上最好将其视为“未定义”。 谁能告诉我内存消耗情况,因为如果你为每个文件夹加载 jars,它会占用大量额外的内存。任何人都可以分享他们对此的想法【参考方案3】:

根据经验:这两个网络应用程序完全相互隔离——一个应用程序的库没有在另一个应用程序中使用——因此回答你最初的问题——是的,它们会被加载两次。

回答第二个问题,是否应该将这些库部署到 Tomcat 的共享目录中 - 我会说不,原因如下:

如果您将库 Jar 部署到共享位置 (tomcat/server/lib),则该库版本将成为在该 Tomcat 实例下运行的所有 Web 应用程序的默认版本。正如您在this overview of the tomcat architecture 中看到的那样,类加载器“沿着链”工作,单个 Web 应用程序的 lib 文件夹是它在引发未找到类的异常之前查看的最后一个位置。这在 Tomcat 6 和 Tomcat 7 中并非如此:Web 应用程序库和类文件夹中的任何类 将在公共类之前解析,因此,这不会破坏部署所有它们的其他应用程序战争中的罐子2。

因此,将共享库部署到该目录的问题在于,它破坏了各个应用程序相互隔离的体系结构。在您的初始示例中很好,但是如果您想要部署第三方应用程序(例如,如果您正在运行使用 Portlet 来处理特定内容的应用程序),您会立即遇到版本依赖性问题 - 您的库的共享版本可能对于第三方应用程序不正确,但是由于包已经加载,您将抛出异常左右和中心。

【讨论】:

我认为它“向上”工作?该链接还指向某种付费文档? 您所引用的文档已被删除。您能否更新或删除链接(Tomcat 架构概述)? @steve-ash 以及 Tomcat 8 和 Tomcat 9 中的解析顺序如何?和Tomcat 7一样吗?【参考方案4】:

我们正在使用 tomcat6,并找到了一种让 tomcat 填充我们所有 web 应用所需的通用库的好方法。

conf/catalina.properties 中编辑条目 common.loader。例如。附加一个额外的文件夹,其中包含您想要共享的 jar 'mylibs'

common.loader=$catalina.base/lib,$catalina.base/lib/*.jar,
              $catalina.home/lib,$catalina.home/lib/*.jar,
              catalina.home/mylibs/*.jar

然后把所有的公共库放在那里。完成。

为什么我们开始在所有 web 应用程序(WAR 文件)中使用 mylibs 文件夹而不是 WEB-INF/lib?

在 WAR 超过 50MB 线后,部署开始成为一场噩梦!

当有一个没有 jar 版本的 webapp 时,你仍然可以将它放入 WEB-INF/lib 以覆盖你在 mylibs 中的内容。

【讨论】:

我同意战后大尺寸是噩梦【参考方案5】:

如果您不希望您的库加载两次,请将它们放入:

雄猫 6:$CATALINA_HOME/lib 雄猫 5:$CATALINA_HOME/common/lib

(从问题中删除并复制到此处,以便对其进行投票/评论)

【讨论】:

您也可以自定义catalina.properties,将自己的自定义目录添加到通用类加载器中。这有助于将 Tomcat jar 与应用程序所需的 jar 分开。 我不建议这样做 - 您不应该将自定义文件与库存文件混合在一起,因为当将来有新人出现时,您将不清楚哪些是您的。【参考方案6】:

堆的PermGen空间用于存储Java中的类和关于类的元数据。

错误 java.lang.OutOfMemoryError: PermGen space 可能经常发生,因为我们在 apache tomcat 中加载了大量重复库 谁能详细分享一下

【讨论】:

以上是关于如果它们位于两个 Web 应用程序中,Tomcat 是不是会将相同的库文件加载到内存中两次?的主要内容,如果未能解决你的问题,请参考以下文章

Tomcat 第六篇:类加载机制

如果它们位于不同的文件夹中,是不是可以有两个具有相同名称的类?

如何在 Tomcat 中为 Java EE 应用程序实现套接字

eclipse 中 tomcat 如何启动两个web项目(配置server,或者tomcat?)

启动Tomcat

Tomcat 部署Web应用