24

由于 Tomcat 可以一次加载多个 webapp,并且这些 webapp 可以单独工作,互不干扰,并且它们工作在同一个 JVM 中。所以我对tomcat如何处理同一个JVM中的对象范围感到很困惑。

例如,我在两个不同的 Web 应用程序中都有一个单例对象,而 tomcat 将为每个生成两个不同的单例对象。我一直认为单例对象在同一个JVM中只有一个对象,但在tomcat JVM中可能有两个或更多。


我已经阅读了一些关于 ClassLoader 的信息,Tomcat 有自己的 WebAppClassLoader 来加载 webapps。那么这是否意味着这里的对象范围是 ClassLoader 还是我错了。有谁知道这个或者可以给我一些关于tomcat工作内存布局的信息?

4

7 回答 7

39

所有的秘密都在这些ClassLoader实例的背后。

类的状态(如所有静态变量、字节码等)由加载该类的类加载器确定范围(一个类在 JVM 中由其完全限定的名称和加载该类的类加载器标识。这不是完全是一个范围,但作为范围思考通常有助于更好地理解这一点)。

所以如果一个类被两个不同的类加载器加载,这个类在虚拟机中存在两次,它有两组静态字段,可以有不同的字节码(比如不同的方法实现)等等。请注意,即使它们的名称相同,这两个对象也不能相互转换。“普通”Java 应用程序的所有类都由类加载器层次结构加载,并且每个类只加载一次。

对于更复杂的场景,您将需要不同的行为。有时您希望将库与您的代码隔离开来(例如 Eclipse 中的插件或应用程序服务器中的 Web 应用程序)。

将您的程序与其他类隔离的基本思想是使用额外的类加载器加载那些并使用大量反射。如果您想阅读此内容,请查看 Oracle 关于ClassLoadersOSGI的文档。

Tomcat(以及许多其他 Web 容器/应用程序服务器)使用单独的 ClassLoader 层次结构加载应用程序。这将所有类与其他(Web)应用程序隔离开来,从而确保单例、不同的类版本和所有这些东西不会发生冲突。

于 2013-10-24T07:21:33.710 回答
14

请记住,Java 中的类由其完全限定名称 加载它的类加载器标识。Tomcat 为您部署的每个上下文(Web 应用程序)使用单独的类加载器,从而使它们保持独立。此外,系统类加载器会加载 tomcat 特定的库,而 JVM 引导加载器会加载 Java 核心库。

于 2013-10-24T07:20:39.167 回答
7

谈论单例时总是被忽略的一件事是,单例每个类加载器只能有一个实例。AClassLoader限制了类的可见性,因此同一个类可以存在于同一个 VM 中的几个不同的类加载器下。除其他外,这允许您同时加载不同版本的 jar。

这个问题:Java Class Loaders似乎有一些不错的链接和资源可供进一步研究。

于 2013-10-24T07:18:57.133 回答
7

在普通 Java 应用程序中,当要求类加载器加载一个类时,它首先将请求委托给它的父类加载器,然后在父类加载器找不到请求的类时加载它。

对于 Web 应用程序服务器,这略有不同。对于部署在像 tomcat 这样的 Web 应用程序服务器中的每个 Web 应用程序,通常有不同的类加载器。对于 Tomcat,它如下所示 -

在此处输入图像描述

因此,对于 Web 应用程序类加载资源按以下顺序发生 -

  1. JVM 的引导类(核心 java 类)
  2. /WEB-INF/Web 应用程序的类
  3. /WEB-INF/lib/*.jar 您的 Web 应用程序
  4. 系统类加载器类(Tomcat / Classpath 特定类)
  5. 通用类加载器类(所有 Web 应用程序通用的类)

但请注意,如果配置了 Web 应用程序类加载器,delegate="true"则顺序会更改 -

  1. JVM 的引导类(核心 java 类)
  2. 系统类加载器类(Tomcat / Classpath 特定类)
  3. 通用类加载器类(所有 Web 应用程序通用的类)
  4. /WEB-INF/Web 应用程序的类
  5. /WEB-INF/lib/*.jar 您的 Web 应用程序

有关更多详细信息,您可以查看 Apache Tomcat 的Class Loader HOW-TO页面。

于 2015-02-22T16:05:12.877 回答
2

JVM 中类的“ID”由完全限定的类名和用于加载它的类加载器组成。这意味着,如果您通过不同的类加载器加载两个具有相同名称的类,它们将被视为不同的类。

于 2013-10-24T07:19:13.507 回答
0

因此,对于类加载器来说,单例就是单例——在容器/JVM 中;作为容器/JVM 可能有多个类加载器。

于 2013-10-24T07:40:17.630 回答
0
  1. tomcat中的不同应用程序使用不同的类加载器来分隔。例如 app1 使用 ClassLoaderA,app2 使用 classloaderB。
  2. 每个类都将使用自己的类加载器来加载其他类。因此,如果 ClassA.class 引用 ClassB.class 那么 ClassB 需要位于 ClassA 的类加载器或其父级的类路径上。例如在 app1 中,com.exmaple.test1 从 ClassLoaderA 加载。而 com.exmaple.test1 想要新的 com.exmaple.test2()。默认情况下,它使用自己的类加载器 ClassLoaderA 来加载 com.exmaple.test2。所以在 com.exmaple.test1 的视图中它只能看到它自己的类路径的类(app1/webapp/classes 或 app1/webapp/lib)。而在 app2 中,它会看到不同的视图。
  3. 总之学习类加载器你必须了解委托模型。可见性是孩子可以看到父母。但是父母看不到孩子,兄弟姐妹看不到兄弟姐妹。所以我们可以隔离不同的应用程序。
于 2017-04-03T13:52:50.693 回答