JVM:适合小白的类加载子系统(大量图解)

 2023-01-06
原文作者:zero_face 原文地址:https://juejin.cn/post/6988513561123225608

jvm--类的加载子系统

@[toc]

类加载子系统

类加载过程

202301011604557241.png

加载
  • 通过一个类的全限定名获取定义此类的二进制文件流、
  • 将这个类字节流所代表的静态存储结构转换为方法去的运行时数据结构
  • 在内存中的==堆区==生成一个代表这个类的Class对象,作为方法区中这个类的数据结构的访问入口

区别两个Class是不是同一个对象

首先两个类是不是在同一个包下,是否同名

两个类的加载器是否相同

加载的方式

  • 在本地系统中直接加载
  • 通过网络获取
  • 从zip压缩包中读取
  • 运行时计算生成,通过动态代理实现
  • 由其他文件生成
  • 从专有数据库中提取.class文件
  • 从加密文件中获取
链接

验证

确保Class文件的字节流中包含的信息符合虚拟机要求,保证加载类的正确性,不会威海虚拟机的自身安全。

主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证。

准备

为类变量分配内存并且设置该类型量的默认初始值,即零值或者是null值(==final修饰的static变量不会分配,因为final变量在编译的时候就分配了,准备阶段会显式初始化;也不会为实例变量分配初始化,类变量会分配到方法区中,而实例变量会随着对象一起分配到java堆中==)

解析

将常量池内的符号引用转换为直接引用的过程

伴随着jvm执行完初始化之后再执行

初始化

对类的静态变量,静态代码块执行初始化操作

初始化过程就是通过执行javac编译器自动收集类中的所有==类变量==和赋值动作和静态代码块中的语句生成一个执行类构造器方法的过程。

虚拟机必须保证执行类构造器方法只会执行一次,在多线程环境下,被同步加锁。

类什么时候才被初始化:

1)创建类的实例,也就是new一个对象

2)访问某个类或接口的静态变量,或者对该静态变量赋值

3)调用类的静态方法

4)反射(Class.forName("com.lyj.load"))

5)初始化一个类的子类(会首先初始化子类的父类)

6)JVM启动时标明的启动类,即文件名和类名相同的那个类 只有这6中情况才会导致类的类的初始化。

类加载器的分类

引导类加载器(bootstrap ClassLoader):c ,c++ 实现

获取不到。不由java编写,嵌套在jvm内部;但是可以找到能供加载哪些api路径:sun,misc.Launcher.getBootstrapClassPath().getURLS();

加载扩展类和应用程序类加载器,并制定为他们的父类加载器

只加载包名为java、javax、sun开头的目录下的类

自定义类加载器: java实现

凡是直接或者间接继承了ClassLoader的类都叫做自定义类加载器

自定义的类默认使用的是系统类加载器进行加载;系统的核心类库都是由引导类加载器进行加载的。

扩展类加载器

派生于classloader类,父类加载器为启动类加载器

从jdk的安装目录的jre/lib/ext子目录或者从java.ext.dirs系统属性所制定的目录中加载类库,如果用户创建的jar包放在此目录下,也会自动由扩展类加载器进行加载

系统类加载器

派生于classloader,父类为扩展类加载器

负责加载环境变量classpath或者系统属性 java.class.path制定路径下的类库

该类加载器是程序中默认的类加载器,一般来说,java应用的类都是由他来加载完成的

用户自定义类加载器

需要自定义类加载器的情况有哪些?
  • 隔离加载器
  • 修改类加载方式
  • 扩展加载源
  • 防止源码泄露
自定义加载器的步骤?
  • 继承抽象类classloader
  • 重写findClass()方法,将自定义的逻辑写在这个方法中

获取类加载器的途径

1.通过反射获取

Class.forName("java.lang.String").getClassLoader();

2.通过线程获取

Thread.currentThread().getContextClassLoader();

3.获取系统加载器

ClassLoader.getSystemClassLoader().getParent();

双亲委派机制

双亲委派机制有什么作用?

如果一个类加载器收到加载请求,他不会自己去加载,而是将它向上交给他的父类加载器去加载,如果父类还存在父类就再向上传递,直到交给启动类加载器。如果父类加载器可以完成加载,就成功返回,否则就子类才会尝试去加载。

可以防止核心类被篡改,也可以避免重复加载

执行流程:

1.当系统类加载器 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器扩展类加载器去完成。

2.当扩展类加载器收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器启动类加载器去完成。

3.如果启动类加载器加载失败(在<JAVA_HOME>\lib中未找到所需类),就会让扩展类加载器尝试加载。

4.如果扩展类加载器也加载失败,就会使用系统类加载器加载。

5.如果系统类加载器也加载失败,就会使用自定义加载器去尝试加载。

202301011604564472.png

202301011604570703.png