定义
所有的代码,都是用来描述数据结构和算法逻辑的
运行代码时,需要把陈述性的逻辑转换成物理性的数据结构,然后照着逻辑描述的算法去操作这些数据结构。
java程序跑起来,需要哪几步?
- 获取描述性的文本
- 按照特定语法,把文本转成物理结构,即一堆二进制。
- 按照特定语法,遵循着文本描述的逻辑去操作这些数据结构,如果在此时又遇到了其它描述性文本的名字(类名),也要走第1步把它获取到。
发生在第1、3步的获取动作,就是类的加载
类加载的机制有以下描述:
- 不同的类加载器,可以从不同的地方加载类文本,各司其职
- A类中用到了B类,B类由A的类加载器加载
- 所有的类,优先由顶层类加载器自顶向下加载
- Thread中可以自行设置类加载器供程序获取(默认是系统类加载器)
通过以上描述,可以得知以下几个特性:
- 在程序进行到某个节点时,能加载哪些类,取决于当前可以取得哪些classLoader,1. 加载类的caller的classloader;2. Thread中设置的classLoader
- 就算当前能获得预期的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应用类加载器,即可正常调用类方法了。