zoukankan      html  css  js  c++  java
  • ClassLoad的加载过程及分析

    -Xbootclasspath:bootclasspath 
    让jvm从指定路径(可以是分号分隔的目录、jar、或者zip)中加载bootclass,用来替换jdk的rt.jar;若非必要,一般不会用到; 
    -Xbootclasspath/a:path 
    将指定路径的所有文件追加到默认bootstrap路径中; 
    -Xbootclasspath/p:path 
    让jvm优先于bootstrap默认路径加载指定路径的所有文件; 

    一,ClassLoader的大体过程

    如图:

    ClassLoader的状态

    详解:

     虚拟机一启动,会先做一些初始化的动作。一旦初始化动作完成之后,就会产生第一个类别加载器,即所谓的Bootstrap Loader,Bootstrap Loader 是由C++ 所撰写而成,这个Bootstrap Loader所做的初始工作中,除了也做一些基本的初始化动作之外,最重要的就是加载定义在sun.misc 命名空间底下的Launcher.java 之中的ExtClassLoader( 因为是inner class ,所以编译之后会变成Launcher$ExtClassLoader.class) ,并设定其Parent 为null,代表其父加载器为Bootstrap Loader 。然后Bootstrap Loader ,再要求加载定义于sun.misc 命名空间底下的Launcher.java 之中的AppClassLoader( 因为是inner class,所以编译之后会变成Launcher$AppClassLoader.class) ,并设定其Parent 为之前产生的ExtClassLoader 实例。

    由以上可以看出,classLoader是由下向上查找,上层的不能向下查找。

    二,ClassLoader中类的关系

    如图:

    详解:

     AppClassLoader 和ExtClassLoader 都是URLClassLoader 的子类别。由于它们都是URLClassLoader 的子类别,所以它们也应该有URL 作为搜寻类别档的参考,由原始码中我们可以得知,AppClassLoader 所参考的URL 是从系统参java.class.path 取出的字符串所决定,而java.class.path 则是由我们在执行java.exe 时,利用 –cp 或-classpath 或CLASSPATH 环境变量所决定。

    ClassLoader的loadClass代码:

    Java代码 复制代码 收藏代码
    1. protected synchronized Class<?> loadClass(String name, boolean resolve)  
    2.     throws ClassNotFoundException  
    3.     {  
    4.     // First, check if the class has already been loaded  
    5.        //类是否被加载过  
    6.     Class c = findLoadedClass(name);  
    7.     if (c == null) {  
    8.         try {  
    9.         if (parent != null) {  
    10.                   //到parentclassloader中去查找(像这个parent还有parent递归方式进行查找)  
    11.             c = parent.loadClass(name, false);  
    12.         } else {  
    13.                   //启动类加载器进行加载  
    14.             c = findBootstrapClass0(name);  
    15.         }  
    16.         } catch (ClassNotFoundException e) {  
    17.             // If still not found, then invoke findClass in order  
    18.             // to find the class.  
    19.               //当一直都没有找到时,启动当前类的findClass进行查找  
    20.              //这个通常也是我们扩展的地方  
    21.             c = findClass(name);  
    22.         }  
    23.     }  
    24.     if (resolve) {  
    25.         resolveClass(c);  
    26.     }  
    27.     return c;  
    28.     }  
    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) {
                      //到parentclassloader中去查找(像这个parent还有parent递归方式进行查找)
    		    c = parent.loadClass(name, false);
    		} else {
                      //启动类加载器进行加载
    		    c = findBootstrapClass0(name);
    		}
    	    } catch (ClassNotFoundException e) {
    	        // If still not found, then invoke findClass in order
    	        // to find the class.
                  //当一直都没有找到时,启动当前类的findClass进行查找
                 //这个通常也是我们扩展的地方
    	        c = findClass(name);
    	    }
    	}
    	if (resolve) {
    	    resolveClass(c);
    	}
    	return c;
        }
     
    详解:
    java文件的编译和Class的载入执行,都是使用Launcher初始化的appclassloader作为类载入器的,我们无法动态的改变App classloader,更无法让JVM使用我们自己的classloader来替换system classloader,根据全盘负责原则,就限制了编译和运行时,我们无法直接显式的使用一个system classloader寻找不到的Class,即我们只能使用Java核心类库,扩展类库和CLASSPATH中的类库中的Class。
    而且我们也无法载入以java.lang....开头的包,进行了限制

    三,分析及证明:

    可以用最底层的ClassLoader得到某一个类(Test)时,Test.class.getClassLoader()就可知当前类在哪一个层次的ClassLoader下被加载

    1,BootStrapClassLoader

    Java代码 复制代码 收藏代码
    1. Class clazz=Class.forName("java.lang.Object");  
    2. System.out.println(clazz.getClassLoader());  
    3.   
    4.   //输出为null,因为bootstrap在java中不是类,而是由c++编写的  
    5.   
    6.   
    7.     URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();  
    8.          for (int i = 0; i < urls.length; i++) {  
    9.            System.out.println(urls[i].getFile());  
    10.          }//用这个进行查找bootstrap所加载的是哪些jar包  
    	Class clazz=Class.forName("java.lang.Object");
    	System.out.println(clazz.getClassLoader());
    
       //输出为null,因为bootstrap在java中不是类,而是由c++编写的
    
    
         URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
              for (int i = 0; i < urls.length; i++) {
                System.out.println(urls[i].getFile());
              }//用这个进行查找bootstrap所加载的是哪些jar包

     

    2,ExtClassLoader

    Java代码 复制代码 收藏代码
    1. clazz = Class.forName("sun.net.spi.nameservice.dns.DNSNameService");    
    2.             clazzLoader = clazz.getClassLoader();    
    3.             System.out.println(" sun.net.spi.nameservice.dns.DNSNameService's loader is  "    
    4.                             + clazzLoader);    
    5. //在些可以说明ExtClassLoader所加载的类  
    clazz = Class.forName("sun.net.spi.nameservice.dns.DNSNameService");  
                clazzLoader = clazz.getClassLoader();  
                System.out.println(" sun.net.spi.nameservice.dns.DNSNameService's loader is  "  
                                + clazzLoader);  
    //在些可以说明ExtClassLoader所加载的类

     3,AppClassLoader

     当前工程中class与lib都是用此Loader加载

    可以通过ClassLoader.getSystemClassLoader()可以获取到AppClassLoader的

    4,DefineClassLoader

    可以继承URLClassLoader或ClassLoader

    当继承ClassLoader重写findClass()方法,parent会相应是AppClassLoader-->ExtClassLoader-->BootStrapClassLoader

    URLClassLoader可以直接设置url即可

    问题:

    由于自己自定义了一个DefineClassLoader替代了加载ant的ClassLoader,另外添加自己jar包,

    但是在执行ant编译时,要执行tools.jar里的javac类,在执行javac这个类时,是处在AppClassLoader

    下,找不到我添加的DefineClassLoader的jar包

    ------------------------------------------------

    其他一些相应的操作(参考)

      在预设情况下,AppClassLoader的搜寻路径为”.”( 目前所在目录),如果使用-classpath 选项(与-cp 等效),就可以改变AppClassLoader 的搜寻路径,如果没有指定-classpath 选项,就会搜寻环境变量CLASSPATH 。如果同时有CLASSPATH 的环境设定与-classpath 选项,则以-classpath 选项的内容为主,CLASSPATH 的环境设定与-classpath 选项两者的内容不会有加成的效果。至于ExtClassLoader 也有相同的情形,不过其搜寻路径是参考系统参数java.ext.dirsBootstrap Loader ,我们可以经由查询由系统参数sun.boot.class.path 得知Bootstrap Loader 用来搜寻类别的路径java -Dsun.boot.class.path=请回头看到java.class.path 与sun.boot.class.path,也就是说,AppClassLoader 与Bootstrap Loader 会搜寻它们所指定的位置(或JAR 文件),如果找不到就找不到了,AppClassLoader 与Bootstrap Loader 不会递归式地搜寻这些位置下的其他路径或其他没有被指定的JAR 文件。反观ExtClassLoader,所参考的系统参数是java.ext.dirs,意思是说,他会搜寻底下的所有JAR 文件以及classes 目录,作为其搜寻路径(所以您会发现上面我们在测试的时候,如果加入-Dsun.boot.class.path=c:windows 选项时,程序的起始速度会慢了些,这是因为c:windows 目录下的文件很多,必须花额外的时间来列举JAR 文件)。

     

      在命令行下参数时,使用–classpath / -cp / 环境变量CLASSPATH 来更改AppClassLoader 的搜寻路径,或者用 –Djava.ext.dirs 来改变ExtClassLoader的搜寻目录,两者都是有意义的。可是用–Dsun.boot.class.path 来改变Bootstrap Loader 的搜寻路径是无效。这是因为AppClassLoader与ExtClassLoader 都是各自参考这两个系统参数的内容而建立,当您在命令行下变更这两个系统参数之后,AppClassLoader 与ExtClassLoader在建立实例的时候会参考这两个系统参数,因而改变了它们搜寻类别文件的路径; 而系统参数sun.boot.class.path 则是默认与Bootstrap Loader 的搜寻路径相同,就算您更改该系统参与,与BootstrapLoader 完全无关,AppClassLoader 与ExtClassLoader 在整个虚拟机之中只会存有一份,一旦建立了,其内部所参考的搜寻路径将不再改变,也就是说,即使我们在程序里利用System.setProperty() 来改变系统参数的内容,仍然无法更动AppClassLoader 与ExtClassLoader 的搜寻路径。因此,执行时期动态更改搜寻路径的设定是不可能的事情。

  • 相关阅读:
    [CF538F]A Heap of Heaps(主席树)
    [BZOJ1901][luogu2617]Dynamic Rankings(树状数组+主席树)
    [BZOJ3932][CQOI2015]任务查询系统(差分+主席树)
    [BZOJ2588]Count on a tree(LCA+主席树)
    [BZOJ2733][HNOI2012] 永无乡(线段树合并)
    [BZOJ1604][Usaco2008 Open]Cow Neighborhoods 奶牛的邻居 (Treap+单调队列)
    【贪心】POJ2376-Cleaning Shifts
    【穷竭】POJ3187-Backward Digit Sums
    【枚举+贪心】POJ2718-Smallest Difference
    【BFS】POJ3669-Meteor Shower
  • 原文地址:https://www.cnblogs.com/xuxiuxiu/p/5001556.html
Copyright © 2011-2022 走看看