搜索
简帛阁>技术文章>ClassLoader工作机制

ClassLoader工作机制

本人在研究生阶段做JavaWeb开发时,当把项目发布到Tomcat上运行时,曾遇到过一个神奇的问题,这个问题由“CLassCastException”异常引起,当然这个异常很常见,其实我要说的神奇之处在于------我明明使用的是这个类本身new的对象,然后编译没有问题,但运行时却抛出这个异常。类似这样:MyClass mc=new MyClass(),异常就发生在这一句,是不是很奇怪了。为什么会类型转换失败?

一般来说,产生ClassCastException的主要原因有以下两点:
        1.类不兼容,当应用程序代码尝试将某一对象转换为某一子类时,如果该对象并非该子类的实例,JVM就会抛出ClassCastException异常。
        2.类兼容,但加载时使用了不同的ClassLoader。而我曾碰到的问题就源于第二种情况,这种情况一般不会引起注意,当发生这种情况时,又让人委迷茫,因为我已经确信是用这个类创建的对象!原因就是因为同一个类使用了不同的ClassLoader(Tomcat容器与JDK类加载器的冲突)导致的

鉴于这种情况的隐蔽性,今天特此分享一下ClassLoader的工作机制和原理方面的问题

Java是具有动态性,什么是动态性?有个最直观的例子:windows系统的即插即用,支持即插即用的设备可以在系统不重新启动的情况下既可以热把插使用。而java的动态性表现在:我们的程序可以不用全盘的重新编译就能对程序某部分进行更新,C#也和java一样具有动态性,而且它的这种动态性表现更为直观:直接生成windows的动态连接库文件------dll文件。而java生成的是class文件,class是怎么实现动态性的了,这个时候就全靠我们今天的主角:java的类加载器。

我们都知道所有的java类都是继承了object这个类,在object这个类中有一个方法:getclass(),这个方法返回的是一个Class类对象。

一旦一个类被载入JVM中,同一个类就不会被再次载入了(切记,同一个类)。这里存在一个问题就是什么是“同一个类”?正如一个对象有一个具体的状态,即标识,一个对象始终和其代码(类)相关联。同理,载入JVM的类也应该有一个具体的标识,我们知道:在JAVA中,一个类用其完全匹配类名(fullyqualifiedclassname)作为标识,这里指的完全匹配类名是包名和类名。不过在JVM中一个类是用其全名再附加上一个加载类ClassLoader的实例作为唯一标识。因此,如果一个名为Pg的包中,有一个名为Cl的类,被类加载器KlassLoader的一个实例对象kl1加载,生成Cl的对象,即C1.class(这里指类,而非对象)在JVM中表示为(Cl,Pg,kl1)。这意味着两个类加载器的实例(Cl,Pg,kl1)和(Cl,Pg,kl2)是不同的,被它们所加载的类也因此完全不同,互不兼容的。

在java中每个类都是由某个类加载器的实体来载入的,因此在Class类的实体中,都会有字段记录载入它的类加载器的实体(当为null时,其实是指BootstrapClassLoader)。在java类加载器中除了引导类加载器(既BootstrapClassLoader),所有的类加载器都有一个父类加载器(因为他们本身自己就是java类)。而类的加载机制是遵循一种委托模式:当类加载器有加载类的需求时,会先请求其Parent加载(依次递归),如果在其父加载器树中都没有成功加载该类,则由当前类加载器加载。

java的类加载器分为以下几种:

1,BootstrapClassLoader,用C++实现,一切的开始,是所有类加载器的最终父加载器。负责将一些关键的Java类,如java.lang.Object和其他一些运行时代码先加载进内存中。

2,ExtClassLoader,用java实现,是Launcher.java的内部类,编译后的名字为:Launcher$ExtClassLoader.class。此类由BootstrapClassLoader加载,但由于BootstrapClassLoader已经脱离了java体系(c++),所以Launcher$ExtClassLoader.class的Parent(父加载器)被设置为null;它用于装载Java运行环境扩展包(jre/lib/ext)中的类,而且一旦建立其加载的路径将不再改变。

3,AppClassLoader,用java实现,也是是Launcher.java的内部类,编译后的名字为:Launcher$AppClassLoader.class。AppClassLoader是当BootstrapClassLoader加载完ExtClassLoader后,再被BootstrapClassLoader加载。所以ExtClassLoader和AppClassLoader都是被BootstrapClassLoader加载,但AppClassLoader的Parent被设置为ExtClassLoader。可见Parent和由哪个类加载器来加载不一定是对应的。

个类装载器是我们经常使用的,可以调用ClassLoader.getSystemClassLoader()来获得,如果程序中没有使用类装载器相关操作设定或者自定义新的类装载器,那么我们编写的所有java类都会由它来装载。而它的查找区域就是我们常常说到的Classpath,一旦建立其加载路径也不再改变。

4,ClassLoader:一般我们自定义的ClassLoader从ClassLoader类继承而来。比如:URLClassloader是ClassLoader的一个子类,而URLClassloader也是ExtClassLoader和AppClassLoader的父类(注意不是父加载器)。

