java-基础-类加载

类加载

一. ClassLoader

默认提供三个ClassLoader:

启动(Bootstrap)类加载器:引导类装入器是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面的核心类库或-Xbootclasspath选项指定的jar包加载到内存中。c实现,无法引用,作为jvm一部分;

扩展(Extension)类加载器:扩展类加载器是由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量-Djava.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
  
系统(System)类加载器:系统类加载器是由 Sun的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径java -classpath或-Djava.class.path变量所指的目录下的类库加载到内存中。开发者可以直接使用系统类加载器。

自定义
自定义的ClassLoader都必须继承自java.lang.ClassLoader类
Extension和App也是继承自ClassLoader
但是Bootstrap不是继承自ClassLoader,它已经嵌入JVM内核中,底层由C编写
当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoader和App ClassLoader类加载器。

二. 载类的原理

双亲委托模型
每个ClassLoader实例都有一个父类加载器的引用,这父类不是继承关系,是包含关系;
首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
总体顺序如下:
Bootstrap ->Extension ->App ->返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类
如果没找到抛出异常
找到之后将它加载到内存当中,最后返回这个类在内存中的Class实例对象;

好处:避免重复加载,比如String永远都不可能自己加载

JVM如何判定两个类相同
两个方面:

  1. 类名
  2. 由哪个class loader加载

不同类加载器加载的,无法进行类型转换,属于不同的类型

三. loadClass

创建
创建时,第二个参数由getSystemClassLoader()生成
首先检查这个loader是否属于ParallelLoaders,如果在的话,直接使用并行loaders的配置,如果不在,需要自定义一些内容;

    private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
		//检查是否已经注册
        if (ParallelLoaders.isRegistered(this.getClass())) {
            parallelLockMap = new ConcurrentHashMap<>();
            package2certs = new ConcurrentHashMap<>();
            domains =
                Collections.synchronizedSet(new HashSet<ProtectionDomain>());
            assertionLock = new Object();
        } else {
            // no finer-grained lock; lock on the classloader instance
            parallelLockMap = null;
            package2certs = new Hashtable<>();
            domains = new HashSet<>();
            assertionLock = this;
        }
    }

加载
传入类的名字,第二个为是否解析这个类,这里用false,解析是native方法
类可以并行加载,不同的类都有一个锁,所以使用getClassLoadingLock(name)获取到锁
先查看这个类是否已经被加载,如果被加载直接返回;
如果parent非空,调用parent的loadClass方法,这就是双亲委派模型;
一层一层找上去,直到

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 查看是否已经加载了
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
						//执行parent的loadclass方法
                        c = parent.loadClass(name, false);
                    } else {
						//Bootstrap顶层了,加载自己的方法
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    long t1 = System.nanoTime();
                    c = findClass(name);
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

系统类加载器加载
三种ClassLoader都是定义在Launcher中的内部类,Launcher是用来运行main函数的类
构建自定义加载器时,继承了ClassLoader
其panrent由getSystemClassLoader()方法生成,那么它的parent是谁呢?
getSystemClassLoader方法就是调用initSystemClassLoader获取初始化的系统加载器,然后返回

  public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }

系统加载器没有初始化时sclSet为false,执行第一个if
调用Launcher的getLauncher获取Launcher,这个Lancher是全局单例,Lancer的getClassLoader方法获取loader

   private static synchronized void initSystemClassLoader() {
        if (!sclSet) {
            if (scl != null)
                throw new IllegalStateException("recursive invocation");
            sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
            if (l != null) {
                Throwable oops = null;
                scl = l.getClassLoader();
                try {
                    scl = AccessController.doPrivileged(
                        new SystemClassLoaderAction(scl));
                } catch (PrivilegedActionException pae) {
                    oops = pae.getCause();
                    if (oops instanceof InvocationTargetException) {
                        oops = oops.getCause();
                    }
                }
                if (oops != null) {
                    if (oops instanceof Error) {
                        throw (Error) oops;
                    } else {
                        // wrap the exception
                        throw new Error(oops);
                    }
                }
            }
            sclSet = true;
        }
    }

