[读书笔记]Java类加载器
来源:互联网 发布:qq聊天软件下载 编辑:程序博客网 时间:2024/06/10 19:33
一、类与类加载器
类加载器除了在类加载阶段的作用外,还确定了对于一个类,都需要由加载它的类加载器和这个类本身一同确定其在Java虚拟机中的唯一性。通俗一点来讲,要判断两个类是否“相等”,前提是这两个类必须被同一个类加载器加载,否则这个两个类不“相等”。
这里指的“相等”,包括类的Class对象的equals()
方法、isAssignableFrom()
方法、isInstance()
方法、instanceof
关键字等判断出来的结果。
示例:不同的类加载器对instanceof关键字结果的影响
package org.kesar;import java.io.IOException;import java.io.InputStream;public class ClassLoaderTest{ /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { ClassLoader myLoader = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) { return super.loadClass(name); } byte[] b = new byte[is.available()]; is.read(b); return defineClass(name, b, 0, b.length); } catch (IOException e) { throw new ClassNotFoundException(name); } } }; Object obj=myLoader.loadClass("org.kesar.ClassLoaderTest").newInstance(); System.out.println(obj.getClass()); System.out.println(obj instanceof ClassLoaderTest); }}
输出结果:
class org.kesar.ClassLoaderTest
false结果分析:
由于ClassLoaderTest在虚拟机中存在两个,一个是由系统应用程序类加载器加载,另一个是由我们自定义的类加载器加载的,所以两个类并不“相等”。
二、双亲委派模型
从Java虚拟机来讲的话,目前的类加载器有不同的两种。一种是启动类加载器(Bootstrap ClassLoader),是虚拟机中的一部分;另一种是其他的所有类加载器,独立于虚拟机外部的,继承自抽象类java.lang.ClassLoader
。
从系统提供的类加载器来讲,有这3种类加载器:启动类加载器、扩展类加载器、和应用程序类加载器。
1. 系统的类加载器
(1)启动类加载器(Bootstrap ClassLoader)
加载内容:<JAVA_HOME>\lib
目录、被-Xbootclasspath
参数所指定的路径中的虚拟机可识别的类库加载到虚拟机内存中。
特点:不能被Java程序直接引用,如果想将加载委托请求给启动类加载器加载,直接使用null代替即可。
示例:java.lang.Class的getClassLoader()源码
/** * Returns the class loader for the class. Some implementations may use * null to represent the bootstrap class loader. This method will return * null in such implementations if this class was loaded by the bootstrap * class loader. */ public ClassLoader getClassLoader() { ClassLoader cl = getClassLoader0(); if (cl == null) return null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass()); } return cl; }
(2)扩展类加载器(Extension ClassLoader)
加载内容:<JAVA_HOME>\lib\ext
目录、被java.ext.dirs
系统变量所指定的路径中的所有类库
特点:使用Java编写,开发者可以直接使用扩展类加载器
(3)应用程序类加载器(Application ClassLoader)
加载内容:加载用户类路径(ClassPath)上所指定的类库
特点:开发者可以直接使用这个类加载器,如果没有自定义的类加载器,将默认使用它。
2. 双亲委派模型(Parents Delegation Model)
工作过程:当类加载器受到类加载请求时,一般不会自己去加载这个类,将会将这个类加载请求委派父类加载器去完成,每一层次的类加载器都是这么做的,只有当这个类没有父类加载器时(Bootstrap ClassLoader)或父类加载器反馈无法加载这个类时(搜索范围中没有找到所需的类),子加载器才会自己去加载。
类的双亲委派模型如下图:
好处:使用双亲委派模型,由于其工作过程的特点,Java类随着它的类加载器一起具备了一种带有优先级的层次关系(层次越高越优先加载类)。比如:java.lang.Object
,每个类都默认继承自这个类,故每次类加载都会加载这个类,而该类存在rt.jar
中,而使用双亲委派模型,每次类价值请求都将会委派到启动类加载器,故将可以成功加载到java.lang.Object
。而且java.lang.Object
每次的委派都是被启动类加载器加载,这样保证了类在虚拟机中的一致性。如果失去这种机制,每个类加载器将自己加载一个java.lang.Object
,那么系统中将会有多个不相等的Object
类,将会导致应用程序一片混乱。
可以看看ClassLoader的loadClass()方法源码:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先,检查请求的类是否已经被加载过 Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // 如果父类抛出 ClassNotFoundException // 说明父类加载器无法完成加载请求 } if (c == null) { // 如果父类加载器找不到,则自己进行类加载 long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
3. 三次破坏双亲委派模型
(1)第一破坏:JDK版本问题
问题:JDK1.2之前是没有双亲委派模型的。JDK1.0时ClassLoader就已经存在,这时就可以自定义类加载器了,是要重写ClassLoader的findClass()方法来实现的。但是在引入双亲委派模型后,重写findClass()一不小心就会破坏双亲委派模型。
处理:JDK1.2后,为ClassLoader增加一个可重写的loadClass()方法,规定以后自定义类加载器就重写loadClass()就可以,避免重写findClass()破坏双亲委派模型。
(2)第二破坏:模型自身缺陷问题
问题:双亲委派模型无法从父类加载器去请求子类加载器委派加载类。比如有一个典型的例子JNDI服务,JNDI现在已经是Java的标准服务,它的代码是由启动类加载器去加载的,需要调用应用程序在ClassPath下的JNDI接口提供者的代码,但启动类加载器不“认识”这些代码。
处理:使用了线程上下文类加载器(Thread Context ClassLoader),使得父类加载器可以请求子类加载器去完成类加载的动作,达到了逆向使用类加载器的效果,打通双亲委托模型的层次结构。
(3)第三破坏:“动态性”问题
问题:程序动态性的发展:代码热替换、模块热部署等。
处理:定制了Java模块化标准OSGI,在这种环境下,将不会是双亲委派模型的树状结构了,而是进一步发展为网结构。
附:OSGI顺序执行过程
1) 将java.*开头的类委托给父类加载器加载。
2) 否则,将委托列表名单内的类委派给父类加载器加载。
3) 否则,将Import列表中的类委托给Export这个类的Bundle的类加载器加载。
4) 否则,查找当前Bundle的ClassPath,使用自己的类加载器加载。
5) 否则,查找类是否在自己的Fragment Bundle中,如果在,则委派给Fragment Bundle的类加载器加载。
6) 否则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载。
7) 否则,类查找失败。
- [读书笔记]Java类加载器
- [读书笔记]Java类加载过程
- java虚拟机类加载机制---《深入理解java虚拟机》读书笔记
- 《深入分析JavaWeb技术内幕》读书笔记五.Java编码,编译及类加载器
- [Java虚拟机读书笔记]7章 类在虚拟机中的加载
- 深入理解java虚拟机-读书笔记4-虚拟机类加载机制
- 《深入理解java虚拟机》读书笔记——类加载机制
- 深入理解JAVA虚拟机读书笔记----虚拟机类加载机制
- Java虚拟机专题之类加载机制(读书笔记)
- 读书笔记JVM探秘之四:类加载器
- JAVA类加载详细整理——《深入理解JAVA虚拟机》读书笔记
- Java类加载器
- java类加载器
- java类加载器
- Java类加载器
- java类加载器
- Java类加载器
- Java 类加载器
- 【Android 开发教程】动态添加Fragments
- POJ2362:Square
- Uva572——Oil Deposits
- 常用的Java工具
- Blocks中的__block
- [读书笔记]Java类加载器
- java内部类详解
- Android init 启动过程分析
- Golang之bytes.buffer学习笔记
- 基于H5的移动端(APP)开发框架的优缺点
- 美好未来在向我们招手(半年总结)
- 勿在头文件中定义static变量
- 信号函数
- N个非常有用的Android程序片段(持续更新)