Java类加载器ClassLoader的理解

作者: lunabird | 来源:发表于2018-08-05 16:11 被阅读18次

    关于类装载这块目前的理解:

    image.png

    一个java类的生命周期包括:
    加载--》连接(验证,准备,解析)--》初始化--》使用--》卸载.

    其中从加载开始到堆中新建了一个对象的过程如下:首先从方法区拿到类的class文件,通过classLoader将类对象加载到虚拟机,通过初始化过程执行类的构造器<clinit>,为类的静态变量赋予正确的初始值,执行类的构造方法,实例化对象。

    在初始化过程中,静态类变量和静态语句块执行的先后顺序依赖于他们出现在代码中的先后顺序。

    类加载的过程使用了双亲委派模型,这样可以避免重复加载。
    JDK已有的类加载器:

    • BootStrapClassLoader JVM启动的类加载器(C++),它主要加载jre的lib下面的jar包,例如rt.jar.
    • ExtensionClassLoader extends ClassLoader,加载%JAVA_HOME%/lib/ext/*.jar。
    • AppClassLoader extends ClassLaoder,加载当前程序classpath下面的java类。
    • 自定义的类加载器 extends ClassLoader,完全自定义家在路径。

    以下是jdk的双亲委派模型加载类的具体实现源码:

    /**
         * Loads the class with the specified <a href="#name">binary name</a>.  The
         * default implementation of this method searches for classes in the
         * following order:
         *
         * <ol>
         *
         *   <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
         *   has already been loaded.  </p></li>
         *
         *   <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
         *   on the parent class loader.  If the parent is <tt>null</tt> the class
         *   loader built-in to the virtual machine is used, instead.  </p></li>
         *
         *   <li><p> Invoke the {@link #findClass(String)} method to find the
         *   class.  </p></li>
         *
         * </ol>
         *
         * <p> If the class was found using the above steps, and the
         * <tt>resolve</tt> flag is true, this method will then invoke the {@link
         * #resolveClass(Class)} method on the resulting <tt>Class</tt> object.
         *
         * <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
         * #findClass(String)}, rather than this method.  </p>
         *
         * <p> Unless overridden, this method synchronizes on the result of
         * {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
         * during the entire class loading process.
         *
         * @param  name
         *         The <a href="#name">binary name</a> of the class
         *
         * @param  resolve
         *         If <tt>true</tt> then resolve the class
         *
         * @return  The resulting <tt>Class</tt> object
         *
         * @throws  ClassNotFoundException
         *          If the class could not be found
         */
        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;
            }
        }
    

    画成流程图是这样:

    classloader.png

    下面用demo来看一下:

    package com.hp.demo.classLoader;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    /**
     * <pre>
     * 任务:
     * 描述:自定义classLoader,从文件系统中加载类
     * 作者:@author huangpeng
     * 时间:@create 2018-08-05 上午9:10
     * 类名: MyClassLoader
     * </pre>
     **/
    
    public class MyClassLoader extends ClassLoader {
    
        private String path; //加载的类的路径
        private String name; //类加载器名称
    
        public MyClassLoader(String name,String path) {
            super();//让系统类加载器成为该类的父加载器
            this.name = name;
            this.path = path;
        }
    
        public MyClassLoader(ClassLoader parent,String name,String path) {
            super(parent);//显示指定父类加载器
            this.name = name;
            this.path = path;
        }
    
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException{
            byte[] data = readClassFileToByteArray(name);
            return this.defineClass(name,data,0,data.length);
        }
    
        @Override
        public String toString() {
            return this.name;
        }
    
        /**
         * 获取class文件的字节数组
         * @param name
         * @return
         */
        private byte[] readClassFileToByteArray(String name) {
            InputStream is = null;
            byte[] returnData = null;
            name = name.replaceAll("\\.", File.separator);
            String filePath = this.path + name +".class";
            File file = new File(filePath);
    
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            try{
                is = new FileInputStream(file);
                int tmp = 0;
                while((tmp = is.read())!=-1){
                    os.write(tmp);
                }
                returnData = os.toByteArray();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try{
                    is.close();
                    os.close();
                }catch (Exception e1){
    
                }
            }
            return returnData;
        }
    }
    
    
    package com.hp.demo.classLoader;
    
    /**
     * <pre>
     * 任务:
     * 描述:测试我的自定义ClassLoader
     * 作者:@author huangpeng
     * 时间:@create 2018-08-05 上午9:29
     * 类名: TestDemo
     * </pre>
     **/
    
    public class TestDemo {
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            MyClassLoader huangpengLoader = new MyClassLoader("huangpeng","/Users/huangpeng/workspace/temp/");
            // null代表 BootStrap ClassLoader
            MyClassLoader loader = new MyClassLoader(null,"lunabird","/Users/huangpeng/workspace/temp/");
            Class<?> c = loader.loadClass("com.hp.demo.classLoader.Demo");
            c.newInstance();
        }
    }
    
    

    运行结果:

    MyClassLoader loader = new MyClassLoader("lunabird","/Users/huangpeng/workspace/temp/");
    加载本项目路径下的Demo类,不设置parent,默认的parent是AppClassLoader,加载classpath下的类。
    
    MyClassLoader loader = new MyClassLoader(huangpengLoader,"lunabird","/Users/huangpeng/workspace/temp/");
    加载本项目路径下的Demo类,因为设置了loader的parent是huangpengLoader,huangpengLoader的parent默认是AppClassLoader,加载classpath下的类。
    
     MyClassLoader loader = new MyClassLoader(null,"lunabird","/Users/huangpeng/workspace/temp/");
    加载文件系统中的Demo类。因为设置了loader的parent为null,即BootStrapClassLoader,根据双亲委派模型BootStrapClassLoader加载不到类是会调用自定义的findClass回调函数,于是就调用到文件系统的类了。
    

    相关文章

      网友评论

      本文标题:Java类加载器ClassLoader的理解

      本文链接:https://www.haomeiwen.com/subject/hsmbvftx.html