欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > java >内容正文

java

JavaJVM之ClassLoader源码分析

发布时间:2023/12/4 java 52 豆豆
生活随笔 收集整理的这篇文章主要介绍了 JavaJVM之ClassLoader源码分析 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

层次结构和类图

ClassLoader层次结构:
  UML类图:
 
  • sun.misc.Launcher.ExtClassLoader
  • sun.misc.Launcher.AppClassLoader
 

显式加载类

在代码中显式加载某个类,有三种方法:
  • this.getClass().getClassLoader().loadClass()
  • Class.forName()
  • MyClassLoader.findClass()
  • ClassLoader.loadClass()

    ClassLoader.loadClass()的加载步骤为:
  • 调用 findLoadedClass(String) 来检查是否已经加载类。
  • 在父类加载器上调用 loadClass 方法。如果父类加载器为null,则使用虚拟机的内置类加载器。
  • 调用 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="http://java.sun.com/docs/books/jls/">Java LanguageSpecification</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="http://java.sun.com/docs/books/jls/">Java Language Specification</a>.      */ protected final void resolveClass(Class<?> c)

    总结

    以上是生活随笔为你收集整理的JavaJVM之ClassLoader源码分析的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。