定义

所有的代码,都是用来描述数据结构和算法逻辑的
运行代码时,需要把陈述性的逻辑转换成物理性的数据结构,然后照着逻辑描述的算法去操作这些数据结构。

java程序跑起来,需要哪几步?

  1. 获取描述性的文本
  2. 按照特定语法,把文本转成物理结构,即一堆二进制。
  3. 按照特定语法,遵循着文本描述的逻辑去操作这些数据结构,如果在此时又遇到了其它描述性文本的名字(类名),也要走第1步把它获取到。

发生在第1、3步的获取动作,就是类的加载

类加载的机制有以下描述:

  1. 不同的类加载器,可以从不同的地方加载类文本,各司其职
  2. A类中用到了B类,B类由A的类加载器加载
  3. 所有的类,优先由顶层类加载器自顶向下加载
  4. Thread中可以自行设置类加载器供程序获取(默认是系统类加载器)

通过以上描述,可以得知以下几个特性:

  1. 在程序进行到某个节点时,能加载哪些类,取决于当前可以取得哪些classLoader,1. 加载类的caller的classloader;2. Thread中设置的classLoader
  2. 就算当前能获得预期的ClassLoader,还要父辈 类加载器 找不到类,它们才能派上用场

案例

一、tomcat

一个Tomcat容器,即为一个JVM容器。

下面可以有多个Web应用(可理解为一个war包为一个应用),相当于多个web应用可以同时跑在一个tomcat容器下

不同的web应用会使用到不同版本的依赖包。都会加载到同一个jvm容器中。可以理解为:同一个类名存在 v1.0、v2.0 … 等多个版本,都被加载到同一个jvm的元数据区。

如何保证多个相同全限定名的类互相不冲突?

每个web应用下的所有类(web-inf/classes,web-info/lib),都由一个新的WebAppClassLoader来加载。这样,它们新引入的类,也都会由这个loader来加载。各个web应用的loader,都只能加载各自范围内的类,这样就能保证各个web应用的类相互隔离。

如何保证Jsp能热加载

jsp本质也是一个描述逻辑的文本,也就是class,所以也需要被类加载器加载。当jsp被修改,为了不让程序去元数据区取已经加载过的类,要给这个类指定一个新的类加载器,来重新加载类文本。每个jsp类都由一个属于自己的类加载器。

二、spring

沿用上面描述的场景,一个tomcat容器下有多个web应用。但是spring框架包放在公共类库中,多个web应用都可以通过同一个祖先类加载器共享spring类。此时再通过spring类来创建各个web应用的bean,是不可能的,因为祖先类加载器无法加载不在其负责范围的类。

spring通过把每个web应用各自的WebAppClassLoader设置到线程中,在spring容器加载类时,直接用线程中的loader来加载。

此时,在一个JVM容器的元数据区中,spring类是独一份的,而各个web应用的类由它们自己的loader加载。spring获取线程loader的核心代码在 DefaultResourceLoader 中

三、arthas

使用arthas的ognl命令,可以实时的调用jvm容器里的类的方法,或者获取类的属性。然而在实践中,通常会出现ClassNotFound异常。原因在于arthas使用的类加载器独立于系统类加载器,直接使用ognl命令会使用arthas的类加载器来加载类,导致找不到类。通过加上参数,指定使用具体的web应用类加载器,即可正常调用类方法了。