news 2026/4/21 13:40:15

类加载器分析(一) 应用类加载器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
类加载器分析(一) 应用类加载器

类加载器分析

1 什么是类加载

当Java程序执行new函数时,会请求Java虚拟机创建一个类对象,那么在创建类对象前会在内存中找到该类的相关信息,并将其作为创建类对象的依据。

如果Java开发者使用过反射,则会知道在JVM中有一个Class类用于描述用户类的元信息,这里的类加载就是JVM在收到类加载请求后,通过类加载器返回Class类实例

2 类加载机制

Java虚拟机把描述类的数据.class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。

在Java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略让Java语言进行提前编译会面临额外的困难,也会让类加载时稍微增加一些性能开销,但是却为Java应用提供了极高的扩展性和灵活性,

类加载的过程如下

3 类加载器分类

类加载器分为启动类加载器(Bootstrap Class Loader),这个类加载器使用C++语言实现,是虚拟机的一部分

另一个就是其他所有的类加载器,由Java语言实现,独立存在于虚拟机外部,并且全部继承自抽象类java.lang.ClassLoader

由于启动类加载器涉及的部分比较复杂,我们先从简单的应用类加载器说起

3.1 应用类加载器

假设虚拟机已经完成了初始化等工作,JVM处于正常运行的状态,那么应用类加载器作为Java语言编写的功能即可使用。

应用类加载器的类继承关系如下

根据上图可知,JVM使用了如下Java代码用于描述其关系。首先需创建一个抽象类ClassLoader,所有的类加载器都继承于此

/* src/java.base/share/classes/java/lang/ClassLoader.java */publicabstractclassClassLoader{

类加载器至少包含有如下属性,即类加载器的名称

// class loader nameprivatefinalStringname;

其中涉及一个关键的函数是loadClass,该函数根据传入的类名用于加载类

publicClass<?>loadClass(Stringname)throwsClassNotFoundException{...

可见,其返回值为Class类对象。

3.1.1 类加载器加载类的方式——双亲委派

openjdk中采用双亲委派机制实现类的加载。

双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。《深入理解Java虚拟机》

具体来说,假设某个类被请求加载,那么假设该类是用户自定义的类,那么第一步会采用应用类加载器进行加载,但是它不会自己尝试加载该类,而是让父类加载器去加载。

考虑到openjdk中的类加载器继承关系如下

则加载的顺序为

AppClassLoader接受加载类的请求,执行loadClass() 该loadClass会请求父类BuiltinClassLoader执行loadClass() ... 一直向上至ClassLoader执行loadClass()

如果父类无法完成类加载工作,则由当前的子类进行加载。

下面的代码用于验证其过程,假设有一个Dog

publicclassDog{privateString_name;privatedouble_weight;publicStringgetName(){return_name;}publicdoublegetWeight(){return_weight;}publicvoidsetName(Stringname){_name=name;}publicvoidsetWeight(doubleweight){_weight=weight;}publicDog(){}publicDog(Stringname){_name=name;}publicDog(Stringname,doubleweight){_name=name;_weight=weight;}publicvoideat(Stringfood){System.out.println(_name+" is eating "+food);}publicStringspeak(){return"Woof!";}publicstaticvoidmain(String[]args){Dogfido=newDog("Fido");fido.setWeight(12.5);fido.eat("a bone");System.out.println(fido.speak());}}

为了解内部的运作机制,可使用IntelliJ Idea调试openjdk的类加载器

可以看到其调用堆栈如下

很明显,在第一步调用ClassLoader时采用的是AppClassLoader,然后逐步往父类调用,一直到最顶层的ClassLoader类的findLoadedClass方法

因此,在JVM实现双亲委派的Java代码中,在ClassLoader中加入了一个parent成员变量表示其父类

publicabstractclassClassLoader{...// The parent class loader for delegationprivatefinalClassLoaderparent;// class loader nameprivatefinalStringname;
3.1.2 如何获得Class对象

由于类加载完成后会常驻内存,因此最终会从hotspot中获得Class的信息

public final class LauncherHelper{ private staic Class<?> loadMainClass(int mode, String what){ //获得系统类加载器,这里采用的是AppClassLoader ClassLoader scl = ClassLoader.getSystemClassLoader(); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/21 13:38:15

专业指南:如何快速重置Navicat Premium的macOS试用期

专业指南&#xff1a;如何快速重置Navicat Premium的macOS试用期 【免费下载链接】navicat_reset_mac navicat mac版无限重置试用期脚本 Navicat Mac Version Unlimited Trial Reset Script 项目地址: https://gitcode.com/gh_mirrors/na/navicat_reset_mac 对于数据库开…

作者头像 李华
网站建设 2026/4/21 13:29:15

ROS2 Nav2 导航地图的构建、保存与加载实战

1. 从零开始理解ROS2 Nav2导航地图 第一次接触机器人导航时&#xff0c;我完全被各种专业术语搞晕了。直到自己动手搭建了一套TurtleBot3的仿真环境&#xff0c;才真正理解导航地图是怎么回事。简单来说&#xff0c;导航地图就是机器人认识世界的眼睛&#xff0c;就像我们人类去…

作者头像 李华
网站建设 2026/4/21 13:28:37

PinWin窗口置顶工具:3种方法让你的窗口永远保持在最上层

PinWin窗口置顶工具&#xff1a;3种方法让你的窗口永远保持在最上层 【免费下载链接】PinWin Pin any window to be always on top of the screen 项目地址: https://gitcode.com/gh_mirrors/pin/PinWin 你是否在编程时需要在代码编辑器和API文档之间来回切换&#xff1…

作者头像 李华