类加载机制及类加载器加载Class流程

一、类加载机制
JVM的类加载机制主要有如下三种
  • 全盘负责。当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其余Class也将由该类加载器负责载入。
  • 父类委托。先让父类加载器试图加载该Class,只有在父类加载器没法加载该类时才尝试从本身的类路径中加载该类。
  • 缓存机制。缓存机制保证全部加载过的Class都会被缓存,当程序中须要使用某个Class时,类加载器先从缓存区中搜寻该Class,缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换为Class对象,存入缓存区中。

二、类加载器加载Class大体通过以下步骤。
(1)、先判断此类是否被载入过(即缓存区中是否有此Class),若是有则返回对应的java.lang.Class对象。不然判断父类加载器是否存在。
(2)、若是父类加载器不存在(若是没有父类加载器,则要么parent必定是根类加载器,要么本省就是根类加载器),则请求使用根类加载器来载入目标类,若是成功则返回对应的java.lang.Class对象,失败则抛出ClassNotFoundException异常。
(3)、若是父类加载器存在,则请求使用父类加载器去载入目标类,若是成功则返回对应的java.lang.Class对象,失败则使用当前类加载器尝试寻找Class文件(从与此ClassLoader相关的类路径中寻找),若是找到则从文件中载入class,成功则返回对应的java.lang.Class对象,若是找不到则抛出ClassNotFoundException.

流程图以下


ClassLoader类的loadClass方法源码以下。
/**
     * 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:
     *
     * <p><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);//此方法为native方法。
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                    // 加载失败则抛出ClassNotFoundException
                }

                if (c == null) {//父类加载器或根类加载器均未加载成功,则使用当前类加载器寻找Class文件,ClassLoader容许子类重写findClass方法
                    // 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;
        }
    }