记录JVM里类加载的过程,我们写了那么多类,却不知道类的加载过程,岂不尴尬。😶🌫️
类的生命周期
类的生命周期包括加载、链接、初始化、使用和卸载。前三个属于类加载的过程,卸载指该对象被垃圾回收了。
类加载过程
-
Loading加载:
通过类的全名(包名+类名),获取到该类的class文件的二进制字节流
在内存中生成一个代表该类的java.long.class对象,作为方法区这个类的各种数据的访问入口。
一句话:加载二进制数据到内存->映射成jvm能识别的结构->在内存中生成class文件。 -
Linking链接
将上面创建好的class类合并到java虚拟机中,使之能够执行的过程。分为验证、准备和解析三个阶段。
1.验证(Verify)
确保class文件中的字节流符合房钱虚拟机的要求,确保class的正确性,不会危害到虚拟机的安全。
2.准备(Prepare)
给类中的静态字段分配内存,并设置默认初始值。
注:被final修饰的静态变量不会设置,因为final在编译阶段就设置好了。
3.解析(Resolve)
解析阶段的目的是将常量池内的符号引用解析成为实际引用。如果符号引用指向一个未被加载的类,那么将触发这个类的加载。
- 初始化
初始化就是执行类的构造器方法。
若该类又父类,则会递归执行父类的init。
类加载器分类
- 第一层:ApplicationClassloader(应用类加载器)
是java中的默认的类加载器。它负责加载环境变量classpath或系统属性java.class.path指定路径下的类。 - 第二层:ExtensionClassLoader(平台类加载器)
从系统属性的java.ext.dirs目录中加载类,或者从JDK的安装目录:jre/lib/ext/目录下加载类。如果将我们自己的jar包放在此文件夹下,那么整台机器的任意一个程序都可以访问。(污染环境) - 第三层:BootStrapClassLoader(引导类加载器)
它是最顶层的加载器,是另外两层的父加载器。出于安全考虑,该加载器只能加载java、javax、sun开头的类,并且在代码获取的时候,返回为null。 - 自定义加载器
大多数情况下上面的三种加载器能够满足我们的日常开发需求,当然我们也可以自定义加载器,并在其中做很多自定义操作。只需继承java.long.ClassLoader类,然后重写findClass方法即可。
获取ClassLoader
ClassLoader loader = Son.class.getClassLoader();
System.out.println(loader);
//sun.misc.Launcher$AppClassLoader@18b4aac2 此处根据JDK版本不同包路径也不同
ClassLoader loader = Son.class.getClassLoader().getParent();
System.out.println(loader);
//sun.misc.Launcher$ExtClassLoader@29453f44
ClassLoader loader = Son.class.getClassLoader().getParent().getParent();
System.out.println(loader);
//此处返回为null,因为出于安全考虑不允许见到bootstrapClassloader
//获取当前类的ClassLoader
System.out.println(Son.class.getClassLoader());
//获取当前线程上下文的ClassLoader
ClassLoader loader = Thread.currentThread().getContextClassLoader();
//获取系统的ClassLoader
ClassLoader loader1 = ClassLoader.getSystemClassLoader();
System.out.println(loader1);
加载机制-双亲委派机制
jvm对class文件采用的是按需加载的方式,当需要使用该类的时,jvm才会将它的class文件加载到内存中产生class对象。
双亲委派机制即是把请求交给父类处理的一种任务委派模式。
- 工作机制:
如果一个类加载器接收到了类加载的请求,它自己不会先加载,会把这个请求向上委托给父加载器去执行。
如果父加载器还存在父加载器,则继续向上委托,一直委托到BootStrapClassLoader
如果父类加载器可以完成加载就返回成功结果,如果加载失败就由子类自己尝试去加载,如果子类也加载失败,就会抛出ClassNotFoundExpection。这就是双亲委派模式。
😺
👾
网友评论