浅谈类加载和双亲委派机制

以下是看了诸葛老师的教程,做一下笔记,方便复习使用

1、类加载过程

首先类加载到使用的过程会涉及到以下几个步骤:

            加载 --->  验证 ---> 准备 ---> 解析---> 初始化  --->  使用 ---> 卸载

加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等

验证:校验字节码文件的正确性

准备:给类的静态变量分配内存,并赋予默认值

解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的 静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用,下节课会讲到动态链接

初始化:对类的静态变量初始化为指定的值,执行静态代码块

2、类加载器

上面的类加载过程主要是通过类加载器来实现的,Java里有如下几种类加载器  

启动类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等

扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中 的JAR类包

应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写 的那些类

自定义加载器:负责加载用户自定义路径下的类包

 

看一个类加载器示例:

public class Demo {
    public  static  void  main(String[] args){
        System.out.println(String.class.getClassLoader());
        System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
        System.out.println(Demo.class.getClassLoader().getClass().getName());
        System.out.println(ClassLoader.getSystemClassLoader().getClass().getName());
    }
}
运行结果:
null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader

3双亲委派机制

1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。

2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。

3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。

说的简单点的话就是 :先找父亲加载,不行再由儿子自己加载

接下来看一下源码及自己添加的注释:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            //name是类名  , 判断当前的类是否被加载
            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();
                   //最终最终还是为空的话,就去自己去执行,即findClass
                    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;
        }
    }

4、为什么要设计双亲委派机制?

沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改

避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性

 

上一篇:一次频繁gc的故障分析


下一篇:查看Java程序运行情况之jconsole远程连接Java应用程序