jvm类加载机制

  虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。

  

  类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期如图7-1所示。

  何时开始加载?没有强制约束。

  何时开始初始化?有且仅有5中情况必须立即对类进行初始化(加载、验证、准备自然在初始化之前)

    1.遇到new、getstatic、putstatic或invokestatic这四条字节码指令时。通俗讲就是new一个对象、读取或设置static修饰的字段以及调用类的静态方法的时候。

    2.对类进行反射调用的时候。

    3.初始化一个类,发现其父类还没初始化,这时父类就要先被初始化。

    4.虚拟机启动时,main()函数所在的类会被初始化

    5.使用动态语言支持时(这种笔者没有接触过,所以没有什么概念)

    需要注意的是,第三种情况,接口和类有所区别,接口在初始化时不要求其父接口初始化,只有在真正使用到父接口时才会初始化父接口。

  类加载的过程

    1.加载:在加载阶段,虚拟机完成以下3件事

       (1)通过一个类的全限定名来获取定义次类的二进制字节流(Class文件或是网络获取等)

         (2)将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构(可以理解为把Class文件中的常量池放在方法区,此时作为运行时常量池)。

       (3)在主内存(可以理解为堆,在一些虚拟机中是放在方法区中的)中生成一个此类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

    2.验证:这一步是为了确保Class文件的字节流中所包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。因为java是相对安全的语言,但是Class文件不一定要求用java源码编译而来,虚拟机如果不检查输入的字节流,诸如数组越界之类的很可能导致系统崩溃。主要有文件格式验证、元数据验证、字节码验证、符号引用验证。

    3.准备:准备阶段是正式为类变量(被static修饰的变量)分配内存并设置变量初始值的阶段。

        需要注意的是,此时是的变量初始值是对应的变量类型的零值。

    4.解析:虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类福海引用进行。下面以类或接口的解析为例分析讲解

        假设当前代码所处的类为D,如果要把一个从未解析过的符号引用N解析为一个类或接口C的直接引用,那虚拟机要完成以下步骤:

        虚拟机将会把代表N的全限定名(包名+类名)传递给D的类加载器去加载C类。如果没有出现异常,那么C在虚拟机中实际已经成为一个有效的类或接口了。

        什么是符号引用?

          一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。

        什么是直接引用?

        直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。不然指向哪?

        什么是字段?类的成员

    

    5.初始化:初始化阶段是执行类构造器<clinit>()方法的过程,是类加载过程的最后一步。<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句结合产生的。

    值得注意的是,构造函数(或者说实例构造器<init>()方法)与类构造器(<clinit>())不同,类构造器是在类加载过程中执行的,而实例构造器是在实例化对象的时候执行的。所有类构造器一定优于实例构造器执行。

      

        

 

      

 

posted on 2018-12-10 13:27  yfyfyf947  阅读(121)  评论(0编辑  收藏  举报