🍡 Land
🏁 概述
ClassLoader
用于依赖隔离的容器实现。
# 在Java
中依赖主要是Jar
。
依赖容器自然涉及下面的问题:
- 可以从多处加载类,并分配不同的
ClassLoader
ClassLoader
之间有继承关系
ClassLoader
的继承关系会是一个树- 类加载会在上下级
ClassLoader
之间有委托关系,如:- 是否允许在上级
ClassLoader
中查找类。
即是否 委托。 - 允许在上级
ClassLoader
查找哪些类/包。
即可以配置 委托 的粒度。 - 只允许在某级
ClassLoader
查找哪些类/包,会忽略这级ClassLoader
的下级ClassLoader
中这些类/包,不允许子ClassLoader
。
即必须 委托。
- 是否允许在上级
🔧 功能
ClassLoader
委托关系的完备配置
1. ClassLoader
委托
父子完备委托关系可以先分析只有父子2层ClassLoader
间委托关系的情况。
某个类的加载在两层父子ClassLoader
间委托关系,按是否加载排列组合一共有4种情况:
- 父不加载,子不加载【00】
可以用来显式禁止某些类的加载。
实际应用中应该 很少 会用到。 - 父不加载,子加载【01】
子自理,父里即使包含了相同的类也不会污染子。实际场景:- 用来
Tomcat
容器自用Lib
,不会影响到的Web
应用。
- 用来
- 父加载,子不加载【10】
一定使用的父的类。实际场景:Tomcat
容器的Servlet
API
,不允许被Web
应用改写。
- 父加载,子加载【11】
两者可以加载的情况下,按谁优先分成2个Case:- 父优先。【
Parent-Child
】
即是Java
缺省的委托策略,代理模式(Delegation Mode
)。
这个委托策略可以保证Java
核心库的类型优先加载,Java
核心库的类的加载工作由引导类加载器来统一完成,保证了Java
应用所使用的都是同一个版本的Java
核心库的类,是互相兼容的。 - 子优先。【
Child-Parent
】
这种委托关系比较复杂,有引起类版本混乱的风险!💣
实际应用中应该 避免 这种委托关系。🙅
- 父优先。【
ℹ️
关于子优先【Child-Parent】类版本混乱的风险的细节原因看了后面参考资料就清楚了,这里只说一个简单例子:
子里有类Wheel
;父里有类Car
、Wheel
;类Car
引用了类Wheel
。
子加载类Car
里通过实际是父来加载(子中没有这个类),返回的Car
所引用Wheel
是用Car
的ClassLoader
即父来加载。
子中直接使用Wheel
时,由子来加载(子里有Wheel
类)。
结果Car
引用的类Wheel
和子直接使用的类Wheel
的ClassLoader
不同,即类型不兼容,看起来正确的赋值会抛出的ClassCastException
!
上面【11】的情况分成2个子Case,合起来一共有5种情况。
委托关系可以统一描述成:
None
Child-Only
Parent-Only
Parent-Child
Child-Parent
按上面说明的2层委托关系约定,嵌套推广即可得到 包含 任意层ClassLoader
的完备委托关系。
ClassLoader
委托
兄弟父子ClassLoader
由于树状的单继承关系,委托关系比较单一。
要实现复杂的代理关系,兄弟ClassLoader
之间代理可以简化。
举个场景,多个中间件如RPC
、Message
等,要需要把部分类代理给应用ClassLoader
,如果就使用父子代理,结果会是这样:
System ClassLoader
|
V
RPC ClassLoader
|
V
Message ClassLoader
|
V
App ClassLoader
上面问题是,RPC ClassLoader
和Message ClassLoader
之间的父子关系不符合实际关系(RPC ClassLoader
和Message ClassLoader
之间并不需要父子代理)。
更合理些的代理关系是:
System ClassLoader
/ | \
/ | \
V V V
RPC ClassLoader -> App ClassLoader <- Message ClassLoader
即RPC ClassLoader
和Message ClassLoader
作为App ClassLoader
的兄弟ClassLoader
并代理。
2. 常用类加载方式
- 加载本地类目录或
Jar
文件 - 加载本地有类目录或
Jar
文件的目录 - 加载网络上的类
这个功能应该很少使用😜 ,为了功能完整而说明。 - 加密类工具/加载加密的类
这个功能应该很少使用😝 ,为了功能完整而说明。
🎨 使用场景
- 在一个
JVM
中部署多个应用,但应用依赖不互相影响。
这样是提高 系统利用率 的一种方式。 - 把平台级的二方库从应用中隔离出来,由架构部门统一升级。这样做的原因是:
- 平台级二方库如果有
Bug
影响面广,有统一的升级的需求。 - 平台级二方库升级使用面广,升级困难。
- 平台级二方库如果有
ℹ️
上面的部署方式中,依赖容器的引入对于应用的开发应该是 透明 的。
🍺 目标
- 给出类加载委托情况的完备说明
- 给出类加载委托规则的规范描述
- 给出类加载委托规则的规范描述的自己的一个描述格式
对于这个项目会优先使用Properties
来描述,简单够用。 - 说明
Java
的ClassLoader
的用途和限制 - 给出
ClassLoader
使用和实现的原则 ClassLoader
使用和实现容易出错的地方- 整理出使用了
ClassLoader
的常见框架 - 说明这些框架中
ClassLoader
的实现方法及其使用契约 - 给出
ClassLoader
实现方法及其使用契约的最佳实践
📢 进阶目标
Web
容器集成- 实现
OSGi
规范
这个也可以用来验证实现是否面向编程友好
📚 相关资料
已有的基于ClassLoader实现的隔离容器项目
jboss-modules
JBoss Modules is a standalone implementation of a modular (non-hierarchical) class loading and execution environment for Java.sofa-jarslink
Jarslink is a sofa ark plugin used to manage multi-application deployment
🔬 官方资料
ClassLoader
- The Java Language Specification的第12章 Execution和The Java Virtual Machine Specification的第5章 Loading, Linking, and Initializing
详细介绍了Java
类的加载、链接和初始化。
不同Java
版本的语言和JVM
规范在http://docs.oracle.com/javase/specs/ - Multithreaded Custom Class Loaders in Java SE 7,
JDK
7修复了ClassLoader
的死锁问题。
这个问题在JDK 7
之前的版本中一直存在。平时使用中确实不容易碰到,但在线上应用复杂和高压力场景中有实际观察到过。
这篇文档给出问题的原因及其修复方法。 Java
命令行选项-verbose:class
可以在加载类时显示相关信息。
完整Java
命令行选项参见: http://docs.oracle.com/javase/7/docs/technotes/tools/windows/java.html- Java API doc - ClassLoader
- Understanding Extension Class Loading
Sun
JDK
用于启动应用的Laucher
实现类。
注:sun.misc.Launcher
类没有在JDK
附带的src.zip
中,因为是JDK
具体实现部分(厂商相关),可以在JDK
源码中找到这个Java
类。
Sun
的JDK
的源码下载在这里,厂商相关Java
类在目录jdk/src/share/classes
下。Laucher
类中包含了Java
除BootstrapClassloader
(是用本地代码C/C++
来实现)外另2个Buildin
ClassLoader
类的实现:
Permission
🔖 二手资料
ClassLoader
- IBM DeveloperWorks - 深入探讨Java类加载器。一篇非常不错的
ClassLoader
的介绍文章,并且对比介绍了Java
SPI
的类加载策略。包含JDBC
和JAXP
为代表的2种方式。Tomcat
的类加载策略。OSGi
的类加载策略。
- The basics of Java class loaders
1996
年(Java 1
的年代)的一篇老文章,其中描述功能现在可以通过Java 2
提供的java.net.URLClassLoader
方便的完成。
但实现复杂自定义ClassLoader
的流程是一样的,文章给出了实现自定义ClassLoader
- 要覆盖的
ClassLoader
关键方法 - 要遵循的基本约定
- 要注意的安全问题
- 要覆盖的
- IBM DeveloperWorks - Understanding the Java ClassLoader
- IBM DeveloperWorks - Java programming dynamics, Part 1: Java classes and class loading
中文版在:Java编程的动态性,第 1 部分: 类和类装入
Java programming dynamics series,这个系列的中文版Java编程的动态性 - onjava.com - Internals of Java Class Loading
- IBM DeveloperWorks - Demystifying class loading problems series
- IBM Java Diagnostics Guide - Class loading
- Getting Started with Javassist - Class Loader
- Sheng Liang and Gilad Bracha, "Dynamic Class Loading in the Java Virtual Machine"
ACM OOPSLA'98, pp.36-44, 1998. - Wikipedia - Java Classloader
- Class.forName() vs ClassLoader.loadClass() - which to use for dynamic loading?
ClassLoader实际应用
- Understanding WebLogic Server Application Classloading
- The Apache Tomcat 5.5 Servlet/JSP Container - Class Loader HOW-TO:详细介绍了
Tomcat
5.5中的类加载器机制。 - OSGi Service Platform Core Specification
安全
- IBM DeveloperWorks - Java安全模型介绍
- IBM DeveloperWorks - Java 授权内幕
更多内容参见:IBM DeveloperWorks - Java安全专题 - Java Security - Chapter 3. Java Class Loaders
ClassLoader Memory Leak
- Classloader leaks: the dreaded "java.lang.OutOfMemoryError: PermGen space" exception
- How to fix the dreaded "java.lang.OutOfMemoryError: PermGen space" exception (classloader leaks)
- IBM DeveloperWorks - Diagnosis of Java class loader memory leaks
- Reloading Java Classes 201: How do ClassLoader leaks happen?
- Classloader-Related Memory Issues