Java Classloader详解

一、Java中的class加载机制有以下三个特性:

1、全盘负责制 

“全盘负责”是指当一个ClassLoader装载一个类时,除非显示地使用另一个ClassLoader,则该类所依赖及引用的类也由这个CladdLoader载入。

例如,系统类加载器AppClassLoader加载入口类(含有main方法的类)时,会把main方法所依赖的类及引用的类也载入,依此类推。“全盘负责”机制也可称为当前类加载器负责机制。显然,入口类所依赖的类及引用的类的当前类加载器就是入口类的类加载器。

2、双亲委派制(Parent Delegation)

1) 委托机制的意义

主要是出于安全性考虑,确保Java的核心类在内存中只有一份字节码,比如两个类A和类B都要加载java.lang.System类,通过双亲委派,系统只会加载一次java.lang.System,即使用户重写了java.lang.System,也不会有机会被加载到,除非你重写ClassLoader。不过有时候为了做容器隔离,需要在JVM中对同一个Class有多份字节码,例如OSGI和Pandora技术,后面会详细谈到。

2) 委托机制是必须的吗?

“双亲委派”机制只是Java推荐的机制,并不是强制的机制。我们可以继承java.lang.ClassLoader类,实现自己的类加载器。如果想保持双亲委派模型,就应该重写findClass(name)方法;如果想破坏双亲委派模型,可以重写loadClass(name)方法。使用自己的类加载器,有很多高级玩法,例如OSGI和Pandora的隔离机制,就是通过自定义ClassLoader来实现的。

3) 如何实现双亲委派?

  默认的ClassLoader的loadClass()实现方式是双亲委派模型,我们可以继承ClassLoader去自定义自己的ClassLoader,如果不重写loadClass方法,那么默认也是双亲委派的。例如,URLClassLoader只是实现了findClass( ),而loadClass( )还是继承ClassLoader的,所以其依然是Parent Delegation的。下面是ClassLoader.loadClass( )源码,看下双亲委派是怎么实现的。

1)Check这个class是否被装载过,如果有直接返回。

装载过的类是被缓存起来的,这样确保了同一个类不会被加载两次,不过有一个问题,用什么来作为class缓存的key呢?在JVM中,class是用类全名(包名+类名) 再加上加载这个类的classLoader来唯一标识的,例如class的类全名是C1,classLoader是L1,那么这个class instance在JVM中的key就是(C1, L1),此时另一个classLoader L2也加载了该类,那么将会有另一个class instance (C1, L2),这两个class instance是不同的type,如果这两个class的object做赋值操作的话,会出现ClassCastException。

2)尝试从parent classloader去加载类。

3)如果parent是null(当parent是bootstrap时就是null了),试图从BootstrapClassLoader的native方法去加载类。

4)在上面尝试都失败的情况下,尝试自己去加载。

3、按需加载 (On-demand Loading)  

什么时候Class会被JVM加载呢? 回答是只有当class被用到时,才会被load,例如new instance,调用其static变量和方法,或使用反射调用其class对象。

这个很容易验证,在启动参数里加上-verbose:class,  就可以清晰看到class是何时被加载的。

二、JVM中classloader加载class的顺序

三、ContextClassloader的用处

1)什么是ContextClassLoader

Thread的一个属性,可以在运行时,通过setContextClassLoader方法来指定一个合适的classloader作为这个线程的contextClassLoader,然后在任何地方通过getContextClassLoader方法来获得此contextClassLoader,用它载入我们所需要的Class。如果没有被显示set过,默认是system classloader。利用这个特性,我们可以“打破”classloader委托机制,父classloader可以获得当前线程的contextClassLoader,而这个contextClassLoader可以是它的子classloader或者其他的classloader。

2) 为什么要使用ContextClassLoader

Thread context classloaders provide a back door around the classloading delegation scheme.

Take JNDI for instance: its guts are implemented by bootstrap classes in rt.jar (starting with J2SE 1.3), but these core JNDI classes may load JNDI providers implemented by independent vendors and potentially deployed in the application‘s -classpath. This scenario calls for a parent classloader (the primordial one in this case) to load a class visible to one of its child classloaders (the system one, for example). Normal J2SE delegation does not work, and the workaround is to make the core JNDI classes use thread context loaders, thus effectively "tunneling" through the classloader hierarchy in the direction opposite to the proper delegation.

这个后门在SPI的实现中很有用,因为接口类是在parent classloader中加载的,而实现类是由它的child classloader加载的,使用contextClassLoader可以绕过双亲委派,达到在parent中使用child classloader去load class的目的。其过程如下图所示:

这样的示例在JDK中很常见,例如JNDI和JAXP都是通过这样的方式去加载具体的provider的。例如

