zoukankan      html  css  js  c++  java
  • 深入研究Java类载入机制

    深入研究Java类载入机制
     
    类载入是Java程序运行的第一步,研究类的载入有助于了解JVM运行过程,并指导开发人员採取更有效的措施配合程序运行。
    研究类载入机制的第二个目的是让程序能动态的控制类载入,比方热部署等,提高程序的灵活性和适应性。
     
    一、简单过程
     
    Java程序执行的场所是内存,当在命令行下执行:
    java HelloWorld
    命令的时候,JVM会将HelloWorld.class载入到内存中,并形成一个Class的对象HelloWorld.class。
    当中的过程就是类载入过程:
    1、寻找jre文件夹,寻找jvm.dll,并初始化JVM;
    2、产生一个Bootstrap Loader(启动类载入器);
    3、Bootstrap Loader自己主动载入Extended Loader(标准扩展类载入器),并将其父Loader设为Bootstrap Loader。
    4、Bootstrap Loader自己主动载入AppClass Loader(系统类载入器),并将其父Loader设为Extended Loader。
    5、最后由AppClass Loader载入HelloWorld类。
     
    以上就是类载入的最一般的过程。
     
    二、类载入器各自搜索的文件夹
     
    为了弄清楚这个问题,首先还要看看System类的API doc文档。
     
     
    相关值的描写叙述
    java.version Java 执行时环境版本号
    java.vendor Java 执行时环境供应商
    java.vendor.url Java 供应商的 URL
    java.home Java 安装文件夹
    java.vm.specification.version Java 虚拟机规范版本号
    java.vm.specification.vendor Java 虚拟机规范供应商
    java.vm.specification.name Java 虚拟机规范名称
    java.vm.version Java 虚拟机实现版本号
    java.vm.vendor Java 虚拟机实现供应商
    java.vm.name Java 虚拟机实现名称
    java.specification.version Java 执行时环境规范版本号
    java.specification.vendor Java 执行时环境规范供应商
    java.specification.name Java 执行时环境规范名称
    java.class.version Java 类格式版本
    java.class.path Java 类路径
    java.library.path 载入库时搜索的路径列表
    java.io.tmpdir 默认的暂时文件路径
    java.compiler 要使用的 JIT 编译器的名称
    java.ext.dirs 一个或多个扩展文件夹的路径
    os.name 操作系统的名称
    os.arch 操作系统的架构
    os.version 操作系统的版本号
    file.separator 文件分隔符(在 UNIX 系统中是“/”)
    path.separator 路径分隔符(在 UNIX 系统中是“:”)
    line.separator 行分隔符(在 UNIX 系统中是“/n”)
    user.name 用户的账户名称
    user.home 用户的主文件夹
    user.dir 用户的当前工作文件夹
     
    可惜这个帮助文档并不全,直接用程序打印出来例如以下:
                    for (Map.Entry<Object, Object> entry : System.getProperties().entrySet()) { 
                            System.out.println(entry.getKey()+" "+entry.getValue()); 
                    }
     
    java.runtime.name Java(TM) SE Runtime Environment
    sun.boot.library.path Q:jdk6jrein
    java.vm.version 14.0-b16
    java.vm.vendor Sun Microsystems Inc.
    java.vendor.url http://java.sun.com/
    path.separator ;
    idea.launcher.port 7532
    java.vm.name Java HotSpot(TM) Client VM
    file.encoding.pkg sun.io
    sun.java.launcher SUN_STANDARD
    user.country CN
    sun.os.patch.level Service Pack 3
    java.vm.specification.name Java Virtual Machine Specification
    user.dir E:projects estScanner
    java.runtime.version 1.6.0_14-b08
    java.awt.graphicsenv sun.awt.Win32GraphicsEnvironment
    java.endorsed.dirs Q:jdk6jrelibendorsed
    os.arch x86
    java.io.tmpdir C:DOCUME~1ADMINI~1LOCALS~1Temp
    line.separator
    java.vm.specification.vendor Sun Microsystems Inc.
    user.variant
    os.name Windows XP
    sun.jnu.encoding GBK
    java.library.path Q:jdk6in;.;C:WINDOWSSunJavain;C:WINDOWSsystem32;C:WINDOWS;Q:jdk6in;Q:JavaFXjavafx-sdk1.2in;Q:JavaFXjavafx-sdk1.2emulatorin;C:WINDOWSsystem32;C:WINDOWS;C:WINDOWSSystem32Wbem;C:MySQL Server 5.1in;C:Program FilesStormIICodec;C:Program FilesStormII
    java.specification.name Java Platform API Specification
    java.class.version 50
    sun.management.compiler HotSpot Client Compiler
    os.version 5.1
    user.home d:我的文档
    user.timezone
    java.awt.printerjob sun.awt.windows.WPrinterJob
    idea.launcher.bin.path C:IDEA8in
    file.encoding UTF-8
    java.specification.version 1.6
    java.class.path Q:jdk6jrelibalt-rt.jar;Q:jdk6jrelibcharsets.jar;Q:jdk6jrelibdeploy.jar;Q:jdk6jrelibjavaws.jar;Q:jdk6jrelibjce.jar;Q:jdk6jrelibjsse.jar;Q:jdk6jrelibmanagement-agent.jar;Q:jdk6jrelibplugin.jar;Q:jdk6jrelib esources.jar;Q:jdk6jrelib t.jar;Q:jdk6jrelibextdnsns.jar;Q:jdk6jrelibextlocaledata.jar;Q:jdk6jrelibextsunjce_provider.jar;Q:jdk6jrelibextsunmscapi.jar;Q:jdk6jrelibextsunpkcs11.jar;E:projects estScanneroutproduction estScanner;C:IDEA8libidea_rt.jar
    user.name Administrator
    java.vm.specification.version 1
    java.home Q:jdk6jre
    sun.arch.data.model 32
    user.language zh
    java.specification.vendor Sun Microsystems Inc.
    awt.toolkit sun.awt.windows.WToolkit
    java.vm.info mixed mode, sharing
    java.version 1.6.0_14
    java.ext.dirs Q:jdk6jrelibext;C:WINDOWSSunJavalibext
    sun.boot.class.path Q:jdk6jrelib esources.jar;Q:jdk6jrelib t.jar;Q:jdk6jrelibsunrsasign.jar;Q:jdk6jrelibjsse.jar;Q:jdk6jrelibjce.jar;Q:jdk6jrelibcharsets.jar;Q:jdk6jreclasses
    java.vendor Sun Microsystems Inc.
    file.separator
    java.vendor.url.bug http://java.sun.com/cgi-bin/bugreport.cgi
    sun.io.unicode.encoding UnicodeLittle
    sun.cpu.endian little
    sun.desktop windows
    sun.cpu.isalist
     
    1、Bootstrap Loader(启动类载入器):载入System.getProperty("sun.boot.class.path")所指定的路径或jar。
    2、Extended Loader(标准扩展类载入器ExtClassLoader):载入System.getProperty("java.ext.dirs")所指定的路径或jar。在使用Java执行程序时,也能够指定其搜索路径,比如:java -Djava.ext.dirs=d:projects estprojclasses HelloWorld
     
    3、AppClass Loader(系统类载入器AppClassLoader):载入System.getProperty("java.class.path")所指定的路径或jar。在使用Java执行程序时,也能够加上-cp来覆盖原有的Classpath设置,比如: java -cp ./lavasoft/classes HelloWorld
     
    ExtClassLoader和AppClassLoader在JVM启动后,会在JVM中保存一份,而且在程序执行中无法改变其搜索路径。假设想在执行时从其它搜索路径载入类,就要产生新的类载入器。
     
    三、类载入器的特点
     
    1、执行一个程序时,总是由AppClass Loader(系统类载入器)開始载入指定的类。
    2、在载入类时,每一个类载入器会将载入任务上交给其父,假设其父找不到,再由自己去载入。
    3、Bootstrap Loader(启动类载入器)是最顶级的类载入器了,其父载入器为null.
     
    四、类载入器的获取
     
    非常easy,看以下样例
    public class HelloWorld { 
            public static void main(String[] args) { 
                    HelloWorld hello = new HelloWorld(); 
                    Class c = hello.getClass(); 
                    ClassLoader loader = c.getClassLoader(); 
                    System.out.println(loader); 
                    System.out.println(loader.getParent()); 
                    System.out.println(loader.getParent().getParent()); 
            } 
    }
     
    打印结果:
    sun.misc.Launcher$AppClassLoader@19821f 
    sun.misc.Launcher$ExtClassLoader@addbf1 
    null 

    Process finished with exit code 0
     
    从上面的结果能够看出,并没有获取到ExtClassLoader的父Loader,原因是Bootstrap Loader(启动类载入器)是用C语言实现的,找不到一个确定的返回父Loader的方式,于是就返回null。
     
    五、类的载入
     
    类载入有三种方式:
    1、命令行启动应用时候由JVM初始化载入
    2、通过Class.forName()方法动态载入
    3、通过ClassLoader.loadClass()方法动态载入
     
    三种方式差别比較大,看个样例就明确了:
    public class HelloWorld { 
            public static void main(String[] args) throws ClassNotFoundException { 
                    ClassLoader loader = HelloWorld.class.getClassLoader(); 
                    System.out.println(loader); 
                    //使用ClassLoader.loadClass()来载入类,不会运行初始化块 
                    loader.loadClass("Test2"); 
                    //使用Class.forName()来载入类,默认会运行初始化块 
    //                Class.forName("Test2"); 
                    //使用Class.forName()来载入类,并指定ClassLoader,初始化时不运行静态块 
    //                Class.forName("Test2", false, loader); 
            } 
    }
     
    public class Test2 { 
            static { 
                    System.out.println("静态初始化块运行了!"); 
            } 
    }
     
    分别切换载入方式,会有不同的输出结果。
     
    六、自己定义ClassLoader
     
    为了说明问题,先看样例:
    package test; 

    import java.net.MalformedURLException; 
    import java.net.URL; 
    import java.net.URLClassLoader; 

    /** 
    * 自己定义ClassLoader 

    * @author leizhimin 2009-7-29 22:05:48 
    */ 
    public class MyClassLoader { 
            public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException { 
                    URL url = new URL("file:/E:\projects\testScanner\out\production\testScanner"); 
                    ClassLoader myloader = new URLClassLoader(new URL[]{url}); 
                    Class c = myloader.loadClass("test.Test3"); 
                    System.out.println("----------"); 
                    Test3 t3 = (Test3) c.newInstance(); 
            } 
    }
     
    public class Test3 { 
            static { 
                    System.out.println("Test3的静态初始化块运行了!"); 
            } 
    }
     
    执行后:
    ---------- 
    Test3的静态初始化块运行了! 

    Process finished with exit code 0
     
    能够看出自己定义了ClassLoader myloader = new URLClassLoader(new URL[]{url});已经成功将类Test3载入到内存了,并通过默认构造方法构造了对象Test3 t3 = (Test3) c.newInstance();
     
    有关ClassLoader还有非常重要一点:
    同一个ClassLoader载入的类文件,仅仅有一个Class实例。可是,假设同一个类文件被不同的ClassLoader载入,则会有两份不同的ClassLoader实例(前提是着两个类载入器不能用同样的父类载入器)。
  • 相关阅读:
    SQL Server Management Studio
    uiimage拉伸
    时间空间复杂度
    冒泡选择排序

    插入排序
    快速构建APP
    TTTAtibutedlabel
    Git命令
    适配
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4023195.html
Copyright © 2011-2022 走看看