当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构
bootstrap classloader
|
extension classloader
|
system classloader
bootstrap classloader -引导(也称为原始)类加载器,它负责加载Java的核心类。在Sun的JVM中,在执行java的命令中使用-Xbootclasspath选项或使用 – D选项指定sun.boot.class.path系统属性值可以指定附加的类。这个加载器的是非常特殊的,它实际上不是 java.lang.ClassLoader的子类,而是由JVM自身实现的。大家可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toExternalForm());
}
在我的计算机上的结果为
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/dom.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/sax.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xalan-2.3.1.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xercesImpl-2.0.0.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xml-apis.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/endorsed/xsltc.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/rt.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/i18n.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/sunrsasign.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/jsse.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/jce.jar
文件:/C:/j2sdk1.4.1_01/jre/lib/charsets.jar
文件:/C:/j2sdk1.4.1_01/jre/classes
这时大家知道了为什么我们不需要在系统属性CLASSPATH中指定这些类库了吧,因为JVM在启动的时候就自动加载它们了。
extension classloader -扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中JAR的 类包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的 JAR类包对所有的JVM和system classloader都是可见的。在这个实例上调用方法getParent()总是返回空值null,因为引导加载器bootstrap classloader不是一个真正的ClassLoader实例。所以当大家执行以下代码时
System.out.println(System.getProperty("java.ext.dirs"));
ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();
System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());
结果为
C:“j2sdk1.4.1_01“jre“lib“ext
the parent of extension classloader : null
extension classloader是system classloader的parent,而bootstrap classloader是extension classloader的parent,但它不是一个实际的classloader,所以为null。
system classloader -系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或 者 CLASSPATH*作系统属性所指定的JAR类包和类路径。总能通过静态方法ClassLoader.getSystemClassLoader()找 到该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。执行以下代码即可获得
System.out.println(System.getProperty("java.class.path"));
输出结果则为用户在系统属性里面设置的CLASSPATH。
classloader 加载类用的是全盘负责委托机制。所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有 Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入;委托机制则是先让parent(父)类加载器 (而不是super,它与parent classloader类不是继承关系)寻找,只有在parent找不到的时候才从自己的类路径中去寻找。此外类加载还采用了cache机制,也就是如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么我们修改了Class但是必 须重新启动JVM才能生效的原因。
每个ClassLoader加载Class的过程是检测此Class是否载入过(即在cache中是否有此Class),如果有到8,如果没有到2
2.如果parent classloader不存在(没有parent,那parent一定是bootstrap classloader了),到4
3.请求parent classloader载入,如果成功到8,不成功到5
4.请求jvm从bootstrap classloader中载入,如果成功到8
5.寻找Class文件(从与此classloader相关的类路径中寻找)。如果找不到则到7.
6.从文件中载入Class,到8.
7.抛出ClassNotFoundException.
8.返回Class.
其中5.6步我们可以通过覆盖ClassLoader的findClass方法来实现自己的载入策略。甚至覆盖loadClass方法来实现自己的载入过程。
类加载器的顺序是
先是bootstrap classloader,然后是extension classloader,最后才是system classloader。大家会发现加载的Class越是重要的越在靠前面。这样做的原因是出于安全性的考虑,试想如果system classloader“亲自”加载了一个具有破坏性的“java.lang.System”类的后果吧。这种委托机制保证了用户即使具有一个这样的类, 也把它加入到了类路径中,但是它永远不会被载入,因为这个类总是由bootstrap classloader来加载的。大家可以执行一下以下的代码
System.out.println(System.class.getClassLoader());
将会看到结果是null,这就表明java.lang.System是由bootstrap classloader加载的,因为bootstrap classloader不是一个真正的ClassLoader实例,而是由JVM实现的,正如前面已经说过的。
下面就让我们来看看JVM是如何来为我们来建立类加载器的结构的
sun.misc.Launcher,顾名思义,当你执行java命令的时候,JVM会先使用bootstrap classloader载入并初始化一个Launcher,执行下来代码
System.out.println("the Launcher‘s classloader is "+sun.misc.Launcher.getLauncher().getClass().getClassLoader());
结果为
the Launcher‘s classloader is null (因为是用bootstrap classloader加载,所以class loader为null)
Launcher 会根据系统和命令设定初始化好class loader结构,JVM就用它来获得extension classloader和system classloader,并载入所有的需要载入的Class,最后执行java命令指定的带有静态的main方法的Class。extension classloader实际上是sun.misc.Launcher$ExtClassLoader类的一个实例,system classloader实际上是sun.misc.Launcher$AppClassLoader类的一个实例。并且都是 java.net.URLClassLoader的子类。
让我们来看看Launcher初试化的过程的部分代码。
Launcher的部分代码
public class Launcher {
public Launcher() {
ExtClassLoader extclassloader;
try {
//初始化extension classloader
extclassloader = ExtClassLoader.getExtClassLoader();
} catch(IOException ioexception) {
throw new InternalError("Could not create extension class loader");
}
try {
//初始化system classloader,parent是extension classloader
loader = AppClassLoader.getAppClassLoader(extclassloader);
} catch(IOException ioexception1) {
throw new InternalError("Could not create application class loader");
}
//将system classloader设置成当前线程的context classloader(将在后面加以介绍)
Thread.currentThread().setContextClassLoader(loader);
……
}
public ClassLoader getClassLoader() {
//返回system classloader
return loader;
}
}
分享到:
相关推荐
- **getParent方法**:允许ClassLoader获取其父ClassLoader,便于类加载的委托机制。 4. **自定义ClassLoader** 自定义ClassLoader允许开发者控制类的加载方式,例如,可以从网络、数据库或其他非标准源加载类,...
深入理解Equinox不仅包括基本的模块管理和类加载机制,还涉及高级话题,如服务的生命周期管理、事件驱动编程模型、安全性和性能优化等。通过实战项目,开发者可以熟练运用这些高级特性,提高应用程序的健壮性和可...
而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被...
java课堂随笔,我这还有很多,有需要的话可以联系我,我会为大家提供很多的资料
javaclassloader是JVM用于加载类的核心组件,它负责从文件系统、网络或其他来源中加载.class文件(Java字节码),然后将这些字节码转换成JVM可识别的Class对象。在加载过程中,classloader会经历加载、验证、准备、...
返回存储已加载类的类加载器和已加载类的映射。 值得注意的方法: // com.richousrick.metautils.loader.JavaClassLoader// compiles the given map of (Class name -> Source code) into a map of (Class name -> ...
### 大唐JAVA笔试题知识点解析 #### 1. 关键字public、protected、private、final的用法 ...以上知识点涵盖了Java基础、面向对象、多线程、异常处理、Web开发等多个方面,对理解和掌握Java技术栈具有重要意义。
这段代码首先定义了一个名为"MyClass"的Java类,然后使用DC4J的`compile`方法进行编译,并通过`JavaClassLoader`加载生成的类。最后,我们创建了这个类的实例并调用了其`hello`方法,输出了"Hello, Dynamic!"。 ...
首先,要理解C#与Java之间的互操作性是通过Java Native Interface (JNI) 实现的,但直接使用JNI会非常复杂。为简化这个过程,我们可以借助一些第三方工具或库,如IKVM.NET。IKVM.NET是一个开源项目,它实现了Java...