写在前面

本文主要介绍了四种类加载器,以及算清委托机制。文章可能还有很多不足,请大家谅解,欢迎大佬提意见。

本文使用到的东西

  1. java

1.类加载器

1.1 类加载器作用:
类加载器负责从文件或者网络中加载Class信息,加载的类信息存放于方法区的内存空间。

1.2 启动类加载器(BootStrap ClassLoader):

  1. C++实现,是虚拟机的一部分;
  2. 负责加载“%JAVA_HOME%\lib”目录中,或者-Xbootclasspath参数指定路径中的类库到虚拟机;
  3. 无法被java程序直接引用。

1.3 扩展类加载器(Extension ClassLoader):
独立于JVM,由sun.misc.Launcher$ExtClassLoader实现,加载”%JAVA_HOME%\lib\ext”目录中,或者java.ext.dirs系统变量指定的路径中的所有类库,该类加载器无法被java程序直接引用。

1.4 应用程序类加载器(Application ClassLoader):
独立于JVM,由sun.misc.Launcher$AppClassLoader实现,也叫系统类加载器。负责加载用户类路径(classpath)上指定的类库,可以直接使用,一般情况下是默认的类加载器。

1.5 启动类加载器(BootStrap ClassLoader):
独立于JVM,需要继承于java.lang.ClassLoader抽象类,复写Class<?> findClass(String name)方法。读取类文件的二进制数据流,通过Class<?> defineClass方法加载类。

2.双亲委托机制

在加载类时必须先交给父加载器加载,父加载器不存在则交给启动类加载器加载,还没加载到再自己加载;目的是为了避免类的重复加载。

已知java中类加载器都继承于java.lang.ClassLoader抽象类,通过loadClass(String name)方法加载类:

loadClass(String name):

//加载类的方法
public Class<?> loadClass(String name) throws ClassNotFoundException {
	//调用了loadClass(name, resolve)方法,false表示不解析类
    return loadClass(name, false);
}

loadClass(name, resolve):

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 检查类是否已经加载
            Class<?> c = findLoadedClass(name);
            // 未加载情况
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                	/**
                	* 双亲委托
                	* 判断父类加载器是否存在,存在则交给父类加载器加载
                	* 不存在父加载器则交给启动类加载器(BootStrap ClassLoader)加载
                	*/
                    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) {
                    long t1 = System.nanoTime();
                    // 如果还没有加载到类,则交给findClass(name)来加载
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
            	// 解析类
                resolveClass(c);
            }
            return c;
        }
    }
    // 自定义类时交给我们来复写
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

通过阅读源码我们可以知道,我们只需要继承ClassLoader抽象类,复写findClass(String name)即可实现自定义类加载器。而复写loadClass(String name, boolean resolve)方法则可以破坏双亲委托机制。

3.总结

本文简单描述了JVM的类加载器以及双亲委托,从ClassLoader源码层面分析了类加载机制。有不清楚的地方欢迎评论留言,看到的我都会回复的。本文到此结束,有什么不足的地方请大家不吝指正。