1.什么是类加载?
类加载就是在JVM虚拟机中通过classLoader类加载把*。class字节码加载到内存中,并对字节码文件内容进行验证,准备。解析和初始化,最终形成可以被虚拟机直接使用的对象,这个过程称作l类加载。
2.类的生命周期

类的生命周期包括以下 7 个阶段:
●加载(Loading)
●验证(Verification)
●准备(Preparation)
●解析(Resolution)
●初始化(Initialization)
●使用(Using)
●卸载(Unloading)
结束类的生命周期有以下四种方式:
●执行System.exit()方法
●程序正常执行结束
●程序执行中遇到了异常或错误而异常终止
●操作系统出现错误或强制结束程序而导致JVM虚拟机进程终止
3.累加载过程
加载、验证、解析、准备、初始化
3.1加载
加载阶段:
- 通过类的完全限定名称获取定义该类*.class字节码文件的二进制流。
- 将该字节流表示的静态存储结构转换为Metsspace元空间区的运行时存储结构。
- 在内存中生成一个代表该类的Class对象,作为元空间中该类各种数据的访问;
在类加载结束后,*.class字节码文件的类信息数据就会存储在元空间,同时在JVM虚拟机堆区生成一个该类的Class对象。
3.2验证
在验证阶段,JVM主要确保 *.class字节码文件中包含的信息符合当前虚拟机的要求,并不会危害虚拟机的安全。
- 文件格式验证:验证字节流是否符合*.class字节码文件格式的规范,且能被当前版本的虚拟机处理。
- 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。
- 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
- 符号引用验证:发生在虚拟机将符号引用转化为直接引用的时候 , 这个转化动作将在连接的第三个阶段------解析阶段中发生。确保解析动作能正常执行。
为什么需要验证?
Java语言本身是相对安全的语言,但*.class字节码文件并不一定要求用Java源码编译而来,可以使用任何途径, 甚至可用十六进制编译器直接编写来产生*.class字节码文件。
类的加载是JVM针对*.class字节码文件的读取加载机制,所以虚拟机如果不检査输入的字节流,可能会因为载入了有害的字节流而导致系统崩溃,所以验证是虚拟机对自身保护的一项重要工作。
另外,通过类加载机制的验证环节,可以增强解释器的运行期执行性能。因为,解释器在运行期间无需再对每条执行指令进行检查。
3.3准备
类变量是被static修饰的变量,准备阶段为类变量分配内存并设置初始值,使用的是元空间区的内存。
实例变量不会在这阶段分配内存,它会在对象实例化时,随着对象一起被分配在堆中。应该注意到,实例化不是类加载的一个过程,类加载发生在所有实例化操作之前,并且类加载只进行一次,实例化可以进行多次。
初始值一般为0;
3.4解析
将常量池的符号引用替换为直接引用;
3.5初始化
初始化阶段才真正开始执行类中定义的 Java 程序代码。初始化阶段是虚拟机执行类构造器 <clinit>()方法的过程。在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。
<clinit>()是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序由语句在源文件中出现的顺序决定。所以,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。
4.类加载的时机
4.1主动引用
- 当遇到 new 、 getstatic、putstatic 或 invokestatic 这 4 条字节码指令时,比如 new 一个对象,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。
- 使用 java.lang.reflect包的方法对类进行反射调用时如 Class.forname("..."), 或newInstance() 等等。如果类没初始化,需要触发类的加载。
- 加载一个类,如果其父类还未加载,则先触发该父类的加载。
- 当虚拟机启动时,用户需要定义一个要执行的主类 (包含 main() 方法的类),虚拟机会先加载这个类。
- 当一个接口中定义了 JDK8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了加载,则该接口要在实现类之前被加载。
4.2被动引用
除主动引用之外,所有引用类的方式都不会触发加载,称为被动引用。
5.类加载器
5.1什么是类加载器:
在类加载过程中,通过类的完全限定名,获取描述类的二进制流的实现类,被称为"类加载器"。
5.2类加载器分类
启动类加载器
该类加载器负责将存放在 <JRE_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。
扩展类加载器
该类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现,负责将 <JRE_HOME>/lib/ext 或者被 java.ext.dir 系统变量所指定路径中的所有类库加载到内存中,例如swing系列、内置的js引擎、xml解析器等以javax开头的扩展类库都是由扩展类加载器加载,开发者可以直接使用扩展类加载器。
应用程序类加载器
该类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现。

5.3什么情况要自定义类加载器?
1.隔离加载类。在某些框架内进行中间件与应用的模块之间进行隔离,把类加载到不同的环境。
2修改类加载方式。
3扩展加载源。比如:从数据库、网络、电视机顶盒进行类加载。
4防止源码泄漏。比如:编译时字节码进行加密,需要通过自定义类加载器对字节码进行解密还原。