`

類裝載過程

阅读更多
ClassLoader 是 Java 虚拟机 (JVM) 的类装载子系统,它负责将 Java 字节码装载到 JVM 中, 并使其成为 JVM 一部分。 JVM 的类动态装载技术能够在运行时刻动态地加载或者替换系统的某些功能模块 , 而不影响系统其他功能模块的正常运行。本文将分析 JVM 中的类装载系统,探讨 JVM 中类装载的原理、实现以及应用。
类装载就是寻找一个类或是一个接口的字节码文件并通过解析该字节码来构造代表这个类或是这个接口的 class 对象的过程。在 Java 中,类装载器把一个类装入 Java 虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:
1.        装载:查找和导入类或接口的字节码;
2.        链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
l        校验:检查导入类或接口的二进制数据的正确性;
l        准备:给类的静态变量分配并初始化存储空间;
l        解析:将符号引用转成直接引用;
3.        初始化:激活类的静态变量的初始化 Java 代码和静态 Java 代码块。
至于在类装载和虚拟机启动的过程中的具体细节和可能会抛出的错误,请参看《 Java 虚拟机规范》以及《深入 Java 虚拟机》。 由于本文的讨论重点不在此就不再多叙述。
JVM 中类的装载是由 ClassLoader 和它的子类来实现的。 Java ClassLoader 是一个重要的 Java 运行时系统组件,它负责在运行时查找和装入 Java 字节码。
在 Java 中, ClassLoader 是一个抽象类,它在包 java.lang 中。可以这样说,只要了解了 ClassLoader 中的一些重要的方法,再结合上面所介绍的 JVM 中类装载的具体的过程,对动态装载类这项技术就有了一个比较大概的掌握,这些重要的方法包括以下几个:
1.        loadCass 方法: loadClass(String name ,boolean resolve) 其中 name 参数指定了 JVM 需要的类的名称 , 该名称以类的全限定名表示,如 Java.lang.Object ; resolve 参数告诉方法是否需要解析类,在初始化类之前,应考虑类解析,并不是所有的类都需要解析,如果 JVM 只需要知道该类是否存在或找出该类的超类,那么就不需要解析。这个方法是 ClassLoader 的入口点。
2.        defineClass 方法  这个方法接受类文件的字节数组并把它转换成 Class 对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时数据结构、校验有效性等等。
3.        findSystemClass 方法  findSystemClass 方法从本地文件系统装入 Java 字节码。它在本地文件系统中寻找类文件,如果存在,就使用 defineClass 将字节数组转换成 Class 对象。当运行 Java 应用程序时 , 这是 JVM 正常装入类的缺省机制。
4.        resolveClass 方法 resolveClass(Class c) 方法解析装入的类,如果该类已经被解析过那么将不做处理。当调用 loadClass 方法时 , 通过它的 resolve 参数决定是否要进行解析。
5.        findLoadedClass 方法  当调用 loadClass 方法装入类时 , 调用 findLoadedClass 方法来查看 ClassLoader 是否已装入这个类 , 如果已装入 , 那么返回 Class 对象 , 否则返回 NULL 。如果强行装载已存在的类 , 将会抛出链接错误。
一般来说,我们实现自定义的 ClassLoader 需要继承抽象类 java.lang.ClassLoader ,其中必须实现的方法是 loadClass(String name) ,对于这个方法需要实现如下操作:
(1) 确认类的名称;
(2) 检查请求要装载的类是否已经被装载;
(3) 检查请求加载的类是否是系统类;
(4) 尝试从类装载器的存储区获取所请求的类;
(5) 在虚拟机中定义所请求的类;
(6) 解析所请求的类;
(7) 返回所请求的类。
所有的 Java 虚拟机都包括一个内置的类装载器,这个内置的类库装载器被称为根装载器 (bootstrap ClassLoader) 。根装载器的特殊之处是它只能够装载基本的 Java 类,如 rt.jar 中的 class 。当应用程序可以使用用户自定义的 ClassLoader 来加载特定 ClassPath 下的类。下面的例子是 JDK5.0 的 URLClassLoader 的实现。
public class URLClassLoader extends SecureClassLoader {
……
protected Class<?> findClass(final String name)
        throws ClassNotFoundException
    {
       try {
           return (Class)
              AccessController.doPrivileged(new PrivilegedExceptionAction() {
                  public Object run() throws ClassNotFoundException {
                                                               // 由类的全限定名得到物理路径
                     String path = name.replace('.', '/').concat(".class");
                                                               // URLs ClassPath 中取得相应的字节码
                     Resource res = ucp.getResource(path, false);
                     if (res != null) {
                         try {
                                                             // Java 字节码创建一个 Class 对象
                            return defineClass(name, res);
                         } catch (IOException e) {
                            throw new ClassNotFoundException(name, e);
                         }
                     } else {
                         throw new ClassNotFoundException(name);
                     }
                  }
              }, acc);
       } catch (java.security.PrivilegedActionException pae) {
           throw (ClassNotFoundException) pae.getException();
       }
    }
……
}
 
2.3           Java 虚拟机的类装载原理
前面我们已经知道,一个 Java 应用程序使用两种类型的类装载器:根装载器 (bootstrap) 和用户定义的装载器 (user-defined) 。根装载器是 Java 虚拟机实现的一部分。根装载器以某种默认的方式将类装入,包括 Java API 的类。在运行期间,一个 Java 程序能使用用户自己定义的类装载器。根装载器是虚拟机固有的一部分,而用户定义的类装载器则不是,它是用 Java 语言写的,被编译成 class 文件之后然后再被装入到虚拟机,并像其它的任何对象一样可以被实例化。 Java 类装载器的体系结构如下所示:
Java 的类装载的体系结构
Java 的类装载模型是一种代理 (delegation) 模型。当 JVM 要求类装载器 CL(ClassLoader) 装载一个类时 ,CL 首先将这个类装载请求转发给他的父装载器。只有当父装载器没有装载并无法装载这个类时 ,CL 才获得装载这个类的机会。这样 , 所有类装载器的代理关系构成了一种树状的关系。树的根是类的根装载器 (bootstrap ClassLoader) , 在 JVM 中它以 "null" 表示。除根装载器以外的类装载器有且仅有一个父装载器。在创建一个装载器时 , 如果没有显式地给出父装载器 , 那么 JVM 将默认系统装载器为其父装载器。 Java 的基本类装载器代理结构如图 2 所示:
Java 类装载的代理结构
下面针对各种类装载器分别进行详细的说明。
根 (Bootstrap) 装载器:该装载器没有父装载器,它是 JVM 实现的一部分,从 sun.boot.class.path 装载运行时库的核心代码。
 
扩展 (Extension) 装载器:继承的父装载器为根装载器,不像根装载器可能与运行时的操作系统有关,这个类装载器是用纯 Java 代码实现的,它从 java.ext.dirs ( 扩展目录 ) 中装载代码。
系统 (System or Application) 装载器:装载器为扩展装载器,我们都知道在安装 JDK 的时候要设置环境变量 (CLASSPATH ) ,这个类装载器就是从 java.class.path(CLASSPATH 环境变量 ) 中装载代码的,它也是用纯 Java 代码实现的,同时还是用户自定义类装载器的缺省父装载器。
小应用程序 (Applet) 装载器:父装载器为系统装载器,它从用户指定的网络上的特定目录装载小应用程序代码。
在设计一个类装载器的时候,应该满足以下两个条件:
对于相同的类名,类装载器所返回的对象应该是同一个类对象
如果类装载器 CL1 将装载类 C 的请求转给类装载器 CL2 ,那么对于以下的类或接口, CL1 和 CL2 应该返回同一个类对象:
a)  S 为 C 的直接超类;
b)  S 为 C 的直接超接口;
c)  S 为 C 的成员变量的类型;
d)  S 为 C 的成员方法或构建器的参数类型;
e)  S 为 C 的成员方法的返回类型。
每个已经装载到 JVM 中的类对象都含有装载它的类装载器的信息。 Class 类的方法 getClassLoader 可以得到装载这个类的类装载器。一个类装载器认识的类包括它的父装载器认识的类和它自己装载的类,由此可见类装载器认识的类是它自己装载的类的超集。注意,我们可以得到类装载器的有关的信息,但是已经装载到 JVM 中的类是不能更改它的类装载器的。
Java 中的类的装载过程也就是代理装载的过程。比如: Web 浏览器中的 JVM 需要装载一个小应用程序 TestApplet 。 JVM 调用小应用程序装载器 ACL(Applet ClassLoader) 来完成装载。 ACL 首先请求它的父装载器 , 即系统装载器装载 TestApplet 是否装载了这个类,由于 TestApplet 不在系统装载器的装载路径中 , 所以系统装载器没有找到这个类 , 也就没有装载成功。接着 ACL 自己装载 TestApplet 。 ACL 通过网络成功地找到了 TestApplet.class 文件并将它导入到了 JVM 中。在装载过程中 , JVM 发现 TestAppet 是从超类 java.applet.Applet 继承的。所以 JVM 再次调用 ACL 来装载 java.applet.Applet 类。 ACL 又再次按上面的顺序装载 Applet 类 , 结果 ACL 发现他的父装载器已经装载了这个类 , 所以 ACL 就直接将这个已经装载的类返回给了 JVM , 完成了 Applet 类的装载。接下来 ,Applet 类的超类也一样处理。最后 , TestApplet 及所有有关的类都装载到了 JVM 中。
2.4           总结
    类的动态装载机制是JVM的一项核心技术, 也是容易被忽视而引起很多误解的地方。本文介绍了JVM中类装载的原理、实现以及应用,分析了ClassLoader的结构、用途以及如何利用自定义的ClassLoader装载并执行Java类,希望能使大家对JVM中的类装载有一个比较深入的理解
分享到:
评论

相关推荐

    Java类装载过程_.docx

    Java 类装载过程是Java虚拟机(JVM)运行的核心机制之一,主要涉及到类从加载到初始化的完整生命周期。这一过程确保了程序的正确运行,并在安全性方面起到关键作用。以下是详细的解释: 1)装载(Loading): 在这...

    Java虚拟机类装载:原理、实现与应用.doc

    类装载过程不仅涉及基本的类查找和导入,还包含了链接、验证、准备、解析和初始化等多个阶段,这些步骤确保了Java程序的安全性和可靠性。 1. **装载过程**: - **装载**:这个阶段主要是寻找指定的类或接口的二...

    jvm类装载器原理

    装载过程包括三个主要步骤:装载、链接和初始化。装载阶段,类装载器依据指定的全限定类名(如`java.lang.Object`)去寻找对应的二进制数据。这个过程可能来源于源代码编译后的字节码文件,也可能来自于网络或其他...

    Java虚拟机类装载的原理及实现

    ### Java虚拟机类装载的原理及实现 #### 引言 Java虚拟机(JVM)作为执行Java程序的核心,承担着解释执行字节码的重要任务。在这个过程中,类的装载是一个关键步骤,它确保了Java程序能够正确无误地运行。类装载...

    java内核研究--可以初步的了解一些java知识

    1. **`loadClass`方法**:此方法是类装载过程的起点,接收类名和一个布尔值作为参数。类名以包表示法给出(例如`java.lang.Object`)。布尔值参数决定了是否需要解析该类。 2. **`defineClass`方法**:将类文件的...

    Java虚拟机类装载:原理、实现与应用

    首先,类装载的过程分为三个主要阶段:装载、链接和初始化。装载阶段涉及寻找并导入类或接口的二进制数据,通常由类装载器(ClassLoader)执行。链接阶段则包括校验、准备和解析,校验确保二进制数据的正确性,准备...

    Java虚拟机类装载.doc

    在装载过程中,JVM会查找和导入类或接口的二进制数据;在链接过程中,JVM会执行校验、准备和解析步骤,其中解析步骤是可以选择的;在初始化过程中,JVM会激活类的静态变量的初始化Java代码和静态Java代码块。 在...

    Java的类装载器和命名空间

    在学习过程中,可能会遇到一些混淆的问题,比如类装载器的委托模型、类的加载顺序以及如何自定义类装载器以加载非标准路径的类。理解这些概念有助于解决实际开发中遇到的类加载问题,例如,当需要加载外部库或者...

    Java虚拟机类装载机制

    装载过程是类装载机制的第一步,主要包括以下子步骤: - **查找**:根据类名(全限定名)在指定的类路径(ClassPath)中找到对应的.class文件。 - **导入**:读取类文件的字节流,通常由类加载器完成。 - **构造**...

    金蝶Apusic应用服务器V6参考b手册b.doc

    当一个类装载请求被提交到某个Classloader时,其默认的类装载过程如下: * 检查这个类有没有被装载过,如果已经装载过,则直接返回; * 调用父Classloader去装载类,如果装载成功直接返回; * 调用自身的装载类的...

    JBoss管理与开发

    这类错误通常发生在类装载过程中,可能是由于类的版本不一致或依赖关系错误等原因导致。 ##### 4.3 IllegalAccessError `java.lang.IllegalAccessError`异常表示某个类试图访问另一个类的私有成员(字段或方法)时...

    深入Java虚拟机

    类装载过程包括加载、验证、准备、解析和初始化五个阶段。加载是找到类的.class文件并将其读入内存;验证确保类文件的正确性和安全性;准备阶段为类的静态变量分配内存并初始化为默认值;解析将符号引用转换为直接...

    Jboss管理与开发核心技术(第三版)

    - **类装载器委派模型**:在典型的类装载过程中,每个类装载器实例都关联有一个父类装载器。当需要查找类或资源时,通常会先委托给父类装载器进行查找,只有当父类装载器找不到时,才会由当前类装载器继续查找。Java...

    利用类装载器动态加载类并启动类

    本文将详细介绍如何通过自定义类装载器来动态加载并启动类的过程,同时介绍一个简单的位移加密算法和非运算加密算法,以及如何使用这些算法进行文件的加密与解密。此方法不仅加深了我们对于Java类装载机制的理解,...

    JVM的相关概念.doc

    类装载过程主要包括加载、连接和初始化三个阶段。 **3.1 加载** 加载阶段主要是通过类装载器将.class文件中的二进制数据读入到内存中,并将其转换成方法区中的数据结构。 **3.2 连接** 连接阶段包括验证、准备和...

    深入JVM内核—原理、诊断与优化视频教程-6. 类装载器

    6. **类的生命周期**:类装载器不仅负责加载类,还参与类的初始化、使用和卸载过程。加载阶段包括找到.class文件并转换为字节码;验证阶段检查字节码是否符合Java规范;准备阶段分配静态变量内存并初始化为默认值;...

    JAVA虚拟机的类装载机制的原理分析与应用研究.pdf

    这一过程涉及到了四个主要角色:类装载器(ClassLoader)、类的二进制表示、验证(Verification)、准备(Preparation)以及初始化(Initialization)。下面我们将逐一解析这些环节。 1. **类装载器**: 类装载器...

    jvm_code深入理解.zip

    了解类装载过程(加载、验证、准备、解析和初始化)对于理解类的生命周期至关重要。 2. 运行时数据区:包括堆内存、栈内存、方法区、本地方法栈和程序计数器。堆内存是所有线程共享的区域,用于存储对象实例;栈...

Global site tag (gtag.js) - Google Analytics