javax.xml.ws.spi.FactoryFinder
Object find(String factoryId, String fallbackClassName){
ClassLoader classLoader;
        try {
            // get context classloader, mostly it‘s system classloader, but it could be user-defined classloader as well.
            classLoader = Thread.currentThread().getContextClassLoader();
        } catch (Exception x) {
            throw new WebServiceException(x.toString(), x);
        }
        String serviceId = "META-INF/services/" + factoryId;
        // try to find services in CLASSPATH
        // Note that if it‘s not system classloader, this will invoke user-defined classloader‘s findResource( ) to find services when all its parents failed.
        try {
            InputStream is=null;
            if (classLoader == null) {
                is=ClassLoader.getSystemResourceAsStream(serviceId);
            } else {
                is=classLoader.getResourceAsStream(serviceId);
            }
.....
    return newInstance(fallbackClassName, classLoader);
}
时间: 2024-10-08 10:14:22

Java Classloader详解的相关文章

(转)Java ClassLoader详解

转:http://java.chinaitlab.com/base/804400.html 类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一.它使得 Java 类可以被动态加载到 Java 虚拟机中并执行.类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的.Java Applet 需要从远程下载 Java 类文件到浏览器中并执行.现在类加载器在 Web 容器和 OSGi 中得到了广泛的使用.一般来说,Java 应用的开发人

java ClassLoader 详解

1.什么是类加载器? 类加载器就是寻找类的字节码文件并构造出类在JVM内部表示的对象组件. 类装在工作有ClassLoader及其子类负责,ClassLoader是一个重要的java运行时系统组件,它负责运行时查找和装入class字节码文件. jvm运行时产生三个ClassLoader: BootStrap ClassLoader:由C++编写,非ClassLoader子类,在java中看不到:根装载器主要负责JRE的核心类库,lib下的 rt.jar.resources.jar.charset

java 反射 详解

本文来自:blog.csdn.net/ljphhj JAVA反射机制:   通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们. 理论的东东太多也没用,下面我们看看实践 Demo - Demo: package cn.lee.demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import

Java反射详解

Java反射详解 分类:java, 基础日期:2012-07-20作者:ticmy 19 反射,是Java中非常重要的一个功能,如果没有反射,可以说很多框架都难以实现. 什么是反射?说白了就是可以通过Java代码获取装载到方法区的类信息的手段. 当装载一个类时,会在方法区产生一个数据结构,该结构中包含着装载的类的相关信息.字节码可以看成是数据流,那么方法区的这种数据结构可以说是字节码数据流的结构化表现.装载的最终产物就是java.lang.Class类的一个对象,它是Java程序与方法区内部数据

java反射详解 (转至 http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html)

本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案例1]通过一个对象获得完整的包名和类名 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package Reflect; /**  * 通过一个对象获得完整的包名和类名  * */ class Demo{     //other codes... } class hello{     pu

getClass()和getClassLoader()区别 以及ClassLoader详解及用途(文件加载,类加载)

获得ClassLoader的几种方法可以通过如下3种方法得到ClassLoader this.getClass().getClassLoader(); // 使用当前类的ClassLoader Thread.currentThread().getContextClassLoader(); // 使用当前线程的ClassLoader ClassLoader.getSystemClassLoader(); // 使用系统ClassLoader,即系统的入口点所使用的ClassLoader.(注意,s

ClassLoader 详解

1.  classLoader是什么? ClassLoader是类的加载器,用于加载class对象到JVM中的,分为2种,启动类加载器和实际类加载器. 2. ClassLoader的作用 .java文件通过javac.exe编译成.class文件,然后通过classLoader加载到JVM内存中. 加载到JVM内存的就是Class对象,存储着.class字节码内容,每一个class对象都有getClassLoader()方法, 得到是谁把我从.class文件加载到内存中变成Class对象的. 3

Java ClassLoad详解

Java ClassLoad详解 类加载器是 Java 语言的一个创新,也是 Java 语言流行的重要原因之一.它使得 Java 类可以被动态加载到 Java 虚拟机中并执行.类加载器从 JDK 1.0 就出现了,最初是为了满足 Java Applet 的需要而开发出来的.Java Applet 需要从远程下载 Java 类文件到浏览器中并执行.现在类加载器在 Web 容器和 OSGi 中得到了广泛的使用.一般来说,Java 应用的开发人员不需要直接同类加载器进行交互.Java 虚拟机默认的行为

package-info.java文件详解

package-info.java文件详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 一.pacakge-info.java介绍 pacakge-info.java是一个Java文件,可以添加到任何的Java源码包中.pacakge-info.java的目标是提供一个包级的文档说明或者是包级的注释. pacakge-info.java文件中,唯一要求包含的内容是包的声明语句,比如: package com.ch.service; 二.包文档 在