JVM加载class文件的原理机制

1.Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中 

2.java中的类大致分为三种: 
    1.系统类 
    2.扩展类 
    3.由程序员自定义的类 

3.类装载方式,有两种 
    1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中,
    2.显式装载, 通过class.forname()等方法,显式加载需要的类 
  隐式加载与显式加载的区别: 
    两者本质是一样?, 

4.类加载的动态性体现 
    一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再 
运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,因为java最早就是为嵌入式系统而设计的,内存宝贵,这是一种可以理解的机制,而用到时再加载这也是java动态性的一种体现 

5.java类装载器 
    Java中的类装载器实质上也是类,功能是把类载入jvm中,值得注意的是jvm的类装载器并不是一个,而是三个,层次结构如下: 
      Bootstrap Loader  - 负责加载系统类 
            | 
          - - ExtClassLoader  - 负责加载扩展类 
                          | 
                      - - AppClassLoader  - 负责加载应用类 
        为什么要有三个类加载器,一方面是分工,各自负责各自的区块,另一方面为了实现委托模型,下面会谈到该模型 

6. 类加载器之间是如何协调工作的 
      前面说了,java中有三个类加载器,问题就来了,碰到一个类需要加载时,它们之间是如何协调工作的,即java是如何区分一个类该由哪个类加载器来完成呢。 
在这里java采用了委托模型机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”,注意喔,这句话具有递归性

/** 
 * @author Jamson Huang 
 * 
 */  
public class TestClass {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args)  throws Exception{  
        //调用class加载器  
        ClassLoader cl = TestClass.class.getClassLoader();  
        System.out.println(cl);  
        //调用上一层Class加载器  
        ClassLoader clParent = cl.getParent();  
        System.out.println(clParent);  
        //调用根部Class加载器  
        ClassLoader clRoot = clParent.getParent();  
        System.out.println(clRoot);  
          
    }  
  
}  
/** 
 * @author Jamson Huang 
 * 
 */  
public class Test1 {  
  
    /** 
     * @param args 
     */  
    public static void main(String[] args)throws Exception {  
        System.out.println(Test1.class.getClassLoader());  
          
        Test2 test2 = new Test2();  
          
        test2.print();  
    }  
  
}  
/** 
 * @author Jamson Huang 
 * 
 */  
public class Test2 {  
    public void print(){  
        System.out.println(Test2.class);  
        System.out.println(this.getClass());  
        System.out.println(Test2.class.getClassLoader());  
    }  
}  
try{   
URL url = new URL("file:/d:/test/lib/");   
URLClassLoader urlCL = new URLClassLoader(new URL[]{url});   
Class c = urlCL.loadClass("TestClassA");   
TestClassA object = (TestClassA)c.newInstance();   
object.method();   
}catch(Exception e){   
e.printStackTrace();   
}   



我们通过自定义的类加载器实现了 TestClassA 类的加载并调用 method ()方法。分析一下这个程序:首先定义 URL 指定类加载器从何处加载类, URL 可以指向网际网络上的任何位置,也可以指向我们计算机里的文件系统 ( 包含 JAR 文件 ) 。上述范例当中我们从 file:/d:/test/lib/ 处寻找类;然后定义 URLClassLoader 来加载所需的类,最后即可使用该实例了。 

9. 类加载器的阶层体系 

讨论了这么多以后,接下来我们仔细研究一下 Java 的类加载器的工作原理: 

当执行 java ***.class 的时候, java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会产生第一个类加载器―― Bootstrap Loader , Bootstrap Loader 是由 C++ 所撰写而成,这个 Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,最重要的就是加载 Launcher.java 之中的 ExtClassLoader ,并设定其 Parent 为 null ,代表其父加载器为 BootstrapLoader 。然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader ,并设定其 Parent 为之前产生的 ExtClassLoader 实体。这两个加载器都是以静态类的形式存在的。这里要请大家注意的是, Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,所以 Parent 和由哪个类加载器加载没有关系。 

下面的图形可以表示三者之间的关系: 
BootstrapLoader <---(Extends)----AppClassLoader <---(Extends)----ExtClassLoader 

这三个加载器就构成我们的 Java 类加载体系。他们分别从以下的路径寻找程序所需要的类: 

BootstrapLoader : sun.boot.class.path 
ExtClassLoader: java.ext.dirs 
AppClassLoader: java.class.path 

这三个系统参量可以通过 System.getProperty() 函数得到具体对应的路径。大家可以自己编程实现查看具体的路径。

相关推荐