Lancher的这个loader是多少呢?Lancher构造如下

    public Launcher() {
		//外部
        ClassLoader extcl;
        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {
            throw new InternalError(
                "Could not create extension class loader", e);
        }

        // app
        try {
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {
            throw new InternalError(
                "Could not create application class loader", e);
        }

        // 线程的ContextClassLoader
        Thread.currentThread().setContextClassLoader(loader);

        // 如果需要安装到SecurityManager中
        String s = System.getProperty("java.security.manager");
        if (s != null) {
            SecurityManager sm = null;
            if ("".equals(s) || "default".equals(s)) {
                sm = new java.lang.SecurityManager();
            } else {
                try {
                    sm = (SecurityManager)loader.loadClass(s).newInstance();
                } catch (IllegalAccessException e) {
                } catch (InstantiationException e) {
                } catch (ClassNotFoundException e) {
                } catch (ClassCastException e) {
                }
            }
            if (sm != null) {
                System.setSecurityManager(sm);
            } else {
                throw new InternalError(
                    "Could not create SecurityManager: " + s);
            }
        }
    }

可看出loader是AppClassLoader

ExtClassLoader
Launcher中首先创建了ExtClassLoader
ExtClassLoader的getExtClassLoader如下

     public static ExtClassLoader getExtClassLoader() throws IOException
        {
            final File[] dirs = getExtDirs();
            try {
                return AccessController.doPrivileged(
                    new PrivilegedExceptionAction<ExtClassLoader>() {
                        public ExtClassLoader run() throws IOException {
                            int len = dirs.length;
                            for (int i = 0; i < len; i++) {
                                MetaIndex.registerDirectory(dirs[i]);
                            }
                            return new ExtClassLoader(dirs);
                        }
                    });
            } catch (java.security.PrivilegedActionException e) {
                throw (IOException) e.getException();
            }
        

ExtClassLoader最后初始化的父类构造
第三个传入的是URLStreamHandlerFactory
parent传入的是null

   public URLClassLoader(URL[] urls, ClassLoader parent,
                          URLStreamHandlerFactory factory) {
        super(parent);
        // this is to make the stack depth consistent with 1.1
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        ucp = new URLClassPath(urls, factory);
        acc = AccessController.getContext();
    }

父类SecureClassLoader构造时Parent也为空,再父类ClassLoader的parent也为空?
ExtClassLoader并没有找到父类!

再写一次loadClass,ExtClassLoader没有parent,但是在loadClass中会首先找到Bootstrap

	//这个调用本地方法,尝试通过Bootstrap加载
	c = findBootstrapClassOrNull(name);
	if (c == null) {
	    long t1 = System.nanoTime();
	    c = findClass(name);
	    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
	    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
	    sun.misc.PerfCounter.getFindClasses().increment();
	}

findClass有区别
APP加载器的父类URLClassLoader的如下

    protected Class<?> findClass(final String name)
         throws ClassNotFoundException {
        try {
            return AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            throw new ClassNotFoundException(name);
                        }
                    }
                }, acc);
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
    

这里的ucp是构建时生成的

   URLClassLoader(URL[] urls, ClassLoader parent,
                   AccessControlContext acc) {
        super(parent);
        // this is to make the stack depth consistent with 1.1
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkCreateClassLoader();
        }
        ucp = new URLClassPath(urls);
        this.acc = acc;
    }

这个urls是在创建App类加载器时获取的

	...
   return AccessController.doPrivileged(
            new PrivilegedAction<AppClassLoader>() {
                public AppClassLoader run() {
                URL[] urls =
                    (s == null) ? new URL[0] : pathToURLs(path);
                return new AppClassLoader(urls, extcl);
            }
        });

三. 测试观察

前两种方法加载

	public class Main {
	    public static void main(String[] args) {
	        ClassLoader loader = Main.class.getClassLoader();
	        while(loader != null) {
	            System.out.println(loader);
	            loader = loader.getParent();    //获得父类加载器的引用  
	        }
	        System.out.println(loader);
	    }
	}  

输出:

sun.misc.Launcher$AppClassLoader@75b84c92
sun.misc.Launcher$ExtClassLoader@1b6d3586
null

把Main.class打包,放在JAVA_HOME/jre/lib/ext目录下,重新运行

Bootstrcp ClassLoader加载
1、在jvm中添加-Xbootclasspath参数,指定Bootstrcp ClassLoader加载类的路径,并追加我们自已的jar(ClassTestLoader.jar)
2、将class文件放到JAVA_HOME/jre/classes/目录下(上面有提到)

四. 动态扩展方式

自定义ClassLoader
因为Java中提供的默认ClassLoader,只加载指定目录下的jar和class,如果我们想加载其它位置的类或jar时
比如:我要加载网络上的一个class文件,通过动态加载到内存之后,要调用这个类中的方法实现我的业务逻辑。

定义自已的类加载器分为两步:

  1. 继承java.lang.ClassLoader
  2. 重写父类的findClass方法

先获取.class的二进制码,存在数据组中

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = null;
        byte[] classData = getClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        }
        clazz = defineClass(name, classData, 0, classData.length);
        return clazz;
    }

调用defineClass把它构建成一个Class

    protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }

测试代码

    MyLoader myLoader = new MyLoader();
    Class clazz = myLoader.loadClass("ClassTest");
    System.out.println(clazz.getClassLoader());

forName


五. forName过程

jdbc中,加载驱动,使用当前类的类加载器加载
Class.forName(DRIVER_NAME);
代码如下:

   public static Class<?> forName(String className)
                throws ClassNotFoundException {
		//先获取哪个类调用了这个方法
        Class<?> caller = Reflection.getCallerClass();
		//获取这caller类的类加载器
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }
posted @ 2016-09-29 17:50  zhangshihai1232  阅读(133)  评论(0)    收藏  举报