JavaJVM之ClassLoader源码分析

186 阅读4分钟

层次结构和类图

ClassLoader层次结构:


 

 

UML类图:

 


  • sun.misc.Launcher.ExtClassLoader
  • sun.misc.Launcher.AppClassLoader

**** 

显式加载类

在代码中显式加载某个类,有三种方法:

  1. this.getClass().getClassLoader().loadClass()
  2. Class.forName()
  3. MyClassLoader.findClass()

ClassLoader.loadClass()

ClassLoader.loadClass()的加载步骤为:

  1. 调用 findLoadedClass(String) 来检查是否已经加载类。
  2. 在父类加载器上调用 loadClass 方法。如果父类加载器为null,则使用虚拟机的内置类加载器。
  3. 调用 findClass(String) 方法查找类。

 

 

     public Class<?> loadClass(String name) throws ClassNotFoundException {

       return loadClass(name, false);

    }

    /**

     * 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> 

     */

    protected synchronized Class<?> loadClass(String name, boolean resolve)

    throws ClassNotFoundException

    {

    // First, check if the class has already been loaded

    Class c = findLoadedClass(name);

    if (c == null) {

        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.

            c = findClass(name);

        }

    }

    if (resolve) {

        resolveClass(c);

    }

    return c;

    }

 

 

URLClassLoader.findClass()

 

ClassLoader.loadClass()的最后一步是调用findClass(),这个方法在ClassLoader中并未实现,由其子类负责实现。

findClass()的功能是找到class文件并把字节码加载到内存中。

自定义的ClassLoader一般覆盖这个方法。——以便使用不同的加载路径

 

    /* The search path for classes and resources */

    URLClassPath ucp;

    /* The context to be used when loading classes and resources */

    private AccessControlContext acc;

 

    /**

     * Finds and loads the class with the specified name from the URL search

     * path. Any URLs referring to JAR files are loaded and opened as needed

     * until the class is found.

     *

     *  @param name the name of the class

     *  @return the resulting class

     *  @exception ClassNotFoundException if the class could not be found

     */

    protected Class<?> findClass(final String name)

     throws ClassNotFoundException

    {

    try {

        return (Class)

        AccessController.doPrivileged(new PrivilegedExceptionAction() {

            public Object run() throws ClassNotFoundException {

            String path = name.replace('.', '/').concat(".class");

            // 1. URLClassPath ucp,帮助获取class文件字节流

            //    URLClassPath会用FileLoader或者JarLoader去加载字节码 

            Resource res = ucp.getResource(path, false); 

            if (res != null) {

                try {

                // 2. defineClass,创建类对象,将字节流解析成JVM能够识别的Class对象。

                return defineClass(name, res, true);

                } catch (IOException e) {

                throw new ClassNotFoundException(name, e);

                }

            } else {

                throw new ClassNotFoundException(name);

            }

            }

        }, acc);

    } catch (java.security.PrivilegedActionException pae) {

        throw (ClassNotFoundException) pae.getException();

    }

    }

 

ClassLoader.resolveClass()

加载完字节码后,会根据需要进行验证、解析。

 

    /**

     * Links the specified class.  This (misleadingly named) method may be

     * used by a class loader to link a class.  If the class <tt>c</tt> has

     * already been linked, then this method simply returns. Otherwise, the

     * class is linked as described in the "Execution" chapter of the <a

     * href="java.sun.com/docs/books/… Specification</a>.

     * </p>

     *

     *  @param  c

     *         The class to link

     *

     *  @throws  NullPointerException

     *          If <tt>c</tt> is <tt>null</tt>.

     *

     *  @see  #defineClass(String, byte[], int, int)

     */

    protected final void resolveClass(Class<?> c) {

    resolveClass0(c);

    }

    private native void resolveClass0(Class c);

 

 

自定义加载器

  • findClass()定义加载路径

findClass()的功能是找到class文件并把字节码加载到内存中。

自定义的ClassLoader一般覆盖这个方法。——以便使用不同的加载路径

在其中调用defineClass()解析字节码。

  • loadClass()定义加载机制

自定义的加载器可以覆盖该方法loadClass(),以便定义不同的加载机制

例如Servlet中的WebappClassLoader覆盖了该方法,在WEB-INFO/classes目录下查找类文件;在加载时,如果成功,则缓存到ResourceEntry对象。——不同的加载机制。

 

AppClassLoader覆盖了loadClass()方法。

如果自定义的加载器仅覆盖了findClass,而未覆盖loadClass(即加载规则一样,但加载路径不同);则调用getClass().getClassLoader()返回的仍然是AppClassLoader!因为真正load类的,还是AppClassLoader。

  • 实现类的热部署

JVM默认不能热部署类,因为加载类时会去调用findLoadedClass(),如果类已被加载,就不会再次加载。

JVM判断类是否被加载有两个条件:完整类名是否一样、ClassLoader是否是同一个。

所以要实现热部署的话,只需要使用ClassLoader的不同实例来加载。

``


MyClassLoader cl1 = new MyClassLoader();
Class c1 = cl1.findClass( "Test.class");

c1.newInstance();

MyClassLoader cl2 = new MyClassLoader();
Class c2 = cl2.findClass( "Test.class");

c2.newInstance();

上例中的c1和c2就是两个不同的实例。

如果用同一个ClassLoader实例来加载,则会抛LinkageError。

 

  • 不可覆盖的final方法

defineClass

    /**

     * Converts an array of bytes into an instance of class <tt>Class</tt>.

     * Before the <tt>Class</tt> can be used it must be resolved.

     */

protected final Class<?> defineClass(String name, byte[] b, int off, int len)

**** 

findLoadedClass


    /**

     * Returns the class with the given <a href="#name">binary name</a> if this

     * loader has been recorded by the Java virtual machine as an initiating

     * loader of a class with that <a href="#name">binary name</a>.  Otherwise

     * <tt>null</tt> is returned.  </p>

     */

protected final Class<?> findLoadedClass(String name) 

**** 

findSystemClass


    /**

     * Finds a class with the specified <a href="#name">binary name</a>,

     * loading it if necessary.

     *

     * <p> This method loads the class through the system class loader (see

     * {@link #getSystemClassLoader()}).  The <tt>Class</tt> object returned

     * might have more than one <tt>ClassLoader</tt> associated with it.

     * Subclasses of <tt>ClassLoader</tt> need not usually invoke this method,

     * because most class loaders need to override just {@link  #findClass(String)}.  </p>

      */

protected final Class<?> findSystemClass(String name)

 

getParent

    /**

     * Returns the parent class loader for delegation. Some implementations may

     * use <tt>null</tt> to represent the bootstrap class loader. This method

     * will return <tt>null</tt> in such implementations if this class loader's

     * parent is the bootstrap class loader.

     */

public final ClassLoader getParent()

 

resolveClass

 /**

     * Links the specified class.  This (misleadingly named) method may be

     * used by a class loader to link a class.  If the class <tt>c</tt> has

     * already been linked, then this method simply returns. Otherwise, the

     * class is linked as described in the "Execution" chapter of the <a

     * href="java.sun.com/docs/books/…  Specification</a>.

     */

protected final void resolveClass(Class<?> c)\