类加载

一、JVM类加载机制

  (1)类加载时机

    下图是类的生命周期。

    

    其中 加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,而解析阶段则不一定,它在某些情况下可以   在初始化之后再开始,这是为了支持JAVA语言的运行时绑定。

    初始化阶段:有且只有5中情况下必须立即对类进行初始化

    1)new 一个对象(new一个对象数组时是不会进行初始化操作的),读取或设置类的静态字段,调用类的静态方法。

    2)反射调用的时候,如果类没有初始化,则需要先触发初始化

    3)当初始化一个类时,如果发现父类没有被初始化,则需要先初始化父类

    4)当虚拟机启动的时候,main()方法所在的主类会被先初始化

    5)当使用JDK1.7的动态语言支持时

二、类加载过程

    加载:

    1. 通过一个类的全限定名来获取此类的二进制字节流(例如class文件)。
    2. 将这个字节流所代表的的静态存储结构转换为方法区的运行时数据结构。
    3. 在内存中生成代表这个的类的java.lang.Class对象,作为方法区中这个类各种数据的访问入口

    验证:

      举例:将一个对象转换为它并未实现的类型,如果这样做了,编译器拒绝编译

    准备:

      正式为静态变量分配内存并设置类变量初始值,这些变量所使用的内存都在方法区中进行分配

      通常情况下初始值是如下表所示:

      

      如果静态变量是final类型的,即字段属性表中存在ConstantValue属性,那么在准备阶段就会被初始化为ConstantValue所指定的值。

      例如 public static final int value = 123231;  //value在准备阶段直接被赋值为123231,而不是0。

    解析:将常量池中的符号引用替换为直接引用的过程  

    1.符号引用(Symbolic References):

    符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可。例如,在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等类型的常量出现。符号引用与虚拟机的内存布局无关,引用的目标并不一定加载到内存中。在Java中,一个java类将会编译成一个class文件。在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language(假设是这个,当然实际中是由类似于CONSTANT_Class_info的常量来表示的)来表示Language类的地址。各种虚拟机实现的内存布局可能有所不同,但是它们能接受的符号引用都是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。

 
    2.直接引用:
 直接引用可以是
(1)直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)
(2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)
(3)一个能间接定位到目标的句柄
直接引用是和虚拟机的布局相关的,同一个符号引用在不同的虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标必定已经被加载入内存中了。
    初始化:
     类初始化阶段是类加载过程的最后一步,到了初始化阶段,才真正开始执行类中的定义的java代码。
    初始化阶段是执行类构造器<clinit>()方法的过程。clint()方法是由编译器自动收集所有静态变量的赋值动作和静态语句块(static{})中的语句合并而成的,
      • 编译器的收集顺序是由语句在源文件中出现的顺序决定。
      • 父类的clint方法会先执行,也就意味着父类的静态语句块要优先于子类的静态变量赋值操作。
      • 虚拟机会保证一个类的clinit方法在多线程中被正确的加锁,同步。

三、类加载器

  

 

        
      
    
 
posted on 2018-03-12 15:24  花溪的小石头  阅读(163)  评论(0编辑  收藏  举报