类加载机制_双亲委派模型

时间:2022-12-29 09:33:11

类加载机制的第一步执行的是类的加载过程,完整的类加载机制包括加载、连接和初始化,其中连接又可以分为验证、准备和解析三个小过程,除了解析过程支持动态绑定发生的时间可能在初始化之后,其他过程按照顺序发生(时间上可重叠)。

类的加载是指将表示类信息的二进制字节流转化为虚拟机中以Class对象为代表的数据结构。

虚拟机中采用双亲委派模型来表现类的加载结构,JVM中预定义的有三种类型的类加载器:

1.启动类加载器(Bootstrap ClassLoader)。由C++代码编写的虚拟机自带的类加载器,负责%JAVA_HOME%/lib目录中或-Xbootclasspath中参数指定的路径中的类

2.扩展类加载器(Extension ClassLoader)。由Sun实现的ExtClassLoader(sun.misc.Launcher$ExtClassLoader),负责%JAVA_HOME%/lib/ext或java.ext.dir目录下的类

3.应用程序类加载器(Application ClassLoader)。由Sun实现的AppClassLoader(sun.misc.Launcher$AppClassLoader),负责%CLASSPATH%中的类,也是程序中默认的类加载器

双亲委派模型是指,除了启动类加载器之外,每个类加载器都有自己的父类加载器,通过组合的方式来复用父类加载器。当一个类加载器收到加载某个类请求的时候,首先检查自己是否已经加载过该类,如果没有则将请求传给父类加载器,直到启动类加载器,父类加载器不能加载再执行自己的类加载过程。

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) {//并且父类不为null,即当前类加载器不为启动类加载器
                        c = parent.loadClass(name, false);//则调用父类加载器的loadClass方法
                    } 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);//则使用自身的findClass加载该类

                    // 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;
        }
    }
因为虚拟机中的类的唯一性,是根据类和加载类的类加载器一起确定的,所以使用带有层级结构的双亲委派模型,可以保证一定的安全性,例如对于Object类,无论执行加载时选择的哪个类加载器,最后都会由启动类加载器来完成加载,从而可以保证了类的一致性,并且使用相同的类加载器加载同一个类,可以防止生成多个不同的类对象,避免多次加载。

关于类加载器的示例:

public static Unsafe getUnsafe() {
	/*
	Unsafe类作为单例模式的一种使用方式,其构造方法为私有的,并且不能通过getUnsafe()方法
	返回实例对象,因为getUnsafe被设计只能从启动类加载器加载
	*/
	//得到调用该方法的Class对象
	Class cc = Reflection.getCallerClass();
	//判断调用该方法的类是否是引导类加载器(bootstrap class loader)
	//如果不是的话,比如由AppClassLoader调用该方法,则抛出SecurityException异常
	if (cc.getClassLoader() != null)
		throw new SecurityException("Unsafe");
	//返回单例对象
	return theUnsafe;
}

参考:

《深入理解java虚拟机》

http://www.cnblogs.com/chenpi/p/5389254.html