`
xinklabi
  • 浏览: 1591803 次
  • 性别: Icon_minigender_1
  • 来自: 吉林
文章分类
社区版块
存档分类
最新评论

java类加载器原理1

    博客分类:
  • Java
 
阅读更多

 

这篇文章试图解决下面一些问题:
类加载原理
引导类加载器,扩展类加载器和系统类加载器
如何知道某个类是哪个类加载器加载的
如何得到系统类加载器加载了那些类

首先我们要分析类加载原理,java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器)

 

引导类加载器负责加载jdk中的系统类,这种类加载器都是用c语言实现的,在java程序中没有办法获得这个类加载器,对于java程序是一个概念而已,基本上不用考虑它的存在,像String,Integer这样的类都是由引导类加载器加载器的.
扩展类加载器负责加载标准扩展类,一般使用java实现,这是一个真正的java类加载器,负责加载jre/lib/ext中的类,和普通的类加载器一样,其实这个类加载器对我们来说也不是很重要,我们可以通过java程序获得这个类加载器。
系统类加载器,加载第一个应用类的加载器(其实这个定义并不准确,下面你将会看到),也就是执行java MainClass 时加载MainClass的加载器,这个加载器使用java实现,使用的很广泛,负责加载classpath中指定的类。

类加载器之间有一定的关系(父子关系),我们可以认为扩展类加载器的父加载器是引导类加载器(当然不这样认为也是可以的,因为引导类加载器表现在java中就是一个null),不过系统类加载器的父加载器一定是扩展类加载器,类加载器在加载类的时候会先给父加载器一个机会,只有父加载器无法加载时才会自己去加载。

我们无法获得引导类加载器,因为它是使用c实现的,而且使用引导类加载器加载的类通过getClassLoader方法返回的是null.所以无法直接操作引导类加载器,但是我们可以根据Class.getClassLoader方法是否为null判断这个类是不是引导类加载器加载的,可以通过下面的方法获得引导类加载器加载的类路径(每个jar包或者文件夹对应了一个URL);
sun.misc.Launcher.getBootstrapClassPath().getURLs()
你可以直接在你的main函数中输出就可以了
System.out.println(java.util.Arrays.asList(sun.misc.Launcher.getBootstrapClassPath().getURLs()).toString());
得到的结果是:
[file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/rt.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/i18n.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/sunrsasign.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/jsse.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/jce.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/charsets.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/classes]

其实我们是可以指定引导类加载器的类路径的,java提供了一个-Xbootclasspath参数,不过这个参数不是标准参数。
java -Xbootclasspath:
运行时指定引导类加载器的加载路径(jar文件或者目录)
java -Xbootclasspath/p:
和上面的相同,不过把这个路径放到原来的路径前面
java -Xbootclasspath/a:
这个就是在原引导类路径后面添加类路径。
上面我们有提过加载第一个应用类未必就是系统加载器。
如果我把这个应用类的路径放到引导类路径中,它将会被引导类加载器加载,大致这样
java -Xbootclasspath/a:myjar.jar MainClass
如果MainClassmyjar.jar,那么这个类将会被引导类加载器加载。
如果希望看详情,使用-verbose参数,为了看的更清楚,使用重定向,大致为(windows)
java -verbose -Xbootclasspath/a:myjar.jar MainClass -> C:\out.txt
通过这个参数我们可以实现自己的系统类,比如替换掉java.lang.Object的实现,自己可以扩展
一些方法,不过这样做似乎没有好处,因为那就不是标准了。

我们最关心的还是系统类加载器,一般都认为系统类加载器是加载应用程序第一个类的加载器,
也就是java MainClass命令中加载MainClass的类加载器,这种说法虽然不是很严谨,但基本上还是可以这样认为的,因为我们很少会改变引导类加载器和扩展类加载器的默认行为。应该说系统类加载器负责加载classpath路径中的而且没有被扩展类加载器加载的类(当然也包括引导类加载器加载的)。如果classpath中有这个类,但是这个类也在扩展类加载器的类路径,那么系统类加载器将没有机会加载它。
我们很少改变扩展类加载器的行为,所以一般你自己定义的类都是系统类加载器加载器的。

获得系统类加载器非常简单,假设MyClass是你定义的一个类
MyClass.class.getClassLoader()
返回的就是系统类加载器,当然这种方法无法保证绝对正确,我们可以使用更简单而且一定正确的方式:
ClassLoader.getSystemClassLoader()
获得系统类加载器。我们知道ClassLoader是一个抽象类,所以系统类加载器肯定是ClassLoader的一个子类实现。我们来看看它是什么
ClassLoader.getSystemClassLoader().getClass();
结果是class sun.misc.Lancher$AppClassLoader 
可以看出这是sun的一个实现,从名字可以看出是一个内部类,目前我也没有看到这个源代码,似乎还不是很清晰:
我们在看看它的父类是什么:
ClassLoader.getSystemClassLoader().getClass().getSuperclass();
结果是:class java.net.URLClassLoader
这个是j2se的标准类,它的父类是SecureClassLoader,SecureClassLoader是继承ClassLoader的。
现在整个关系应该很清楚,我们会看到几乎所有的ClassLoader实现都是继承URLClassLoader的。
因为系统类加载器是非常重要的,而且是我们可以直接控制的,所以我们后面还会介绍,不过先来看一下扩展类
加载器以及它们之间的关系。

扩展类加载器似乎是一个不起眼的角色,它负责加载java的标准扩展(jre/lib/ext目录下的所有jar),它其实就是一个普通的加载器,看得见摸得着的。
首先的问题是怎么知道扩展类加载器在哪里?
的确没有直接途径获得扩展类加载器,但是我们知道它是系统类加载器的父加载器,我们已经很容易的获得系统类加载器了,所以我们可以间接的获得扩展类加载器:
ClassLoader.getSystemClassLoader().getParent().getClass();
其实是通过系统类加载器间接的获得了扩展类加载器,看看是什么东西:
结果是:class sun.misc.Launcher$ExtClassLoader
这个类和系统类加载器一样是一个内部类,而且定义在同一个类中。
同样看看它的父类是什么:
ClassLoader.getSystemClassLoader().getParent().getClass().getSuperclass();
可以看出结果也是class java.net.URLClassLoader
扩展类加载jre/lib/ext目录下的所有类,包括jar,目录下的所有类(目录名不一定要classes).
现在可以回答上面的问题了,你写一个HelloWorld,放到jre/lib/ext/下的某个目录
比如 jre/lib/ext/myclass/HelloWorld.class
然后在你classpath也设置一份到这个类的路径,结果执行java HelloWorld时,这个类是被扩展类加载器加载器的,可以这样证明
public static void main(String[] args){
System.out.println("loaded by"+HelloWorld.class.getClassLoader().getClass());
System.out.println("Hello World");
}
结果可以得到class sun.misc.Launcher$ExtClassLoader
当然如果你把jre/lib/extmyclass这个目录删除,仍然可以运行,但是这样结果是
class sun.misc.Lancher$AppClassLoader
如果你不知道这个过程的话,假设在你扩展类路径下有一份classpath中的拷贝,或者是比较低的版本,当你使用新的版本时会发现没有起作用,知道这个过程你就不会觉得奇怪了。另外就是两个不同的类加载器是可以加载一个同名的类的,也就是说虽然扩展类加载器加载了某个类,系统类加载器是可以加载自己的版本的,
但是现有的实现都没有这样做,ClassLoader中的方法是会请求父类加载器先加载的,如果你自己定义类加载器完全可以修改这种默认行为,甚至可以让他没有父加载器。

这里给出一个方法如何获得扩展类加载器加载的路径:
    String path=System.getProperty("java.ext.dirs");
        File dir=new File(path);
        if(!dir.exists()||!dir.isDirectory()){
            return Collections.EMPTY_LIST;
        }
        File[] jars=dir.listFiles();
        URL[] urls=new URL[jars.length];
        for(int i=0;i<jars.length;i++){
            urls[i]=sun.misc.URLClassPath.pathToURLs(jars[i].getAbsolutePath())[0];
        }
        return Arrays.asList(urls);

对于扩展类加载器我们基本上不会去关心,也很少把你自己的jar放到扩展路径,大部分情况下我们都感觉不到它的存在,当然如果你一定要放到这个目录下,一定要知道这个过程,它会优先于classpath中的类。

现在我们应该很清楚知道某个类是哪个加载器加载的,并且知道为什么是它加载的,如果要在运行时获得某个类的类加载器,直接使用ClassgetClassLoader()方法就可以了。

用户定义的类一般都是系统类加载器加载的,我们很少直接使用类加载器加载类,我们甚至很少自己加载类。
因为类在使用时会被自动加载,我们用到某个类时该类会被自动加载,比如new A()会导致类A自动被加载,不过这种加载只发生一次。
我们也可以使用系统类加载器手动加载类,ClassLoader提供了这个接口
ClassLoader.getSystemClassLoader().loadClass("classFullName");
这就很明确的指定了使用系统类加载器加载指定的类,但是如果该类能够被扩展类加载器加载,系统类加载器还是不会有机会的。
我们最常用的还是使用Class.forName加载使用的类,这种方式没有指定某个特定的ClassLoader,会使用调用类的ClassLoader
也就是说调用这个方法的类的类加载器将会用于加载这个类。比如在类A中使用Class.forName加载类B,那么加载类A的类加载器将会用于加载类B,这样两个类的类加载器是同一个。

最后讨论一下如何获得某个类加载器加载了哪些类,这个似乎有一定的使用价值,可以看出哪些类被加载了。其实这个也不是很难,因为ClassLoader中有一个classes成员变量就是用来保存类加载器加载的类列表,而且有一个方法
void addClass(Class c) { classes.addElement(c);}
这个方法被JVM调用。
我们只要利用反射获得classes这个值就可以了,不过classes声明为private,我们需要修改它的访问权限(没有安全管理器时很容易做到)
classes = ClassLoader.class.getDeclaredField("classes");
classes.setAccessible(true);
List ret=(List) classes.get(cl); //classes
是一个Vector
可惜的是对于引导类加载器没有办法获得加载的类,因为它是了的,java中很难控制了。

 

 

 

分享到:
评论

相关推荐

    Java类加载器原理

    Java 类加载器原理 Java 类加载器是Java虚拟机(JVM)的核心组成部分,它负责将类的字节码加载到内存中并转换为可执行的Java类。类加载器的作用不仅仅是加载类,还包括确保类的唯一性,避免重复加载,并且遵循特定...

    java类加载器

    ### Java 类加载器详解 #### 一、类加载器概述 在Java中,类加载器(Class ...通过理解和掌握类加载器的工作原理及其不同类型的加载器,可以帮助开发者更好地管理Java应用程序的依赖关系,提高程序的性能和可维护性。

    Java类加载原理解析

    1. 启动(Bootstrap)类加载器:这是最基础的类加载器,由本地代码实现,负责加载JRE目录下的`&lt;Java_Runtime_Home&gt;/lib`路径中的类库。 2. 扩展(Extension)类加载器:由Sun的`ExtClassLoader`实现,加载`&lt;Java_...

    java类加载原理分析

    Java 类加载原理是Java运行时环境中的核心机制之一,它涉及到类的生命周期、类加载器的层次结构以及类的查找和加载过程。了解这一机制对于深入理解Java平台的工作原理至关重要,尤其是在开发自定义类加载器或者处理...

    java 类加载器 加密

    Java 类加载器是Java运行时环境的一个重要组成部分,它的主要职责是将编译后的字节码(.class文件)加载到JVM中,使得程序能够运行。类加载器的机制保证了类的唯一性,同时也提供了灵活性,允许我们自定义加载逻辑。...

    Java类加载器.pdf

    1. **Bootstrap ClassLoader(启动类加载器)**:这是最顶层的类加载器,负责加载核心库(如`rt.jar`),它是由本地代码实现的。 2. **Extension ClassLoader(扩展类加载器)**:它加载位于`JAVA_HOME/lib/ext`目录...

    Java的类加载器

    Java的类加载器是Java虚拟机(JVM)的核心组件之一,它负责将类的字节码文件从文件系统或网络中加载到JVM中,然后进行校验、解析和初始化,使得Java程序能够运行。类加载器的概念是Java动态加载和运行时类重定义的...

    类加载器文件

    ### 类加载器详解 #### 一、类加载器概述 **类加载器(ClassLoader)**是Java虚拟机(JVM)中的一个重要组成部分,它负责将编译好的`.class`文件加载到JVM中...理解类加载器的工作原理对于深入掌握Java编程有着重要意义。

    类加载器(java)

    总之,Java的类加载器是理解JVM工作原理的关键一环,其背后涉及到了许多高级特性,如设计模式、动态加载、模块化以及安全性。通过对这些知识点的深入学习,开发者可以更好地控制和优化应用程序的行为。

    java类加载机制原理与实现

    1. 启动类加载器(Bootstrap ClassLoader):负责加载 Java 的核心类库,例如 java.lang.String、java.util.ArrayList 等。 2. 扩展类加载器(Extension ClassLoader):负责加载 Java 的扩展类库,例如 javax.xml ...

    深入解析Java类加载器及其工作机制

    理解类加载器的工作原理对于深入掌握 Java 语言及其运行机制至关重要。本文将详细探讨 Java 类加载器的概念、类型以及工作流程。 类加载器是 Java 运行时环境中的核心组件,它们不仅负责类的加载,还维护了 Java ...

    JAVA的类加载器的工作原理.pdf

    JAVA类加载器的工作原理 Java 类加载器是 Java 虚拟机(JVM)中的一种机制,用于加载 Java 类文件。 Java 类加载器的作用是将类文件加载到 JVM 中,以便 JVM 执行类文件中的字节码。Java 类加载器基于三个机制:...

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

    理解类加载器的工作原理对于深入学习Java编程至关重要。在Java中,类加载器按照层次结构进行组织,包括引导类加载器、扩展类加载器和应用程序类加载器,每个都有特定的职责。 1. **引导类加载器(Bootstrap ...

    JAVA的类加载器的工作原理.docx

    JAVA类加载器的工作原理 JAVA类加载器是JAVA虚拟机(JVM)的一部分,负责加载类文件,并将其转换为JVM可以执行的字节码。类加载器基于三个机制:委托、可见性和单一性。 委托机制是指将加载一个类的请求交给父类...

    java类加载器1

    Java 类加载器是Java语言的核心特性之一,它允许程序在运行时动态地加载类到Java虚拟机(JVM)中。这一特性对于实现模块化、插件化和动态部署等功能至关重要。类加载器的设计遵循“分层加载”原则,形成了一个树状的...

    深入探讨 Java 类加载器

    1. **加载**:加载阶段,类加载器找到指定类的字节码,这可以通过读取磁盘上的.class文件或从网络上获取。 2. **验证**:确保字节码符合Java语言规范,不会对JVM造成危害。 3. **准备**:分配内存并初始化静态变量的...

    Java类加载原理解析文档

    1. 启动(Bootstrap)类加载器:这是最基础的类加载器,由本地代码实现,负责加载`&lt;Java_Runtime_Home&gt;/lib`目录下的核心类库。由于涉及虚拟机内部实现,开发者无法直接访问。 2. 扩展(Extension)类加载器:由`...

Global site tag (gtag.js) - Google Analytics