类和类加载器子系统
跟随宋红康老师学习JVM的学习笔记
类加载器子系统的作用
- 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的标识。
- 类加载器只负责class文件的加载,对于是否可以执行,则交给执行引擎来决定。
- 加载类的信息存放在方法区的内存空间中,除了存在类的信息,还会存放常量池信息、字符串字面量和数字常量。
类加载器子系统
加载(Loading)
- 通过一个类的全类名获取此类的二进制字节流。
- 将字节流所代表的静态存储结构转化为方法去运行时的数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的访问入口。
链接(Linking)
-
验证(Verify)
确保被加载的Class文件符合虚拟机的要求,保证类加载的正确性。
主要包括四种验证,文件格式验证、元数据验证、字节码验证、符号引用验证。
-
准备(Prepare)
为类变量分配内存并且赋上初始值——零值。被final修饰的static变量不会被赋初始值,因为在编译时就被分配值了。
-
解析(Resolve)
将常量池的符号引用转换为直接引用。类在执行过程中会加载很多的类,例如Object类,用符号引用引用相关的类。
初始化(Initialization)
- 初始化阶段就是执行类构造器方法()的过程。(class-init—>clinit)clinit是自动生成的。会将类变量初始化和静态代码块初始化合并。收集顺序由语句在源文件中出现的顺序决定。父类先加载。
- 虚拟机必须保证一个类的clinit方法在多线程下被同步加锁。
类加载器的分类
-
引导类加载器(启动类加载器)
非Java语言编写,使用C/C++编写
主要用于加载Java的核心类库,没有父加载器。
-
自定义类加载器(扩展类加载器+用户自定义类加载器)
对于用户自定义类来说,默认使用系统类加载器(应用程序类加载器)进行加载。String类使用的是引导类加载器进行加载。——>Java核心类库都是用引导类加载器加载。
- 扩展类加载器:父类为启动类加载器 - 应用程序类加载器(系统类加载器):父类为扩展类加载器,该类是程序中默认的类加载器。 - 用户自定义类加载器:隔离加载器、修改类加载方式、扩展加载源、防止源码泄露。 *自定义类加载器的步骤:①继承ClassLoader类 ②重写findClass()方法
*双亲委派机制
Java虚拟机对Class文件采用按需加载的方式,加载某个类class文件时,JVM采用的是双亲委派机制,把能否加载这个类的操作交给父加载器处理。
-
如果一个类加载器收到了类加载的请求,它自己并不会先加载,而是交给父类加载器去加载。
-
如果父类加载器还存在父类加载器,则继续向上,直到最上层引导类加载器。
-
如果父加载器可以完成该类的加载,就成功返回,如果父类加载器无法完成加载任务,子类加载器才会自己加载。
举例:自定义String类,并编写main方法执行
执行main方法后会报错,错误中指明未找到main方法,因为Java中的类加载是采用双亲委派机制,我们自己编写的String类的应用程序类加载器收到加载的请求后,会将加载任务向上委托,委托给父类加载器扩展类加载器,但是扩展类加载器不能加载此类,则继续向上委托加载任务给引导类加载器,引导类加载器发现可以加载此类,但是String是Java核心类库中的类,不存在main方法,故报错。
双亲委派机制的优势
1. 防止加载相同的类 2. 防止Java核心API被篡改、破坏
沙箱安全机制
自定义String类,会率先使用启动类加载器加载,对Java核心API代码的保护。
其他
如果一个类是由用户类加载器加载的,则JVM会讲这个类加载器的引用保存在方法区中。
类的主动使用和被动使用
区别:会不会导致类的初始化
- 主动使用(可初始化)
- 创建类的实例
- 访问某个类或接口的静态变量
- 反射
- 初始化一个子类
- 被动使用(不可初始化)