类文件结构,类加载机制

   上一章讲的GC垃圾回收机制以及算法和收集器,这一章节来看一下类文件结构,类加载机制。

 一.类文件结构

   都知道类文件是可以由.java文件通过编译器编译成.class文件,当并非只有一种方式,java虚拟机是支持跨平台的,也可以其他语言编译生成class文件,甚至可以自己通过16进制编辑器来写class文件。弹药自己写class文件,格式掌握是必不可少的。class文件是以8个字节为单位的二进制流,里面的数据项目紧凑而有序的排列着。当遇到8个字节以上的数据项目时,则会按照高位在前的方式分割成若干8个字节进行存储。

   class文件结构只做简单的介绍。class文件采用一种类似于c语言的伪结构进行存储,这种结构中只有两种数据类型:无符号数和表,其中无符号数是属于基本类型,按照u1,u2,u4,u8分别代表一个字节,2个字节,4个字节以及8个字节的无符号数,表示由多个无符号数或多张表组成的结构,习惯性以_info结尾,其实整个class文件就是一张紧凑而有序的一张表,如下图:

                 

                 (图片来源于:-气宗】深入理解Java虚拟机:JVM高级特性与最佳实践(最新第二版))

    其中前四个字节为魔数(magic),具体为0xCOFEBABE(咖啡宝贝),它的主要作用是用来判断class文件是否能被虚拟机所接受。接下来两个是次版本号和主版本号,分别占位2个字节,如果class文件的版本号高于jdk的版本号,则虚拟机将会拒绝执行此class文件。接下来就是常量池大小和常量池,分别代表2个字节以及表,常量池中一般存储一些字面量(文本字符串和生命为final的常量)和符号引用(限定名类符号引用和方法符号引用以及field符号引用等)。接下来就是访问标志,它主要用于识别类或接口的访问信息,如类和接口的访问权限(public,private等),是否是abstract类型,是否是被final修饰等。然后就是各为2个字节的类索引,父类索引以及接口索引集合数量和接口索引集合,这三项数据主要用于确定这个类的继承以及实现关系。java是不支持多继承的,如果类继承多个父类,那么在类加载阶段出错。接下来是字段,方法以及属性数量和表集合分别用于描述类或接口中声名的变量,方法以及某些场景专有的信息。

  二.类加载机制    

    重点讲解一下类加载机制,类加载的整个生命周期分为:加载,连接(验证,准备,解析),初始化,使用,卸载7个阶段。其中加载,验证,准备,使用,卸载按照它们开始的顺序依次执行(注意是开始顺序,而不是依次进行或完成,准备也有可能在加载开始进行的时候执行)。类加载发生的时机主要是有初始化阶段来触发的,对加载没有强制约束。下面五种情况会对类进行初始化:

    1)使用new,getstatic,putstatic,invokestatic这四个字节码指令的时候。如果类没有进行初始化,则先触发初始化。如下几个场景会触发上面四个指令:用new关键字实例化一个对象时,获取或设置一个类的静态变量,调用类的静态方法的时候。

    2)使用java.lang.reflect包中的方法进行反射调用方法的时候,如果没有对类进行初始化,则会先对类进行初始化。

    3)如果类有继承父类,当初始化这个类的时候,如果父类没有初始化,则先会对父类进行初始化。

    4)当虚拟机启动的时候,如果勒种包含main主方法,则会初始化这个类。

    5)当使用java.lang.invoke.MethodHandle实例最后的解析结果有REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,并且方法句柄对应的类没有进行初始化,则需要对类进行初始化。

   1.类加载过程

  上面写完了一些类加载的时机,接下来讲一下类加载的5个过程。

    1.1.加载

      加载是类加载阶段的第一步,它主要分为如下几个步骤:

        1)通过类的全限定名来获取类的二进制字节流。

        2)将字节流中的静态数据结构转化为方法区中运行时数据结构。

        3)在内存中生成对应的java.lang.class对象,作为方法区中这个类的访问入口。

    1.2.验证

      验证是类加载阶段的第二步,属于连接阶段的第一步,它主要是分为文件格式验证,元数据验证,字节码验证以及符号引用验证。

      文件格式验证主要是验证字节流是否符合class文件格式规范,主要有魔数验证判定是否能被虚拟机接受,版本号验证判定class文件版本号是否超出jdk要求的版本号,常量池验证查看其中的常量是否有不合要求的常量等等。

      元数据验证主要是对字节码描述的信息进行语义分析,以保证符合java语义规范要求。比如当前类是否实现了接口中的全部方法,是否有多重继承等。

      字节码验证主要是针对类中的方法体以及其中的语句指令,比如保证字节码指令跳转的正常,字节码指令运算的时候不会有类型转换,赋值等错误

      符号引用验证是为解析过程而作准备的,再讲符号引用解析为直接引用的时候,如果符号引用没有指向对应的对象,则它的匹配性校验就会出错。一般有对类,方法以及字段的符号引用进行验证。

    1.3.准备

      准备阶段主要是对类变量(被static修饰的变量)分配内存并默认初始化的过程,这里的初始化一般是变量类型对应的零值。例如public static int age = 23;这个阶段只会将age变量初始化为0,而23赋值的过程是在初始化阶段执行pustatic指令的时候初始化为23的。但是如果public static final int age = 23;则这个准备阶段就会赋值为23,这是由于age变量在编译期就将23赋给age并将其放入class文件对应的常量池中。

    1.4.解析

       解析阶段就是对class文件常量池中的符号引用解析为直接引用,符号引用一般包括类符号引用,方法符号引用以及字段符号引用。

    1.5.初始化

      初始化阶段就是按照用户程序主观来初始化类变量或其他资源。初始化过程实际就是执行<clinit>()方法的过程。

        1)<clinit>方法是由编译器自动收集所有类变量的赋值动作和静态代码块中的语句合并产生的。静态语句块只能访问之前静态语句块中的变量,对之后的变量只能赋值,不能访问。

        2)<clinit>方法不需要像构造器显示调用,他能保证父类的<clinit>方法一定先于子类的执行,所以最先执行的一定是java.lang.object的<clinit>方法。

        3)父类中定义的静态语句块一定是优先于子类的执行,这是由于2)中<clinit>方法优先于子类执行。

        4)如果类中没有类变量的赋值操作也没有静态语句块,也就有可能没有<clinit>方法。

        5)接口中虽然没有静态语句块,但有变量初始化赋值操作,因此他也会有<clinit>方法。接口中的<clinit>方法不必优先于父接口的执行。只有当父接口中的中的初始化变量使用时,才会执行<clinit>方法,接口实现类在初始化的时候不会执行接口中的<clinit>方法。

        6)虚拟机会保证<clinit>方法在多线程环境下被正确的加锁,同步。

  2.类加载器

    2.1 类与类加载器

      对于任意一个类,都需要由加载它的类加载器和这个类本身确定其在虚拟机中的唯一性,每一个类加载器,都拥有独立的类名称空间。比较两个类是否相等,只有这两个类是在同一个类加载器加载的前提下才有意义。两个相同的class文件如果被不同的类加载器加载,那这两个类是不等的。

    2.2 双亲委派模型

      java虚拟机中只有3种类加载器:

      1)启动类加载器(Bootstrap ClassLoader),它是由c++编写的,属于虚拟机的一部分。这个类加载器负责将存放在<JAVA_HOME>\lib目录下鞥呗虚拟机是别的类库加载到虚拟机内存中。入座用户自定义类加载器需要请求委派给引导类加载器,知己使用null来代替即可。

      2)扩展类加载器(Extension ClassLoader),由java实现,独立于虚拟机外部,继承于java.lang.ClassLoader。它负责加载<JAVA_HOME>\lib\ext目录。

      3)应用程序类加载器(Application ClassLoader),由java实现,独立于虚拟机外部,继承于java.lang.ClassLoader。他负责加载用户类路径上指定的类库。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以也称之为系统加载器。

      双亲委派模型的工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求为派给父类家在崎岖完成,每一个层次的类加载器都是如此。因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当副类加载器反馈自己无法完成这个请求(它的搜索范围没有这个类)时,子加载器才会尝试去加载。

    

    本文仅仅是个人对深入理解虚拟机的笔记整理,如有不当之处欢迎点评,转载请注明:https://www.cnblogs.com/qven/p/8832498.html

 

  (参考文献:-气宗】深入理解Java虚拟机:JVM高级特性与最佳实践(最新第二版).pdf)

 

   

 

    

posted @ 2018-11-27 20:21  qven  阅读(179)  评论(0编辑  收藏  举报