类加载

转自:

http://www.cnblogs.com/holoyong/p/7479460.html
类加载

java的类加载过程分为加载、验证、准备、解析、初始化等阶段,加载、验证、准备、初始化的开始顺序是一定的,解析则可能会出现在初始化之后、使用过程中。

加载

类加载的第一个阶段就是加载类字节流,通过加载,jvm运行时数据区的方法区内就会产生该类相应的数据结构,并在heap中创建一个class对象来代表该类的数据结构,我们可以通过class对象访问方法区的类数据结构,通过反射对类进行操作。

验证

在加载的同时,就可以开始验证了,所谓验证,就是验证类的字节码是否符合java规范。编译为class文件时其实就包含了验证过程,但由于加载阶段加载的字节流未必是通过合法编译产生的class文件,所以jvm有责任对加载内容进行验证。当然,如果确认加载内容合法,可以选择关闭类加载的验证过程。只有通过了验证的字节序列,才会在方法区创建其对应的数据结构,之后的所有操作,都是针对该数据结构了。

准备

为加载、验证阶段在方法区生成的数据结构的类字段分配空间、赋零值。

例外,对于被赋值字面量static final字段来说,准备阶段就可以确定其真实值了,这也是在初始化之前唯一执行用户逻辑的地方。

解析

解析就是将常量池内的符号引用替换为直接引用,比如运行下面程序最终会打印加载Inner的信息,这是因为类字段inner在解析时会引发Inner的类加载,如果注释掉inner字段,则不会打印。注意,解析的目的只是将符号引用变为直接引用,即将符号引用变为内存中的真实地址(实际是逻辑地址),而一个类的字节序列变为方法区的数据结构只需要经过加载、验证即可,所以,此处的Inner类可能只经过了加载、验证。

复制代码
1 public class Outer{
2     private static Inner inner;
3     private static class Inner{
4     }
5     //-XX:+TraceClassLoading
6     public static void main(String[] args){
7         System.out.println("main");
8     }
9 }
复制代码

解析可能发生在初始化之后,比如将Inner inner变成Object obj = new Inner()。

解析阶段还可能发生在使用时,方法执行时都会将方法内未解析的符号引用转换为直接引用,比如执行下面程序,会打印Inner的加载信息。与前例不同的在于,new Inner()不但会引发加载、验证,还会引发解析、初始化、对象实例化等操作。如果此处只声明了Inner类型变量,而不使用Inner(所谓使用类,比如new、使用类字段、类方法等),则不会触发加载。

复制代码
1 public class Outer{
2     private static class Inner{
3     }
4     //-XX:+TraceClassLoading
5     public static void main(String[] args){
6         Inner outer = new Inner();
7     }
8 }
复制代码

初始化

注意,这里是类的初始化,要与实例的初始化区分,类的初始化就是执行用户对类字段的赋值操作。在类字段声明时赋值,以及在static块中对类字段赋值的逻辑,在java->class的编译后都会按顺序放入类初始化方法中。比如,如果实例化Test对象,输出1,但i最终会被赋值为2。

复制代码
1 public class Test{
2     static{
3         i = 1;
4         System.out.println(i);//1,注意,实例块中禁止向前引用
5     }
6     private static int i = 2;
7 }
复制代码

大致会生成如下的 类初始化方法:

1 //<clinit>方法
2 i = 1;
3 System.out.println(i);
4 i = 2;

类加载器

类加载器执行的类加载逻辑应当符合双亲委派模型,即优先父级类加载尝试加载,父级加载不成功时,子级才去尝试加载。我们自定义类加载器时,应当重写ClassLoader的findClass方法,而不是重写loadClass方法,因为loadClass方法遵守着双亲委派规则。

ClassLoader的loadClass方法:

 View Code

当然也有破坏双亲委派模型的情况出现,比如tomcat7的类加载机制

这里的Bootstrap包括了Ext ClassLoader,common加载的class为webapps共享,webapp加载类时,首先使用SystemClassloader(遵循双亲委派),这是为了避免用户类覆盖java基础类,然后会使用webappN进行加载,如果加载失败再尝试双亲委派加载(即使用父级common加载器加载)。先webappN在common,而common是webappN的父级类加载器,这就破坏了双亲委派模型。

下面简单说一下tomcat加载的jar

对于System ClassLoader来说,tomcat会忽略系统变量CLASS_PATH,System ClassLoader会加载tomcat的bin目录下的相关jar:

common ClassLoader会加载tomcat的lib目录下的jar:

WebappClassLoader重写了loadClass方法:

原文地址:https://www.cnblogs.com/tangxz/p/7750809.html