初识Java虚拟机(6)类文件加载

    虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。在Java语言中,类型的加载、连接和初始化过程都是在程序运行期间完成的,为Java程序提供了高度的灵活性。
   类从加载到内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段 。其中,验证。,准备和解析合称为连接(Linking)。
    (1)除了解析,其他其他6步的顺序是固定的,而解析在某些情况下可以在初始化后开始,这是为了支持Java的运行时绑定。
    (2)有且仅有以下五几种情况会立刻对类进行初始化:1、遇到new等字节码指令,如果类没有进行初始化,则需要先触发其初始化。常见场景是:使用new创建新对象、读取或设置类的静态字段、调用静态方法。2、对类进行反射调用的时候。3、初始化一个类时,若其父类未初始化,则需要先触发对其父类的初始化。4、当虚拟机启动时,用户需要指定一个要执行的类(包含main()方法的那个类)。5、当使用JDK1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle 实例最后的解析结果的方法句柄没有进行过初始化,则需要先触发其初始化。以上五种称为主动引用,其他的引用不会触发初始化,称为被动引用,例如:1、通过子类引用父类的静态字段,不会导致子类初始化,只有直接定义这个字段的类会初始化。2、通过数组定义来引用类,不会触发此类的初始化,另外一提数组会触发newarray的字节码指令,它是Object 的子类。3、static final 修饰的常量在编译时会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。另外对于接口来说,与类不同,接口的立即初始化没有第三点,即不要求父类接口全部完成初始化,只在真正使用了父接口时才会初始化。
    类的加载过程
    (1)加载
    在加载阶段,虚拟机要完成三件事:1、通过一个类的全限定名来获取类的二进制字节流。2、将这个字节流所代表的静态储存结构转化为方法区的运行时数据结构。3、内存中生成java.lang.Class对象(HotSpot中存于方法区),作为方法区这个类的各种数据的访问入口。第一件事的各种奇技淫巧:从ZIP包中读取(JAR/EAR/WAR)、网络中获取(Applet)、运行时生成(代理类)、其他文件生成(JSP)···
    (2)验证
    验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。对于Java语言本身,一些无法做到的事情比如数组越界等,在字节码语言层面上是可以做到的。所以虚拟机如果不检查输入的字节流,很可能因为载入了有害的字节流而导致系统的崩溃,所以验证是虚拟机对自身保护的一项重要工作。
    (3)准备
    准备阶段是正式为类变量分配内存空间并设置类变量初始值的阶段。这些变量所使用的内存都在方法区中分配,注意这里的变量是指static修饰的变量而不是实例变量,实例变量会分配在java堆中;并且初始值是为‘零’值而不是Java代码中设置是值,static int I=123;是为编译后,存放于<clinit>()方法中,该动作在初始化阶段才会进行。如果为 final static,则会被直接设置为该值。

    (4)解析

    解析阶段是虚拟机将常量池内的符号引用替换为直接引用过程,下面简单介绍下符号引用和直接引用的关联。

        符号引用:以一组符号来描述所引用的目标,可以是任何字面量,只要能无歧义地定义到目标即可。目标不一定在内存中。

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

    (5)初始化

    到了初始化阶段,才真正开始执行类中定义的Java程序代码(字节码)。初始化阶段是执行类构造器<clinit>()方法的过程,它的特点如下:

        1、由编译器自动收集类中的所有变量的赋值动作和静态语句块中的语句合并而成,顺序有Java源文件决定。

        2、<clinit>方法与类的构造函数不同,它不需要显式地调用父类构造器,虚拟机会保证在子类<clinit>方法执行前已经调用了父类方法。

        3、<clinit>不是必须的,若无静态语句块,也无变量的赋值操作,可以没有该方法。

        4、接口中不能使用静态语句块,但仍有变量初始化的赋值操作。此外不需要先执行父类方法。       

        5、虚拟机能保证<clinit>方法的加锁、同步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值