虚拟机类加载机制

虚拟机的类加载机制

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。

类的加载过程:

加载—>验证—>准备—>解析—>初始化

加载:查找并加载类的二进制文件

需要完成3件事:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流
  2. 将这个字节流的金泰存储结构转为方法区的运行时数据结构
  3. 在内存中生成java.lang.Class对象,作为方法区这个类的各种数据的访问入口
验证:确保被加载类的正确性
  • 文件格式验证:是否以魔数0xCAFEBABE开头
  • 元数据验证
  • 字节码验证
  • 符号引用验证
准备:正式为类变量分配内存并设置类变量初始值,变量使用的内存在方法区分配。
public static int value = 123; //在初始化阶段才赋值为123,准备阶段为0
public static final int value = 123; //在准备阶段就被赋值为123
解析:虚拟机将常量池内的符号引用替换为直接引用
初始化:为类的静态变量初始化

静态块中可以给静态变量赋值,但不允许访问,否则会提示“非法向前引用”。

类加载器

双亲委派模型

启动类加载器 <— 扩展类加载器<— 应用程序类加载器<— 自定义类加载器

工作过程:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求的时候,子加载器才会尝试自己去加载。

使用双亲委派模型的好处是:Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar中,无论哪个类加载器加载这个类,最终都是委派给启动类加载器进行加载,因此,Object类在程序的各种类加载器环境中都是同一个类。

先检查是否已经被加载过,若没有加载则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,抛出ClassNotFoundException异常后,再调用最的findClass()方法进行加载。
双亲委派模型的代码在java.lang.ClassLoader.loadClass()中:

protected Class loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

Tomcat的Classloader体系结构

CommonClassLoader <— CatalinaClassLoader
         ^—  SharedClassLoader<— WebAppClassLoader

你可能感兴趣的:(虚拟机类加载机制)