首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

JVM 类加载器引见

2013-12-26 
JVM 类加载器介绍public class ClassLoaderTest { public static void main(String[] args) throws ClassN

JVM 类加载器介绍
public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { ClassLoader myLoder = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { String fileName = name.substring(name.lastIndexOf(".")+1)+".class"; InputStream in = getClass().getResourceAsStream(fileName); Class<?> c = null; try { if(in == null){ return super.loadClass(name); } byte[] b = new byte[in.available()]; in.read(b); c = defineClass(name, b, 0, b.length); } catch (Exception e) { e.printStackTrace(); } return c; } }; Object obj = myLoder.loadClass("com.ClassLoaderTest").newInstance(); System.out.println(obj.getClass()); System.out.println(obj instanceof com.ClassLoaderTest); }}

?结果:
class com.ClassLoaderTest
false

返回false 是因为系统存在两个ClassLoaderTest,一个是系统默认加载的,一个是自己new 的加载器,虽然是用一个class 文件,但是依然是独立的两个类。而我们经常做equals判断的时候会先进行instanceof 检查,大多数情况都是true,因为统一是默认的加载器加载的,下面看看一些加载器的工作情况。

一、双亲委派模型
?从JAVA虚拟机的角度来说,主要有两个加载器:一种是启动了加载器
(Bootstrap ClassLoader),这是由C++写的,是虚拟机一部分,另一种是其他类的加载器,是有Java实现的,独立于虚拟机外,并且都继承自抽象类java.lang.ClassLoader.细分一下,可以分为扩展类加载器(Extension ClassLoader)和应用程序加载器(Application ClassLoader)。

??????? 1.启动类加载器:主要负责加载<JAVA_HOME>\lib 目录中的,虚拟机能识别的文件名的文件。这个加载器无法没Java程序直接用。
?2.扩展类加载器:主要负责加载<JAVA_HOME>\lib\ext下的,或者被java.ext.dirs 系统变量指定的类库,开发者可以直接使用的。
?3.应用类加载器:主要负责加载用户类路径(classpath)上所指定的类库,程序中默认的类加载器,开发者可以自己进行使用。
?这几个加载器在rt.jar sun.misc.Launcher 下,这里我还进不去..!他们直接由上到下的关系,就是双亲委派模型,关于这个模型定义,这里大概介绍一下,它要求除了启动类加载器以外,其他都需要自己的父类加载器,并且父子关系不是继承,而是组合,也就是说类的加载会先让最上面的进行加载,加载不了的,再让后面的进行加载,你会发现每次都会从启动类加载器开始,而且object 这个最大的父类就在rt.jar 里面,也是有启动类加载器进行加载的。这种模式是JDK推荐的,因为如果不按这种方式,自己写个Object 类,自己定义的位置进行加载,那么会混乱。
?对于双亲委派模型代码都集中在java.lang.ClassLoader 的loadClass 中,可以通过图和源码进行了解:
图参考:http://yueyemaitian.blog.163.com/blog/static/2165043320097211052534/

?

??

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException    { //1.  先检查是否被加载过了 Class c = findLoadedClass(name); if (c == null) {     try {  if (parent != null) {      c = parent.loadClass(name, false);  } else {      c = findBootstrapClass0(name);  }     } catch (ClassNotFoundException e) {         // 2. 如果父类找不到,就抛出ClassNotFoundException  // 默认直接抛出异常,JDK要求重写这个方法         c = findClass(name);     } } if (resolve) {     resolveClass(c); } return c;    }  

??

注:JDK 建议我们在实现自己的加载器的时候,不要覆盖locaClass方法,而是建议我们去重写findClass 方法,这样能保证双亲委派模型,同时也实现了自己的方法。

?????????? 双亲委派模型比较稳定,能很好的解决类加载的基础类问题(越基础的类,越由上层加载器进行记载),这些了之所以叫"基础",因为他们总是被用户代码调用API,但是在某些时候,这些基础类又要会调回用户代码,怎么办呢?
???????????比如:用户调用基础类的method-1 方法,而该方法又要调用用户自己写的method-2,这类引用场景一般会在API写了接口,或者一种实现方法,而我们调的时候需要实现自己的方法,比如JDBC,JDNI,提供了接口,具体的实现需要各个不同的数据库厂商去实现,这里就是不基本的双亲委派模型了。
???????????JNDI现在放在rt.jar 里面,目的是对资源进行查找和集中管理,他需要独立厂商去实现并部署,而启动类加载器不识别这些代码,怎么办呢?
???????????Java 团队又设计了一个上下文类加载器(Thread Context ClassLoader),可以通过java.lang.Thread 的setContextClassLoder 进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果程序全局都没设置过,那么默认还是App应用程序加载器。
???????????有了线程上下文加载器,就能加载厂商独立实现的代码了,也就是父类加载器请求子类加载器去完成类的加载动作,双亲委派模型是父类先加载,加载不了再给子类加载,而这里是父类直接请求子类加载,已经破坏了委派模型层次。其中JCE、JAXB、JBI都也都采用了。

???????????其次,用户在追求程序动态性的时候,希望程序想USB 接口一样,可以不用重启电脑,而更换各种外接东西。为了完成这个诱人的操作,比如OSGI实现了模块化热部署,它的关键是自己定义类加载器的实现机制,每一个模块都有自己的类加载器,需要更换模块的时候,就把模块和类加载器一起换掉,实现热替换。(ps:这玩意儿我也没用过,感觉就像webservice 远程调用那样,规定了接口,客户端固定,然后可以随意的替换服务端,从而客户端获得不同的输出)

???????????关于osgi 的原理以及机制,可以参考<深入理解osgi>..之类的书。

小结:
?1.这里主要介绍了类加载器的种类以及运行原理,方便我们加深对类的理解
?2.这些都参考JVM里面的东西,写成小章节,方便自己理解,也分享一部分经验。

?

?

?

热点排行