1. 什么是类加载器?
类加载器(Class Loader)是 Java 虚拟机(JVM)的一部分,主要负责将 class 文件从硬盘等外部存储加载到内存中,并在 JVM 运行时期进行动态链接。
Java 的类加载器架构是通过类加载器之间的级联关系来统一组织和管理类及其加载的。其被设计成扩展机制,支持多种不同类型的类型文件(JAR、ZIP、WebInf 等),并且通过“父-子”委派模型来保证安全性。
2. 为什么需要类加载器?
Java 语言的可移植性是 Java 获得成功的最重要原因之一。而类加载器正是 Java 可移植性的核心所在,使 Java 程序能够在不同的操作系统和硬件环境中运行。同时,它还具有以下几个重要功能:
- 隔离命名空间:类加载器隔离了不同的类命名空间,保证了在同一个命名空间内对同一个全限定名的类只被加载一次,也解决了不同类文件中有同名类而导致的问题。
- 动态链接:动态链接是类加载器最主要的功能。JVM 在运行期间才会确定类的具体信息,而不是在编译期间,这就保证了程序的灵活性。
- 保护重要类:Java 的启动类是由 JVM 预先定义的,比如 Object、String 等,这些核心 Java 类由引导类加载器(Bootstrap ClassLoader)来加载,它是 JVM 的一部分(用本地代码实现),因此应用程序无法直接使用它,从而保证了 Java 运行的稳定性和安全性。
3. 类加载器的实现原理
Java 虚拟机规范(JVM Specification)中定义了三种类加载器:引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、应用程序类加载器(Application ClassLoader)。不同类加载器的主要区别在于寻找类文件的方式以及类加载器间的父子关系。其中,引导类加载器负责加载 JVM 必须的核心类(如 java.lang、java.io 等),属于最顶层的类加载器,而另外两个类加载器则是 Java 程序员在开发中最常用到的两种类加载器。
Java 虚拟机在执行一个类时,先由当前正在执行的线程的类加载器去找该类,如果找到并成功加载,则立即结束查找;如果找不到该类,则委托给它的父类加载器去查找,直到找到为止,或到达最顶层的类加载器后仍然找不到该类,则抛出 ClassNotFoundException。
4. 类加载器的使用示例
// 获取类加载器 ClassLoader classLoader = MyClassLoader.class.getClassLoader(); // 打印 classLoader 的层级关系 while (classLoader != null) { System.out.println(classLoader); classLoader = classLoader.getParent(); }
5. 类加载器的优点
- 提高程序的灵活性和可拓展性:Java 类加载器为 Java 程序提供了想要随时替换、更新和升级代码的好处,增强了其灵活性和可拓展性。
- 隔离不同的 jar 包:由于 Java 可由多个 jar 包组成,所以 Java 类加载器将每个 jar 包作为单独的命名空间来加载,从而可以隔离不同的 jar 包和它们之间的类,防止类的调用冲突。
6. 类加载器的缺点
- 协同工作带来的负担:由于类加载器是相互协同的,如果其中一个类加载器出现了问题,将可能导致整个系统的崩溃。
- 内存占用:不同的类加载器会共同创建多个相同类或接口的 Class 对象,并各自维护其自身的 Class 对象的引用。这些重复的 Class 对象实际上占用了大量的内存。
7. 类加载器的使用注意事项
- 类变动重载不成功:类加载器缓存了已经加载过的类,当检测到类变动时,不会全部重载,此时需要完全重新加载系统中涉及到的所有类。
- ClassLoader 对象的泄漏:ClassLoader 对象重要,存在泄漏应该结合代码进行分析优化.
- 线程上下文类加载器的使用:线程上下文类加载器,在多线程并发时容易发生混乱,可能会引发类路径设置错误或者是某些依赖包的 Jar 无法加载的问题。
8. 总结
类加载器在 JVM 中扮演着非常关键的角色。它通过委派模型来保证不同类加载器在虚拟机中的并存,实现如代码隔离、安全性检查等诸多保障。同时,类加载器也有自己的缺点,比如协同工作的负担、内存占用等。因此,在使用时需要注意遵守一些注意事项,并区分优缺点,从而合理的使用类加载器。
本文由 mdnice 多平台发布