`
g21121
  • 浏览: 694725 次
  • 性别: 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

    1. **引导类加载器(Bootstrap ClassLoader)**:这是最基础的类加载器,由JVM本身实现,主要负责加载JDK核心库,如rt.jar中的类,这些类通常位于JRE的lib目录下。 2. **扩展类加载器(Extension ClassLoader)**:由...

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

    《深入Java虚拟机(七)深入源码看java类加载器ClassLoader》 Java类加载器(ClassLoader)在Java运行环境中扮演着至关重要的角色。它负责将类的字节码加载到Java虚拟机(JVM)中,使得程序能够运行。ClassLoader是...

    ClassLoader类加载器

    3. Application ClassLoader:也称为系统类加载器,负责加载用户类路径`-cp`或`-classpath`指定的所有类。 当一个类被加载时,如果它的父类加载器无法加载该类,那么会将任务委派给子类加载器。这就是著名的"委托...

    JAVA ClassLoader 讲解 (类加载器)

    ### Java ClassLoader (类加载器)详解 #### 一、教程提示 如果你正在查看这份文档,在线版中你可以点击下面的任何主题直接跳转到相应的部分。 1. **教程提示** 2. **介绍** 3. **类加载器结构** 4. **编译类加载...

    Java类加载器(ClassLoader)1

    Java类加载器分为三种主要类型:引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassLoader,也称为系统类加载器)。它们共同工作,确保了Java...

    java的ClassLoader类加载器机制

    在 JVM 运行过程中,类加载器会形成一个层次结构,包括引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和系统类加载器(System ClassLoader)。 引导类加载器(Bootstrap ...

    java类加载器

    System.out.println("JDK提供的Object类由哪个类加载器加载--&gt; " + classLoader); } ``` 从上述代码中可以看出,系统的类加载器是`AppClassLoader`,它的父加载器是`ExtClassLoader`,而`ExtClassLoader`的父加载...

    类加载器文件

    **类加载器(ClassLoader)**是Java虚拟机(JVM)中的一个重要组成部分,它负责将编译好的`.class`文件加载到JVM中,使得这些类可以在Java环境中运行。类加载器不仅能够加载类,还能够根据不同的需求定制加载方式,如从...

    类加载机制ClassLoader

    这个过程可以由系统类加载器、自定义类加载器或者父类加载器完成。加载过程中,如果类已经被加载,那么就不会再次加载。 2. **验证**: 验证是确保.class文件的字节流符合JVM规范,防止恶意代码对系统的破坏。验证...

    ClassLoader类加载机制和原理详解

    当一个类被加载时,它首先会尝试由当前线程的Context ClassLoader进行加载,如果该类加载器无法加载,则向上委托给父类加载器,直至Bootstrap ClassLoader。这种设计可以避免类的重复加载,同时保证核心库的稳定性和...

    Java 类加载机制 ClassLoader Class.forName.pdf

    3. **Application ClassLoader** (应用程序类加载器): 也称为系统类加载器,负责加载用户定义的类路径上的类,通常通过`-classpath`或`-cp`命令行参数指定。 这三种类加载器之间存在层级关系,形成了所谓的“双亲...

    tomcat 类加载机制 —— ClassLoader

    4. **Webapp ClassLoader**:每个Web应用都有自己的类加载器,负责加载对应Web应用的WEB-INF/classes和WEB-INF/lib目录下的类和JAR文件。这种设计确保了不同Web应用间的类隔离,防止类冲突。 5. **Catalina ...

    自定义类加载器实现自定义加载

    - Java中的类加载器采用双亲委派模型,即一个类首先由启动类加载器Bootstrap ClassLoader尝试加载,如果找不到则交给扩展类加载器Extension ClassLoader,再找不到则交由应用程序类加载器AppClassLoader,最后如果...

    java自定义类加载classloader文档,包括代码

    上述代码展示了如何创建一个自定义类加载器`MyClassLoader`,该类继承自`java.lang.ClassLoader`。`MyClassLoader`的主要功能是从文件系统中加载指定类的二进制数据。 - **构造函数**:接受一个父类加载器和基础...

    ClassLoader类加载机制

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

    tomcat类加载器

    类加载器遵循双亲委派模型,这意味着当一个类加载器尝试加载类时,它首先会将请求委托给其父类加载器,直到到达顶层的Bootstrap ClassLoader,如果父类加载器无法找到该类,子类加载器才会尝试自己加载。 在Tomcat...

    Java的类加载器

    以下是对类加载器的详细介绍: 1. 类加载机制: Java的类加载分为加载、验证、准备、解析和初始化五个阶段。加载阶段是类加载器的主要工作,它负责找到类的二进制表示并将其转化为Class对象。加载器按照双亲委派...

    Java类加载器ClassLoader用法解析

    Java 类加载器 ClassLoader 用法解析 Java 中的类加载器(ClassLoader)是一种机制,负责将类从文件系统、JAR 文件或网络等来源加载到 Java 虚拟机中。类加载器的作用是将类的二进制数据加载到内存中,并为其创建一...

    classloader的简单实现

    在Java编程语言中,类加载器(ClassLoader)是一个至关重要的组件,负责将类的字节码从磁盘、网络或其他数据源加载到JVM(Java虚拟机)中,然后将其转换为运行时的类对象。这个过程是Java程序动态加载和运行的基础。...

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

    Java类加载器(ClassLoader)是Java虚拟机(JVM)中的一个重要组成部分,用于将Java类文件加载到JVM中,以便能够执行Java程序。在Java中,类加载器的设计采用了一种称为“双亲委派模式”(Parent Delegation Model)...

Global site tag (gtag.js) - Google Analytics