让我们先回到java的动态性,实现java的动态性有两种方法类型:一种是隐式,另一种是显式。什么是隐式?new 这个关键字我们都认识,当我们用其将类实例化时(即将对象载入),这种就是隐式!我们再来看显式的实现方法,一种可以由java.long.Class里面的forName()方法将类实例化,其中也用到了类加载器,另一种是由也就是直接用类加载器ClassLoader来实现。

ClassLoader一些重要的方法

A) 方法 loadClass

ClassLoader.loadClass() 是 ClassLoader 的入口点。该方法的定义如下:

Class loadClass( String name, boolean resolve );

参数name JVM 需要的类的名称,如Person 或 java.lang.Object。

参数 resolve 参数告诉方法是否需要解析类。在准备执行类之前,应考虑类解析。并不总是需要解析。如果 JVM 只需要知道该类是否存在或找出该类的超类,那么就不需要解析。

B) 方法 defineClass

defineClass 方法是 ClassLoader 的主要诀窍。该方法接受由原始字节组成的数组并把它转换成 Class 对象。原始数组包含如从文件系统或网络装入的数据。defineClass 管理 JVM 的许多复杂、神秘和倚赖于实现的方面 -- 它把字节码分析成运行时数据结构、校验有效性等等。不必担心,您无需亲自编写它。事实上,即使您想要这么做也不能覆盖它,因为它已被标记成final的。

C) 方法 findSystemClass

findSystemClass 方法从本地文件系统装入文件。它在本地文件系统中寻找类文件,如果存在,就使用 defineClass 将原始字节转换成 Class对象,以将该文件转换成类。当运行 Java 应用程序时,这是 JVM 正常装入类的缺省机制。(Java 2 中 ClassLoader 的变动提供了关于 Java 版本 1.2 这个过程变动的详细信息。)对于定制的 ClassLoader,只有在尝试其它方法装入类之后,再使用 findSystemClass。原因很简单:ClassLoader 是负责执行装入类的特殊步骤,不是负责所有类。例如,即使 ClassLoader 从远程的 Web 站点装入了某些类,仍然需要在本地机器上装入大量的基本 Java 库。而这些类不是我们所关心的,所以要 JVM 以缺省方式装入它们:从本地文件系统。这就是findSystemClass 的用途。

D) 方法 resolveClass

正如前面所提到的,可以不完全地(不带解析)装入类,也可以完全地(带解析)装入类。当编写我们自己的 loadClass 时,可以调用resolveClass,这取决于 loadClass 的 resolve 参数的值。

E) 方法 findLoadedClass

findLoadedClass 充当一个缓存:当请求 loadClass 装入类时,它调用该方法来查看 ClassLoader 是否已装入这个类,这样可以避免重新装入已存在类所造成的麻烦。应首先调用该方法。

怎么组装这些方法

1)调用 findLoadedClass 来查看是否存在已装入的类。

2)如果没有,那么采用那种特殊的神奇方式来获取原始字节。

3)如果已有原始字节,调用 defineClass 将它们转换成 Class 对象。

ClassLoader是个抽象类,它还有很多子类,我们如果要实现自己的ClassLoader,一般都会继承URLClassLoader这个字类,因为这个类已经帮我们实现了大部分工作,我们只需要在适当的
一、ClassLoader概念ClassLoader是用来动态的加载class文件到虚拟机中,并转换成javalangclass类的一个实例,每个这样的实例用来表示一个java类,我们可以根据Clas
本人在研究生阶段做JavaWeb开发时,当把项目发布到Tomcat上运行时,曾遇到过一个神奇的问题,这个问题由“CLassCastException”异常引起,当然这个异常很常见,其实我要说的神奇之处
要深入了解ClassLoader,首先就要知道ClassLoader是用来干什么的,顾名思义,它就是用来加载Class文件到JVM,以供程序使用的。我们知道,java程序可以动态加载类定义,而这个动
1,类加载每个编写的”java”拓展名类文件都存储着需要执行的程序逻辑,这些”java”文件经过Java编译器编译成拓展名为”class”的文件,”class”文件中保存着Java代码经转换后的虚拟机
做Java开发,对于ClassLoader机制是必须要熟悉的基础知识,本文针对JavaClassLoader的机制做一个简要的总结。因为不同的JVM的实现不同,本文所描述的内容均只限于Hotspot
简单地纯粹地记录下如何进行自定义一个自己的ClassLoader什么双亲委派模型啊,双亲委派模型的破坏啊,好处啊,缺点啊什么的,一概不说。自定义ClassLoader的博客啥的,看过不少,但是就是没自
ClassLoader学习http://blogitpubnet/31561269/viewspace2222522/[添加链接描述]转载自学ClassLoader是Java届最为神秘的技术之一,无数
当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构:bootstrapclassloader|extensionclassloader|systemclassloader
一、什么是ClassLoader?大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程序