我的面试经之JVM(二)类加载器子系统ClassLoader类的加载过程

ClassLoader,类加载器子系统:

  负责从文件系统或网络中读取class文件,class文件所在开头有特定的文件标识

  ClassLoader只负责class文件的加载,是否能运行,则由Execution Engine决定

  加载类的信息存放在一块称为方法区的内存空间。除了类的信息,方法区还可能会存放运行时常量池的信息,还可能包括字符串字面量和数字常量(这部分常量信息是class文件中常量池部分的内存映射)

举例:

类的加载过程:

  加载阶段:  

       1、通过类的全限定名获取定义此类的额二进制流

       2、将字节流所代表的静态存储结构转化为方法区(<7叫永久代)的运行时数据结构

       3、在内存生成一个代表此类的java.lang.Class对象,作为方法区这个类的各种数据访问入口

     加载.class方式:

                                                   

  链接(linking)阶段:      

         验证(Verify):   确保Class文件额字节流中包含的文件信息符合当前虚拟机要求,保证被加载类的正确性,不危害虚拟机自身安全

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

         准备(prepare):  为类变量分配内存并设置该类变量为0值

                  final修饰的static不包含在内,因为这样修饰的变量就成为一个常量,编译时就会分配,这个阶段会显示初始化

                  这里不会为实例变量分配初始化,类变量会分配在方法区,实例变量会随着创建对象分配到Java堆中

         解析(Resolve): 将常量池内的符号引用转换为直接引用的过程【符号引用就是一组符号描述所引用的目标。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄

                  事实上,解析操作是在JVM初始化之后进行的  

                  解析动作主要针对类或接口、字段、类方法接口方法、方法类型等。       

  

  初始化(Initialization)阶段:

                  初始化就是执行类构造器方法<clinit>()的过程

                  此方法不用定义,由javac编译器自动搜集类中的所有类静态变量(口误没说)赋值动作和静态代码块中的语句合并而来【如果没有赋值动作或者静态代码,那么也不会有<clinit>()

                  构造器方法中指令按照在源文件中出现的顺序执行

                  <clinit>()不同于类的构造器。(关联:构造器是虚拟机视角下的<init>()方法);【任何一个类至少有一个构造器,就是init构造器】

                  若该类具有父类,JVM会保证子类的<clinit>()执行前,父类的<clinit>()执行完毕 ,如下图注释。

                                                                                                 

                  虚拟机必须保障一个类的<clinit>()方法在多线程下被同步加锁

注意:静态代码块中可以给变量赋值,但是在声明之前不能调用,如下图:

         

原文地址:https://www.cnblogs.com/Timeouting-Study/p/12445580.html