类加载器初步学习

    近期学习java的classloader(类加载器),有必要记录下,好记性不如烂笔头,这里略去代码测试部分,纯粹记录脑中所想,如有纰漏、错误请大家及时提出,也是帮我提高。

 

系统类加载器:

  当java虚拟机(jvm.dll)启动后,会创建三个类加载器BootStrapClassLoader、ExtClassLoader、AppClassLoader,它们是java虚拟机的一部分,一直逗留在内存中,直到java虚拟机终止才会在内存中消失,它们都有自己的管辖目录范围,在管辖内寻找类(.class文件)来加载。

BootStrapClassLoader:c++编写,在java中表现形式为null。

ExtClassLoader:java编写,源代码在sun.misc.Launcher类中,它是一个内部类。

AppClassLoader:java编写,源代码也在sun.misc.Launcher类中,它也是一个内部类。

简单理解,ExtClassLoader和AppClassLoader被设计成单例模式,在内存中分别只有一个对象。

 

用户创建类加载器:

  用java编写的类加载器都要直接或者间接继承类java.lang.ClassLoader,包括用户(用户指程序员)自己编写的,也包括系统类加载器ExtClassLoader和AppClassLoader。

  •   父子委托模式是在java.lang.ClassLoader中的loadClass方法描述的,ExtClassLoader和AppClassLoader在加载类的时候也会调用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;
        }
    }
  • java.lang.ClassLoader的构造函数可以用来设置加载器的父亲(parent),不带参数的构造函数会自动把AppClassLoader设置为parent,带参数的构造函数可以根据参数设置parent,子类最好覆盖java.lang.ClassLoader的构造函数,下面是java.lang.ClassLoader的构造函数:
    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

    protected ClassLoader(ClassLoader parent) {
        this(checkCreateClassLoader(), parent);
    }

  具体细节查看javadoc或者sourcecode.

 

  • 按javadoc中描述,继承java.lang.ClassLoader后需要覆盖findClass方法,实现具体的加载类的过程,用户编写的类加载器也可以有自己的管辖目录范围,可以在覆盖findClass方法时定义。
  • java.net.URLClassLoader 是系统API提供的可直接使用的类加载器,它继承了java.lang.ClassLoader,也覆盖了构造函数,也覆盖了findClass方法。 ExtClassLoader和AppClassLoader都是继承了java.net.URLClassLoader来扩展自己的功能的。
  • 绕开父子委托模式  我个人简单认为可以把parent设置成null或者直接使用findClass方法,避免使用直接继承类java.lang.ClassLoader的loadClass方法。

类加载器有两种,一种是系统类加载器(在内存中只有一份),另一种是用户自己定义类加载器(在内存中可以有很多份,根据new的次数)。

 

class文件在内存中的形态:

    同一个class文件可以被不同的类加载器对象加载,每个类加载器对象只能对它加载一次(如果用loadClass方法的话)。这样看来class文件在内存中可能会有很多份,每一份都是一个class对象,大家都知道在new的时候就是根据这个class对象为模板创建对象的,当内存里有很多class对象的时候,new的时候会根据系统类加载器(BootStrapClassLoader、ExtClassLoader、AppClassLoader)加载的那个class对象为模板创建对象,效果和其他份class对象的newInstance()一样。class对象newInstance()的时候返回一个Object对象,如果对它向下转型为具体类的时候可能会出现奇怪的现象,见 http://www.iteye.com/problems/77669  在向下转型的时候,对比的是class对象是否相等,不同的类加载器加载的class对象是不同的,是各占一个内存空间的,所以出现连接中的问题。

 

    上面的内容都是根据现象推出来的,还没有足够的证据支持,下一步继续学习jvm规范,希望能找到依据,内部实现机制还是很多不了解。

 

 

你可能感兴趣的:(ClassLoader,Class,类加载器)