文章目录
- 类加载器
- 什么是类加载器
- 类加载器的作用是什么
- 应用场景
- 类加载器的分类
- 启动类加载器
- 用户扩展基础jar包
- 扩展类加载器和应用程序类加载器
- 扩展类加载器
- 通过扩展类加载器去加载用户jar包:
- 应用程序加载器
- Arthas中类加载器相关功能
- 文章说明
类加载器
什么是类加载器
类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术
类加载器会通过二进制流的方式获取到字节码文件的内容,接下来将获取到的数据交给Java虚拟机,虚拟机会在方法区和堆上生成对应的对象保存字节码信息。
类加载器的作用是什么
- **类加载器(ClassLoader)负责在类加载过程中的字节码获取并加载到内存这一部分,**通过加载字节码数据放入内存转换成byte[],接下来调用虚拟机底层方法将byte[]转换成方法区和堆中的数据。
应用场景
- 企业级应用
- SPI机制
- 类的热部署
- Tomcat类的隔离
- 大量的面试题
- 什么是类的双亲委派机制
- 打破类的双亲委派机制
- 自定义类加载器
- 解决线上问题
- 使用Arthas不停机解决线上故障
类加载器的分类
类加载器分为两类,一类是Java代码中实现的,一类是Java虚拟机底层源码实现的。
虚拟机底层实现
:源代码位于Java虚拟机的源码中,实现语言与虚拟机底层语言一致,比如Hotspot使用C++。主要目的是保证Java程序运行的基础类被正确地加载,比如java.lang.String
,Java虚拟机需要确保其可靠性。JDK中默认提供或者自定义
:JDK中默认提供了多种处理不同渠道的类加载器,程序员也可以自己根据需求使用Java语言定制。所有Java中实现的类加载器都需要继承ClassLoader这个抽象类。
可以查看所有类加载器,即ClassLoader的子类
类加载器的设计,JDK8和8之后的版本差别较大。首先来看JDK8及之前的版本,这些版本中默认的类加载器有如下几种:
启动类加载器(Bootstrap ClassLoader、C++实现)
:加载核心类,String类扩展类加载器(Extension ClassLoader、Java实现)
:加载扩展类,拓展Java中比较通用的类,只是通用,不是特别重要,最重要的在启动类加载器加载了应用程序类加载器(Application ClassLoader、Java实现)
:加载应用classpath中的类,包括我们自己写的类,还有第三方Jar包的类自定义类加载器(Java实现)
:需要重写findClass方法。
类加载器的详细信息可以通过Arthas的classloader命令查看
classloader
- 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去 getResource
- BootstrapClassLoader是启动类加载器,numberOfInstances是类加载器的数量只有1个,loadedCountTotal是加载器所加载的类的数量为1861个
- ExtClassLoader是扩展类加载器
- AppClassLoader是应用程序类加载器
- DelegatingClassLoader是用来提升反射效率的类加载器
- 启动类加载器(Bootstrap ClassLoader)是由Hotspot虚拟机提供的、使用C++编写的类加载器,Java程序员无法修改或者扩展源代码,所以只关注这个加载器的作用。
- 作用:默认加载Java安装目录/jre/lib下的类文件,比如rt.jar,tools.jar,resources.jar等,给java程序提供了一个基础的运行环境。
运行如下代码:
/**
* 启动程序类加载器案例
*/
public class BootstrapClassLoaderDemo {
public static void main(String[] args) throws IOException {
ClassLoader classLoader = String.class.getClassLoader();
System.out.println(classLoader);
System.in.read();
}
}
这段代码通过String类获取到它的类加载器并且打印,本来以为是Bootstrap ClassLoader
,结果是null
。这是因为启动类加载器在JDK8中是由C++语言来编写的,在Java代码中去获取既不适合也不安全,所以才返回null
在Arthas中可以通过sc -d 类名
的方式查看加载这个类的类加载器详细的信息,比如:
通过上图可以看到,java.lang.String类的类加载器是空的,Hash值也是null。所以只要看到为null,就知道这是启动类加载器
用户扩展基础jar包
如果用户想扩展一些比较基础的jar包,让启动类加载器加载,有两种途径:
- 打包成jar包,放入jre/lib下进行扩展。不推荐,尽可能不要去更改JDK安装目录中的内容,因为即使放进去由于文件名不匹配的问题也不会正常地被加载(在加载jar包的时候,会对名称进行校验,名称必须符合JVM内部的一些规范)。
- **使用参数进行扩展。**推荐,使用
-Xbootclasspath/a:jar包目录/jar包名
进行扩展,参数中的/a代表新增。
【使用参数进行扩展实现流程】
先创建第一个项目,打包成jar包
再创建第二个项目,在第二个项目的IDEA配置中添加虚拟机参数,就可以加载D:/jvm/jar/classloader-test.jar
这个jar包了。
使用Class.forName
获取Jar包的类,可以正常执行初始化,说明自己拓展的Jar包被加载了
扩展类加载器和应用程序类加载器
- 扩展类加载器和应用程序类加载器都是JDK中提供的、使用Java编写的类加载器。
- 它们的源码都位于sun.misc.Launcher中,是一个静态内部类。继承自URLClassLoader,具备通过目录或者指定jar包将字节码文件加载到内存中的能力。
继承关系图如下:
ClassLoader类
:定义了具体的行为模式,简单来说就是先从本地或者网络获得字节码信息,然后调用虚拟机底层的方法创建方法区和堆上的对象。这样的好处就是让子类只需要去实现如何获取字节码信息这部分代码。SecureClassLoader
:提供了证书机制,提升了安全性。URLClassLoader
:提供了根据URL获取目录下或者指定jar包进行加载,获取字节码的数据的能力。
扩展类加载器和应用程序类加载器继承自URLClassLoader,获得了上述的三种能力。
扩展类加载器(Extension Class Loader)是JDK中提供的、使用Java编写的类加载器。默认加载Java安装目录/jre/lib/ext下的类文件。
如下代码会打印ScriptEnvironment类的类加载器。ScriptEnvironment是nashorn框架中用来运行javascript语言代码的环境类,他位于nashorn.jar包中被扩展类加载器加载。这些类我们很少用,所以被放到了扩展类加载器中。
/**
* 扩展类加载器
*/
public class ExtClassLoaderDemo {
public static void main(String[] args) throws IOException {
ClassLoader classLoader = ScriptEnvironment.class.getClassLoader();
System.out.println(classLoader);
}
}
打印结果如下:
通过扩展类加载器去加载用户jar包:
- 放入/jre/lib/ext下进行扩展。不推荐,尽可能不要去更改JDK安装目录中的内容。
- 使用参数进行扩展使用参数进行扩展。推荐,使用
-Djava.ext.dirs=jar包目录
进行扩展,这种方式会覆盖掉原始目录,可以追加上原始目录,并使用 ;(windows系统所用符号) :(macos/linux) 进行分隔
如下图中:
使用引号
将整个地址包裹起来,这样路径中即便是有空格也不需要当做特殊字符额外处理。路径中要包含原来ext文件夹,同时在最后加上扩展的路径。
应用程序类加载器会加载classpath下的类文件,默认加载的是项目中的类以及通过maven引入的第三方jar包中的类。
如下案例中,打印出Student
(自己写的)和FileUtils
(引入的)的类加载器:
/**
* 应用程序类加载器案例
*/
public class AppClassLoaderDemo {
public static void main(String[] args) throws IOException, InterruptedException {
//当前项目中创建的Student类
Student student = new Student();
ClassLoader classLoader = Student.class.getClassLoader();
System.out.println(classLoader);
//maven依赖中包含的类
ClassLoader classLoader1 = FileUtils.class.getClassLoader();
System.out.println(classLoader1);
Thread.sleep(1000);
System.in.read();
}
}
输出结果如下,这两个类均由应用程序类加载器加载。
Arthas中类加载器相关功能
类加载器的加载路径可以通过classloader –c hash值 查看:
查看应用程序类加载器所加载的jar包
文章说明
该文章是本人学习 黑马程序员 的学习笔记,文章中大部分内容来源于 黑马程序员 的视频黑马程序员JVM虚拟机入门到实战全套视频教程,java大厂面试必会的jvm一套搞定(丰富的实战案例及最热面试题),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对 黑马程序员 的优质课程表示感谢。