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
Stringname)
throws
ClassNotFoundException
...
{
try
...
{
return
(Class)
AccessController.doPrivileged(
new
PrivilegedExceptionAction()
...
{
public
Objectrun()
throws
ClassNotFoundException
...
{
//
由类的全限定名得到物理路径
Stringpath
=
name.replace(
'
.
'
,
'
/
'
).concat(
"
.class
"
);
//
从URLsClassPath中取得相应的字节码
Resourceres
=
ucp.getResource(path,
false
);
if
(res
!=
null
)
...
{
try
...
{
//
由Java字节码创建一个Class对象
return
defineClass(name,res);
}
catch
(IOExceptione)
...
{
throw
new
ClassNotFoundException(name,e);
}
}
else
...
{
throw
new
ClassNotFoundException(name);
}
}
}
,acc);
}
catch
(java.security.PrivilegedActionExceptionpae)
...
{
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中的类装载有一个比较深入的理解。
分享到:
相关推荐
5. **示例分析** - `ClassLoaderDemo`可能是包含了一个或多个类加载器实现的Java源代码文件或编译后的字节码文件。 - 它可能演示了如何加载不在标准类路径下的类,或者如何动态加载和执行代码。 - 示例可能还涉及...
【图解版】深入分析ClassLoader类加载工作机制,从原理到JVM的装载过程,详情分析了ClassLoader加载类以及自定义类加载器的过程,不可用于商业用途,如有版权问题,请联系删除!
在Java世界中,JVM(Java Virtual Machine)是运行所有Java程序的核心,它负责解析字节码并执行程序。ClassLoader是JVM中的一...阅读"ClassLoader简析.pdf"文档和分析"Sample.zip"中的代码,将有助于形成更全面的了解。
本文将深入探讨ClassLoader的工作原理,源码分析以及实际应用案例。 首先,我们需要理解ClassLoader的基本概念。ClassLoader是Java中的一个核心组件,它遵循"按需加载"的原则,只有当类被引用时才会加载。Java的类...
### 完整实例分析 在这个实例中,作者通过实现一个自定义的类加载器`myLoader`,并通过类的全名来加载并实例化对象,这与反射机制类似但又有所不同。 #### 自定义类加载器代码解析 ```java public class ...
开发者可以通过日志输出、调试工具等方式监控和分析ClassLoader的行为,以解决诸如类找不到、类加载错误等问题。 总之,ClassLoader是Java程序运行的基础,理解其工作原理及特性对于成为一名合格的Java开发者至关...
1. 源码分析:研究ClassLoader的源码,了解其内部实现,特别是加载类的逻辑。 2. 类加载顺序:观察不同类加载器加载类的顺序,验证双亲委派模型。 3. 动态加载:创建自己的类加载器,尝试动态加载新的类,实现代码的...
这篇博文主要围绕`Classloader`的`loadClass`方法进行深入探讨,并分析了类加载的细节。我们将通过以下几点来详细解析这个主题: 1. **类加载器的层次结构** Java中的类加载器通常遵循双亲委托模型。当一个类加载...
9. **Classloader分析**:对于复杂的类加载问题,`classloader`命令能帮助开发者查看类的加载路径和加载器关系。 10. **AOP增强**:Arthas提供了AOP相关的命令,如`aop`,可以帮助开发者了解和调试AOP切面。 使用...
这样可以增加逆向工程的难度,防止代码被恶意分析或篡改。加密解密的过程通常涉及到对字节码的操作,例如使用AES(高级加密标准)或其他加密算法。 防止类被反编译是另一个安全问题。Java的字节码是可以被工具如JAD...
### Java ClassLoader与ClassPath详解 #### 一、概述 ...通过以上分析可以看出,Java中的类加载器和类路径机制对于程序的运行至关重要。理解这些概念有助于开发者更好地管理和控制程序中的类和资源的加载过程。
在Android系统中,Classloader(类加载器)是至关重要的组件,它负责查找并加载Java类到Dalvik或ART运行时环境。这个测试demo是为了帮助开发者深入理解Android中两种主要的类加载器:DexClassLoader和...
在分析`ClassLoader`源码时,我们需要关注以下几个关键点: 1. 类加载的委托模型:理解`ClassLoader`如何按照委托顺序从父类加载器到子类加载器查找类。 2. 类的定位:如何根据类名找到对应的`.class`文件,特别是...
java面试视频资源探索JVM底层奥秘ClassLoader源码分析与案例讲解提取方式是百度网盘分享地址
分析这些文件可以帮助我们更好地理解C++类加载器的具体实现细节,包括它是如何处理类的加载、解析和实例化的。 总之,类加载器是编程语言中实现动态加载和运行代码的关键组成部分。在Java中,类加载器的双亲委派...
此外,文档中提到的Oracle WebLogic Server 12c支持Maven运行平台,这是为了方便开发人员在持续集成和持续部署(CI/CD)流程中使用,其包含了Classloader分析工具来检测与解决Java类冲突,提供了一种新型的迭代开发...
在Java Web应用中,类加载...通过分析`web_classloader_test`,我们可以学习到如何理解和调试Web应用的类加载问题,以及如何利用类加载器的特性来实现特定的功能。这个示例对于理解Java Web应用的运行机制非常有价值。