一步一步深入jvm(4)—jvm classloader体系结构及加载原理

      我们正常遇到这样的情况,自定义一个jar文件中包含一个class【类】文件。我们想重写这个类的话直接在工程中包名及类名一致就可行。
      但是对于某些类文件例如String等一些类,在我们的工程中直接重写这些类,是调用不了的。这是什么原因呢?
      这和java的类的加载机制有关,那到底java是怎么加载类文件?这个问题也是下文将阐述的问题。

      首选要提到的是ClassLoader,那什么是ClassLoader?
      ClassLoader负责将相应的类文件(即class文件)装载到内存中。jvm中存在多种类型的ClassLoader,主要分为4类:
     (1)BootStrap[启动类加载器] 是最顶层的类加载器,它是由C++编写而成,并且已经内嵌到JVM中了,主要用来读取Java的核心类库JRE/lib/rt.jarresources.jar、charsets.jar
    (2)Extension ClassLoader[扩展类加载器] 是用来读取Java的扩展类库,读取JRE/lib/ext/*.jar
    (3)App ClassLoader[应用类加载器] 是用来读取当前应用下CLASSPATH指定的所有jar包或目录的类文件【是用户自定义类装载器的缺省父装载器】。
    (4)Custom ClassLoader[用户自定义类加载器] 是用户自定义编写的,它用来读取指定类文件

    不同的类装载器,只能加载相应范围的类,哪怕是同包下的类,只要他们不属于同一类装载器,都是相互隔绝的。这对一些有安全隐患的类起到了安全隔离的作用。使它不能冒充系统类来破坏程序正常运作。

    这些ClassLoader组成了jvm的ClassLoader的体系结构,如下图所示为。

   一步一步深入jvm(4)—jvm classloader体系结构及加载原理_第1张图片

   从图中我们可以看出,当加载一个类的时候,是先检查是当前加载器是否已经加载该类,如已加载则返回。否则委托给它的父加载器检查是否已经加载,一直检查到BootStrap【启动类加载器】,如果还是没有发现该类被加载,则BootStrap【启动类加载器】尝试加载该类,加载成功则返回,如没有加载到则将委托还给子装载器,子类加载器继续加载,一直装载到最底层的ClassLoader【可能为App ClassLoader或者Custom ClassLoader,甚至可能为Extension ClassLoader】,如果没有装载到相应的类则抛出ClassNotFoundException。整个模型也称作双亲委派模型。

   总结来说 双亲委派方式的类加载,指的是优先从顶层启动类加载器开始,自顶向下的方式加载类的模型 (参见图示)。 

   可能有点迷糊,我们结合一个实际的源码来说明该模型。

   java中的Launcher类【在sun.misc包下】,它包含两个内部类AppClassLoader、ExtClassLoader,java通过这两个类完成对应用及扩展类文件的加载,它们继承URLClassLoader类,而URLClassLoader类继SecureClassLoader类,SecureClassLoader继承抽象类ClassLoader。
   它们的类图关系如下

 

一步一步深入jvm(4)—jvm classloader体系结构及加载原理_第2张图片

     虽然AppClassLoader、ExtClassLoader都是继承于URLClassLoader,但是AppClassLoader的父加载器为ExtClassLoader【这里就不多讲了,可以看看源码就知道了】。而对于AppClassLoader、ExtClassLoader类中并未覆盖抽象类ClassLoader的loadClass方法。ClassLoader的loadClass的方法如下

protected synchronized Class<?> loadClass(String name, boolean resolve)
 throws ClassNotFoundException
    {
 // 首选查看类是否已经被加载
 Class c = findLoadedClass(name);
        //没有被加载
 if (c == null) {
     try {
  //如果父加载器不为空,父加载器查看该类是否被加载
  if (parent != null) {
      c = parent.loadClass(name, false);
  } else {
         //如果父加载器不存在调用BootStrap查看是否已被加载
      c = findBootstrapClass0(name);
  }
     } catch (ClassNotFoundException e) {
         // If still not found, then invoke findClass in order
         // to find the class.
                //如果顶级装载器BootStrap未发现已加载该类,增尝试加载该类
         c = findClass(name);
     }
 }
 if (resolve) {
     resolveClass(c);
 }
 return c;
    }

    而findBootstrapClass0方法如下

 

    private Class findBootstrapClass0(String name)
	throws ClassNotFoundException
    {
	check();
	if (!checkName(name))
	    throw new ClassNotFoundException(name);
	return findBootstrapClass(name);
    }

    private native Class findBootstrapClass(String name)
	throws ClassNotFoundException;

     它是一个本地接口的方法时C++写的。

     从源码中可以看出ClassLoader总是现从下往上查看类是否已被加载,然后从上往下尝试加载类。

    一般自定类加载器时loadClass是不重写,而自定义加载器重写的是findClass。从源码中我们可以看出,所有加载器的顶级加载器为BootStrap【即父加载器不存时】,同时注意到的时出于安全等因素考虑, BootStrap不会加载 lib 存在的陌生类或jar 开发者通过将要加载的非 JDK 自身的类放置到此目录下期待启动类加载器加载是不可能的,其他类加载器无这样的限制
   示例代码如下

public class classLoaderTest {
 public static void main( String args[]  ){
   classLoaderTest t = new classLoaderTest();
   System.out.print( t.getClass() );
   System.out.println( t.getClass().getClassLoader() ) ; 
   System.out.println( t.getClass().getClassLoader().getParent() ) ; 
   System.out.println( t.getClass().getClassLoader().getParent().getParent() ) ;    
  }
}

 

类加载器查看如下

class classLoaderTest
sun.misc.Launcher$AppClassLoader@82ba41
sun.misc.Launcher$ExtClassLoader@923e30
null

 

      第二行的结果表示 classLoaderTest 的类的加载器为 AppClassLoader

       第三行的结果表示  AppClassLoader的父加载器为 ExtClassLoader

       第四行的结果null表示  ExtClassLoader 的父加载器为 BootStrap

       整个的加载流程如下自定类classLoaderTest,创建该类对象时,先是AppClassLoader检查是否已经加载该类,AppClassLoader并未加载该类,AppClassLoader委托给ExtClassLoader,而ExtClassLoader也未发现已经装载该类ExtClassLoader将委托交给BootStrap,BootStrap也未发现。这时BootStrap将尝试加载classLoaderTest,BootStrap未加载到,将委托交还给ExtClassLoader,ExtClassLoader未加载到,将委托交还给AppClassLoader,AppClassLoader加载到该类,并创建classLoaderTest。

      这里我们回到问题为什么对于String等类,在应用重写,为什么不能覆盖系统的String类型。

     从双亲委派模型 中我们可以知道,String对象存在rt.jar中它是由BootStrap负责加载,这样我们在应用中重写该类的时候无法加载自定义的String类【且是永远加载不到】,只能加载系统的String,这里也是java沙箱模型的第一保障。

 

   备注:

java –verbose:class ClassLoaderTest


可以查看具体的类运行的时候类加载过程

 图及部分类容参考

  http://www.cnblogs.com/ChrisWang/archive/2009/11/17/Inside-JVM-4-ClassLoader-Knowledge-Sharing.html

你可能感兴趣的:(jvm,C++,c,C#,ext)