`
g21121
  • 浏览: 687402 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

类加载器(ClassLoader)介绍

 
阅读更多

        Class Loader 名为“类加载器”,用以加载class 文件到Java 虚拟机中。与普通程序不同,class 文件(Java 程序)并不是本地的可执行程序。当运行class 文件时,首先会运行Java 虚拟机(以下简称JVM),然后再将class 文件加载至JVM,最后JVM 通过内部机制将其执行。负责加载class 文件的这部分程序即被称为"Class Loader"。

        默认情况下,一个Java2 JVM通常提供了一个引导类加载器(Bootstrap Class Loader)和两个用户自定义的类装载器:扩展类加载器(Extension Class Loader)和系统/应用类加载器(System Class Loader/Application Class Loader)。

        Bootstrap Class Loader 是负责加载JVM 的核心Java 类(如Java.* javax.*等)。Extension Class Loader 负责加载JRE 的扩展目录类。最后,System Class Loader 负责从系统类路径加载类。除了以上三种Class Loader 之外,用户还可以自定义Class Loader,自定义Class Loader是通过继承java.lang.ClassLoader 类来实现的,下面是几种Class Loader 的详细工作内容:

 

        1).Bootstrap Class Loader
        主要负责JRE_HOME(JRE 所在目录)/lib 目录下的核心API 或-Xbootclasspath 选项指定的jar包装入工作。
        2).Extension Class Loader
        主要负责JRE_HOME/lib/ext 目录下的jar 包或-Djava.ext.dirs 选项指定目录下的jar 包装入工作。
        3).System/Application Class Loader
        主要负责java -classpath 或-Djava.class.path 所指定目录下的class 与jar 包装入工作。
        4).User Custom Class Loader(java.lang.ClassLoader的子类) 
        在程序运行期间, 通过自定义Class Loader 动态加载class 文件, 体现Java 语言动态实时类装入的特性。


        四种Class Loader是逐级向上委托的关系,即4-->3-->2-->1 他们的关系如图所示:


 

        这种向上依赖的关系模型被称作:双亲委托模型(或类似叫法,本文以JVM 实现为例),从Java2 (Java1.2版本)开始,Java引入了此种模型。
        在此模型下,当一个装载器被请求装载某个类时,它首先委托自己的parent 去装载,若parent 能够装载,则返回这个类所对应的Class 对象,若parent 无法装载,则由parent 的请求者自行去装载。
        采用此种模型可以避免重复加载,当上级Class Loader 已经加载了该类的时候,下级Class Loader就没有必要再加载一次。

        我们来看一下JDK中java.lang.ClassLoader类加载类部分的源码:

    protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

        从代码中我们可以清晰的看到,在加载类的过程中会先判断改加载器有没有父类,如果有尝试用父类加载,如果加载不成功则自己加载。

        考虑到安全因素,如果不使用这种委托模式,那我们就可以随时使用自定义Class Loader 重复加载Java 核心类,并修改相应内容。采用此种模式后,例如String,Integer等核心类将在JVM 启动之后自动被加载,用户将无法再去加载相关内容。从而防止了用户用恶意代码代替JVM 系统可靠代码的安全问题。


        注意:此处所指parent 、上级、下级、父、子与Java中的继承不同,是一种逻辑依赖关系。

                   本文以JVM ClassLoader 实现为例未考虑其他特殊情况。

 

        可以通过以下代码来验证一下这个模型:

public class HelloWorld {

    public static void main(String[] args) throws InterruptedException {
        ClassLoader loader = HelloWorld.class.getClassLoader();
        int i=1;
        while (loader != null) {
            System.out.println(i+++":"+loader.getClass().getName());
            loader = loader.getParent();
        }
        System.out.println(loader);
    }
}

 

显示结果:
1:sun.misc.Launcher$AppClassLoader
2:sun.misc.Launcher$ExtClassLoader
null

        1 表示HelloWorld 的类加载器是AppClassLoader

        2 表示AppClassLoader 的类加载器是ExtClassLoder

        null 表示ExtClassLoader 的类加载器是BootstrapClassLoader。

 

        还可以做一个试验,那就是将HelloWOrld 打包成hello.jar 并放到JRE_HOME\lib\ext\ 目录下,然后重新运行程序。

显示结果:
1:sun.misc.Launcher$ExtClassLoader
null

  

        之所以结果不同是因为在JVM 启动之后ExtClassLoder 已经将JRE_HOME\lib\ext\ 下的所有jar包加载,当我们的程序再运行时无需再一次加载,所以它的类加载器是ExtClassLoder,符合双亲委托模型的限制原则。

 

        如果类装载器查找到一个没有装载的类,它会按照下图的流程来装载和链接这个类:



 

 

        每个阶段的描述如下:
        Creation and Loading: 类的信息从文件中获取并且载入到JVM的内存里。
        Verification: 检查读入的结构是否符合Java语言规范以及JVM规范的描述。这是类装载中最复杂的过程,并且花费的时间也是最长的。并且JVM TCK工具的大部分场景的用例也用来测试在装载错误的类的时候是否会出现错误。
        Preparation: 分配一个结构用来存储类信息,这个结构中包含了类中定义的成员变量,方法和接口的信息。
        Resolution: 把这个类的常量池中的所有的符号引用改变成直接引用。
        Initialization: 把类中的变量初始化成合适的值。执行静态初始化程序,把静态变量初始化成指定的值。
 
        使用以下命令可以直接显示更详细的加载过程内容:
java -verbose:class HelloWorld

  

        Bootstrap Class Loader并不是用Java 编写的所以之前结果会显示为null,其他三种Class Loader均采用Java 编写。这四种Class Loader 会在下两节分别详细介绍。

 


        命名空间
        每个Class Loader都维护了一份自己的命名空间,命名空间由所有以此装载器为创始类装载器的类组成,同一个命名空间内不能出现同名的类。不同命名空间的两个类是不可见的,但只要得到类所对应的Class 对象的reference,还是可以访问另一命名空间的类。

        当一个类装载器装载一个类时,它会通过保存在命名空间里的类全局限定名(Fully Qualified Class Name)进行搜索来检测这个类是否已经被加载了。如果两个类的全局限定名是一样的,但是如果命名空间不一样的话,那么它们还是不同的类。不同的命名空间表示class 被不同的类装载器装载。

 

        二进制名称
        按照《Java Language Specification》的定义,任何作为String 类型参数传递给ClassLoader 中方法的类名称都必须是一个二进制名称。

有效类名称的示例包括:
"java.lang.String"
"javax.swing.JSpinner$DefaultEditor"
"java.security.KeyStore$Builder$FileBuilder$1"
"java.net.URLClassLoader$3$1"

 

 

         运行时包(runtime package)
        由同一类装载器定义装载的属于相同包的类组成了运行时包,决定两个类是不是属于同一个运行时包,不仅要看它们的包名是否相同,还要看的定义类装载器是否相同。只有属于同一运行时包的类才能互相访问包可见的类和成员。这样的限制避免了用户自己的代码冒充核心类库的类访问核心类库包可见成员的情况。

        假设用户自己定义了一个类java.lang.Integer2,并用用户自定义的类装载器装载,由于java.lang.Integer2 和核心类库java.lang.* 有着不同的装载器装载,它们属于不同的运行时包,所以java.lang.Integer2 不能访问核心类库java.lang 中类的包可见的成员。

 

        总结:
        双亲委托模型加强了Java 的安全与效率,命名空间允许不同空间的类的互相访问,运行时包增加了对包可见成员的保护。

        下一节将讲解JVM 预先实现的这三种Class Loader。

  • 大小: 31.4 KB
  • 大小: 24 KB
8
6
分享到:
评论
4 楼 Motte2010 2013-08-22  
很多都这样理解classLoader的4级委托,实际上只有3级,Bootstrap Class Loader并不会参与,Bootstrap Class Loader只会加载JVM自身需要的类,由JVM自身管理。加载的方式也和 ExtClassLoader,AppClassLoader不同。
3 楼 Rambing 2013-08-21  
楼主你的文章中有2点是不特别准确的地方,请仔细核对。
1)类的双亲委托机制或者成为Parent-first的加载模式,并不是所有的类加载都是遵循该模式的。例如在应用服务器的实现中,Tomcat的WebappClassLoader采用了一种Child-first的模式。
2)Resolving: 把这个类的常量池中的所有的符号引用改变成直接引用。——这一点也要看具体的JVM的实现。当前的JVM更多的是只在一个形式引用真正需要的时候(也就是说在所引用类的对象被创建)才进行解析。
2 楼 lvwenwen 2013-08-21  
对ClassLoader讲解的很清楚?
1 楼 kingsfighter 2013-08-19  
不错的文章,对ClassLoader讲解的很清楚

相关推荐

    java应用程序类加载器,ClassLoader for java Application

    java应用程序类加载器(ClassLoader for java Application),类似exe4j, 方便启动java程序, 配置灵活,支持多平台选择性配置

    ClassLoader类加载器

    ClassLoader的API使用和自定义

    JAVA ClassLoader 讲解 (类加载器)

    ClassLoader类加载器讲解,理解JAVA类加载机制

    ClassLoader类加载机制

    类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java ...

    Java类加载器(ClassLoader)1

    如果户创建的JAR放在此录下,也会动由扩展类加载器加载.应程序类加载器(系统类加载器,Application ClassLoader)java语编写,由sun.

    Java类加载器ClassLoader用法解析

    主要介绍了Java类加载器ClassLoader用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    类加载器,classload

    关于类加载器的 上课ppt -java虚拟机自带的加载器 根类加载器(Bootstrap) c++写的看不到扩展类加载器(extension) 系统类加载器(System) AppClassLoad 用户自定义的类加载器 Java.lang.ClassLoader的子类

    java类加载器实例

    类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一。它使得 Java 类可以被动态加载到 Java 虚拟机中并执行。类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的。Java ...

    深入java虚拟机(七)深入源码看java类加载器ClassLoader 1

    摘要视图订阅曹胜欢欢迎关注微信账号:java那些事:csh624366188.每天一篇java相关的文章登录 | 注册Java程序员从笨鸟到菜鸟(81)3054

    【图解版】深入分析ClassLoader类加载工作机制

    【图解版】深入分析ClassLoader类加载工作机制,从原理到JVM的装载过程,详情分析了ClassLoader加载类以及自定义类加载器的过程,不可用于商业用途,如有版权问题,请联系删除!

    3-7Tomcat中自定义类加载器的使用与源码实现(1).mp4

    3-7Tomcat中自定义类加载器的使用与源码实现(1).mp4

    classloader类加载器_基于java类的加载方式详解

    下面小编就为大家带来一篇classloader类加载器_基于java类的加载方式详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    java的ClassLoader类加载器机制

    jvm运行的过程中,需要载入类,而类的加载需要类加载器,本文章提供了java的类加载器的工作原理。可以使读者更加理解jvm的运行机制。

    掌握Java类加载器

    类加载器是Java最强大的特征之一。但是开发者常常忘记类加载组件。类加载器是在运行时负责寻找和加载类文件的类。Java允许使用不同的类加载器,甚至自定义的类加载器。类加载器从源文件(通常是.class 或 .jar文件)...

    自定义类加载器

    简单的自定义类加载器问候世界hello word,基于磁盘的ClassLoader

    java类加载器

    ClassLoader 三种类加载方式 Boostrap Extenxsion 以及Application ClassLoad分别适用的场景

    Java类加载机制与反射-PPT

    Java的类加载机制:加载,连接,初始化。JAVA类加载器: Bootstrap ClassLoader : 根类加载器, Extension ClassLoader: 扩展类加载器, System ClassLoader : 系统类加载器, Java反射

    类加载器文件

    JDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK)。 SE(J2SE),standard edition,标准版,是我们通常用的一个版本,从JDK 5.0开始,改名为Java SE。

    java 类加载器 双亲委派 根加载器、扩展类加载器、系统类加载器

    类加载器分为根加载器(bootstrap classloader)、扩展类加载器(ext classloader)、系统类加载器(system classloader)、自定义类加载器(通常继承java.net.URLClassLoader,重写findClass()),它们的关系通常...

    classloader的简单实现

    一个开源的Cplusplus类加载器,基于它实现了一个简单的例子,见我写的classloader的文章。

Global site tag (gtag.js) - Google Analytics