JVM加载class文件的原理机制(转)

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 找不到,那么才由本身依照本身的搜索路径搜索类”,注意喔,这句话具备递归性 

java

Java代码   收藏代码
  1. /** 
  2.  * @author Jamson Huang 
  3.  * 
  4.  */  
  5. public class TestClass {  
  6.   
  7.     /** 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args)  throws Exception{  
  11.         //调用class加载器  
  12.         ClassLoader cl = TestClass.class.getClassLoader();  
  13.         System.out.println(cl);  
  14.         //调用上一层Class加载器  
  15.         ClassLoader clParent = cl.getParent();  
  16.         System.out.println(clParent);  
  17.         //调用根部Class加载器  
  18.         ClassLoader clRoot = clParent.getParent();  
  19.         System.out.println(clRoot);  
  20.           
  21.     }  
  22.   
  23. }  

 

Result代码   收藏代码
  1. Run, Console中出现的log信息以下:  
  2. sun.misc.Launcher$AppClassLoader@7259da  
  3. sun.misc.Launcher$ExtClassLoader@16930e2  
  4. null  


能够看出TestClass是由AppClassLoader加载器加载的 
AppClassLoader的Parent 加载器是 ExtClassLoader 
可是ExtClassLoader的Parent为 null 是怎么回事呵,朋友们留意的话,前面有提到Bootstrap Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体,因此在java程序代码里试图打印出其内容时,咱们就会看到输出为null 
【注:如下内容大部分引用java深度历险】 
弄明白了上面的示例,接下来直接进入类装载的委托模型实例,写两个文件,以下:程序员

Java代码   收藏代码
  1. /** 
  2.  * @author Jamson Huang 
  3.  * 
  4.  */  
  5. public class Test1 {  
  6.   
  7.     /** 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args)throws Exception {  
  11.         System.out.println(Test1.class.getClassLoader());  
  12.           
  13.         Test2 test2 = new Test2();  
  14.           
  15.         test2.print();  
  16.     }  
  17.   
  18. }  
  19. /** 
  20.  * @author Jamson Huang 
  21.  * 
  22.  */  
  23. public class Test2 {  
  24.     public void print(){  
  25.         System.out.println(Test2.class);  
  26.         System.out.println(this.getClass());  
  27.         System.out.println(Test2.class.getClassLoader());  
  28.     }  
  29. }  
Result代码   收藏代码
  1. Run,Console出现log以下:  
  2. sun.misc.Launcher$AppClassLoader@7259da  
  3. class com.java.test.Test2  
  4. class com.java.test.Test2  
  5. sun.misc.Launcher$AppClassLoader@7259da  


7. 预先加载与依需求加载 

Java 运行环境为了优化系统,提升程序的执行速度,在 JRE 运行的开始会将 Java 运行所须要的基本类采用预先加载( pre-loading )的方法所有加载要内存当中,由于这些单元在 Java 程序运行的过程中常常要使用的,主要包括 JRE 的 rt.jar 文件里面全部的 .class 文件。 

当 java.exe 虚拟机开始运行之后,它会找到安装在机器上的 JRE 环境,而后把控制权交给 JRE , JRE 的类加载器会将 lib 目录下的 rt.jar 基础类别文件库加载进内存,这些文件是 Java 程序执行所必须的,因此系统在开始就将这些文件加载,避免之后的屡次 IO 操做,从而提升程序执行效率。 

图( 2 )咱们能够看到多个基础类被加载, java.lang.Object,java.io.Serializable 等等。
相对于预先加载,咱们在程序中须要使用本身定义的类的时候就要使用依需求加载方法( load-on-demand ),就是在 Java 程序须要用到的时候再加载,以减小内存的消耗,由于 Java 语言的设计初衷就是面向嵌入式领域的。
8. 自定义类加载机制 

以前咱们都是调用系统的类加载器来实现加载的,其实咱们是能够本身定义类加载器的。利用 Java 提供的 java.net.URLClassLoader 类就能够实现。下面咱们看一段范例: 

编程

Java代码   收藏代码
  1. try{   
  2. URL url = new URL("file:/d:/test/lib/");   
  3. URLClassLoader urlCL = new URLClassLoader(new URL[]{url});   
  4. Class c = urlCL.loadClass("TestClassA");   
  5. TestClassA object = (TestClassA)c.newInstance();   
  6. object.method();   
  7. }catch(Exception e){   
  8. e.printStackTrace();   
  9. }   



咱们经过自定义的类加载器实现了 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() 函数获得具体对应的路径。你们能够本身编程实现查看具体的路径。网络

 

转自:http://blog.csdn.net/seelye/article/details/8266192jvm

相关文章
相关标签/搜索