类加载机制的第一步执行的是类的加载过程,完整的类加载机制包括加载、连接和初始化,其中连接又可以分为验证、准备和解析三个小过程,除了解析过程支持动态绑定发生的时间可能在初始化之后,其他过程按照顺序发生(时间上可重叠)。
类的加载是指将表示类信息的二进制字节流转化为虚拟机中以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