文章总结: 本文详细解析Java动态类加载机制,涵盖类生命周期(加载、准备、初始化、使用)、双亲委派模型(Bootstrap/Extension/System/自定义类加载器层级)及源码调试分析。重点跟踪ClassLoader.loadClass()至URLClassLoader.findClass()的完整调用链,说明如何通过重写loadClass打破双亲委派或重写findClass实现自定义加载,为理解Java安全基础与反序列化漏洞利用提供底层原理支撑。 综合评分: 78 文章分类: 代码审计,安全开发,应用安全,WEB安全,二进制安全
从0到1理解动态类加载
小猫咪 小猫咪
梦想变成大黑客的小猫咪
2026年2月9日 16:55 北京
动态类加载是Java安全基础中的核心。
类的声明周期
简单列举一下几个阶段:
加载:加载这一步主要是通过 类加载器 完成的。类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载由 双亲委派模型 决定(不过,我们也能打破双亲委派模型)。
准备:JVM 会在该阶段对类变量(也称为静态变量,static 关键字修饰的)分配内存并初始化,对应数据类型的默认初始值,如 0、0L、null、false 等。
初始化:初始化阶段是执行初始化方法 ()方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。初始化阶段,类变量将被赋值为代码期望赋的值。
使用:就是new,代码中实际进行使用这个类,创建对象
初始化执行的代码
静态参数的赋值 会调用静态代码块,初始化的时候就会调用静态代码块中
Class a=Student.class;
ClassLoader cl=ClassLoader.getSystemClassLoader();
Class.forName("Student",false,cl);
ClassLoader cl=ClassLoader.getSystemClassLoader();
Class<?> c=cl.loadClass("Student");
只进行加载不会进行初始化
Class.forName("Student");
ClassLoader cl=ClassLoader.getSystemClassLoader();
Class<?> c=Class.forName("Student",false,cl);
c.newInstance();实例化
ClassLoader cl=ClassLoader.getSystemClassLoader();
Class<?> c=cl.loadClass("Student");
c.newInstance();实例化
进行初始化会进行初始化调用静态代码块
类加载器
①、引导类加载器(Bootstrap ClassLoader):负责加载 JVM 基础核心类库,如 rt.jar、sun.boot.class.path 路径下的类。
②、扩展类加载器(Extension ClassLoader):负责加载 Java 扩展库中的类,例如 jre/lib/ext 目录下的类或由系统属性 java.ext.dirs 指定位置的类。
③、系统(应用)类加载器(System ClassLoader):负责加载系统类路径 java.class.path 上指定的类库,通常是你的应用类和第三方库。
④、用户自定义类加载器:Java 允许用户创建自己的类加载器,通过继承 java.lang.ClassLoader 类的方式实现。这在需要动态加载资源、实现模块化框架或者特殊的类加载策略时非常有用。
ClassLoader的加载机制(向上委托优先、向下尝试加载兜底)
双亲委派模型:如果一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。
通俗理解先给自己的父类加载器,一直给到最顶端bootstrap classloader,如果被加载到了就不回往下进行传递。
并不是继承关系是逻辑上 的父类,逻辑的指向
加载的顺序如下:
应用程序类加载器 → 扩展类加载器 → 启动类加载器(失败)→ 扩展类加载器(失败)→ 应用程序类加载器
源代码分析
跟踪一下源码进行调试分析
ClassLoader cl=ClassLoader.getSystemClassLoader();
Class<?> c=cl.loadClass("Student");
先调用到了ClassLoader类的方法
因为AppClassLoader没有一个参数的loadClass
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
又回到AppClassLoader
public Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
int i = name.lastIndexOf('.');
if (i != -1) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPackageAccess(name.substring(0, i));
}
}
if (ucp.knownToNotExist(name)) {
// The class of the given name is not found in the parent
// class loader as well as its local URLClassPath.
// Check if this class has already been defined dynamically;
// if so, return the loaded class; otherwise, skip the parent
// delegation and findClass.
Class<?> c = findLoadedClass(name);
if (c != null) {
if (resolve) {
resolveClass(c);
}
return c;
}
throw new ClassNotFoundException(name);
}
return (super.loadClass(name, resolve));
}
又回到父类ClassLoader中
双亲委派的流程ClassLoader类中如下
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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 thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
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;
}
}
Class<?> c = findLoadedClass(name);
if (resolve) {
resolveClass(c);
}
return c;
}
查看是否被加载,如果加载到了,直接就会被返回
如果没有被加载,执行下面的内容
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
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();
}
}
parent不是空的就让parent.loadClass(name, false);父类进行加载
父类等于空说明在最顶层了 使用Bootstrap ClassLoader进行加载,Bootstrap ClassLoader是JVM底层的C++实现,不属于Java类加载器体系,故返回null(ExtClassLoader的 parent 是null)
c = findBootstrapClassOrNull(name);
抛到顶层没有被加载就调用自己的findClass进行加载
如果想要打破双亲委派就进行重写loadclass方法,如果不进行打破就重写findclass就可以了。
当前是ExtClassLoader调用findClass
所以到了它的父类URLClassLoader
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
没有加载到 回退
当前是AppClassLoader
所以到了它的父类URLClassLoader
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
path xxx.class
Resource res = ucp.getResource(path, false); 就是在系统类路径 java.class.path 上指定的类库,通常是你的应用类和第三方库。
找到了res不等于空
就会调用 defineClass(name, res);
private Class<?> defineClass(String name, Resource res) throws IOException {
long t0 = System.nanoTime();
int i = name.lastIndexOf('.');
URL url = res.getCodeSourceURL();
if (i != -1) {
String pkgname = name.substring(0, i);
// Check if package already loaded.
Manifest man = res.getManifest();
definePackageInternal(pkgname, man, url);
}
// Now read the class bytes and define the class
java.nio.ByteBuffer bb = res.getByteBuffer();
if (bb != null) {
// Use (direct) ByteBuffer:
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
return defineClass(name, bb, cs);
} else {
byte[] b = res.getBytes();
// must read certificates AFTER reading bytes.
CodeSigner[] signers = res.getCodeSigners();
CodeSource cs = new CodeSource(url, signers);
sun.misc.PerfCounter.getReadClassBytesTime().addElapsedTimeFrom(t0);
return defineClass(name, b, 0, b.length, cs);
}
}
SecureClassLoader类中
protected final Class<?> defineClass(String name,
byte[] b, int off, int len,
CodeSource cs)
{
return defineClass(name, b, off, len, getProtectionDomain(cs));
}
ClassLoader类中
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain);
String source = defineClassSourceLocation(protectionDomain);
Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
postDefineClass(c, protectionDomain);
return c;
}
private native Class<?> defineClass1(String name, byte[] b, int off, int len,
ProtectionDomain pd, String source);
AppClassLoader 继承 URLClassLoader
ExtClassLoader同样继承 URLClassLoader
URLClassLoader 的⽗类的 SecureClassLoader
总的流程:
ClassLoader —-> SecureClassLoader —> URLClassLoader —-> APPClassLoader
loadClass() —-> findClass(重写的方法)—–》defineclass(从字节码加载类)
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:梦想变成大黑客的小猫咪 小猫咪 小猫咪《从0到1理解动态类加载》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论