文章目录
一、Java中类加载过程
1.1、类运行过程
我们在启动一个类的过程中,首先是由java.exe调用jvm.dll创建Java虚拟机,再由其虚拟机创建一个引导类加载器实例,获取到对应的类加载器后,加载执行的类资源后调用其main执行的入口方法,执行相关的业务逻辑代码后,Java程序运行结束,JVM就销毁,相关的图示如下:
1.2、类加载过程
加载器类的过程主要分为以下步骤:
- 加载:也就是从磁盘上读取类的class文件;
- 验证:这一步主要是验证读取的class文件的格式是否是正确的;
- 准备:将定义好的静态变量设置默认值,分配内存;
- 解析:这里会将符号引用转变为直接引用,直接引用中分为静态连接和动态连接。详见名词解释;
- 初始化:对静态变量进行赋值,以及执行静态代码块
具体过程如下图示:
类加载到JVM的方法区中,主要包含:运行时常量池、类型信息、字段信息、方法信息、类加载的引用(当前这个类到类加载实例的引用)、对应的class实例引用
二、Java的类加载器和双亲委派模型
2.1、类加载器的分类
java中的类加载器主要分为四类,他们分别是:引导类加载器、扩展类加载器、应用类加载器、自定义加载器,各自的加载器资源下图示:
2.2、类加载器的初始化过程
- 首先创建的JVM启动器实例sun.misc.Launcher,在这个Launcher构造方法内部,创建了两个类加载器,分别为:sun.misc.Launcher.ExtClassLoader和sun.misc.Launcher.AppClassLoad,并且JVM默认使用Launcher的getClassLoader()方法返回的类加载器为:AppClassLoader实例,可详见底层Launcher类,如下图示:
2.3、双亲委派机制
1、我们可以先看一个类加载到JVM的过程,所使用到类加载器的过程是一个什么样的
首先,接2.2小结所说,JVM默认使用Launcher的getClassLoader()方法返回的类加载器为:AppClassLoader实例,AppClassLoader的loadClass方法最终会调用其父类ClassLoader的loadClass方法,该方法的大体逻辑如下:
-
首先,会检查指定的类是否已经加载过了,如果加载过了,就不需要再加载了,直接返回,底层的逻辑代码如下:
-
如果这个类没有被加载过,就先判断其是否有父加载类,如果有,就调父加载类进行加载,如果没有,就调用bootstrap加载类加载,图示如下:
-
如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载,底层代码如下图示:
那我们再回到最初的问题,加载一个类的过程,首先是会先判断其在已加载的列表中是否存在,存在就返回,不存在就先判断是否有父加载类,有的话,就让父加载类加载,如果父加载类以及bootstrap都没加载出,那才使用当前类加载器加载,这样一个过程,我们就称之为类加载的双亲委派机制
2、那为什么要设置这种双亲委派的类加载机制呢
其实主要有两个方面,分别为:
- 沙箱安全机制:主要是能够保护java核心类库资源不会被其他新写的同包同名的类给覆盖掉,这样子可以防止核心的API库被篡改
- 类不被重复加载:当父类已经加载过该类了,那就没有必要让子加载类再重新加载一遍,这可以保证加载类的唯一性
三、自定义类加载器与打破双亲委派机制
3.1、自定义类加载器示例
自定义的类我们需要继承至ClassLoader,从写其loaderClass(定义其类加载的逻辑==>可打破双亲委派模型)和findClass方法,具体如下:
3.2、自定义类加载器示例运行结果
其运行结果1如下(没有从写loadClass方法):
其运行结果2如下(从写loadClass方法):
四、总结
4.1、首先我们先知道java类运行和加载的过程是如何运行的
运行时,由java.exe调用jvm.dll创建一个java虚拟机,java虚拟机同时创建了一个sun.misc.Launcher实例,这个实例的构造方法中会同时创建两个类加载器,分别是ExtClassLoader和AppClassLoader,接下来就是去加载类(双亲委派机制),读取类的过程:加载=>验证=>准备=>解析=>初始化;
4.2、类的双亲委派机制(原因以及底层源码机制)
4.3、自定义类加载器及打破双亲委派
自定义的类我们需要继承至ClassLoader,从写其loaderClass(定义其类加载的逻辑==>可打破双亲委派模型)和findClass方法