JVM-底层之类加载

Klass 模型

  • klass模型类的继承结构
    在这里插入图片描述
    由上面图可以看出:类的元信息是储存在元空间里面的也就是方法区
    在JVM中,普通类对应的是instanceKlass类的实例。
  • InstanceMirrorKlass:用于表示java .lang.Class,java 代码中获取到Class对象,实际上就是这个C++ 类的实例。储存在堆区也就是我们反射得到的Class对象是一个OOPKlass。
  • InstanceRefKlass:用于表示java/lang/ref/Reference类的子类
  • InstanceClassLoaderKlass:用于遍历某个加载器加载的类
    Java中的数组不是静态数据类型,是动态数据类型,即是运行期生成的,Java数组的元信息用ArrayKlass的子类来表示
  • TypeArrayKlass:用于表示基本类型的数组
  • ObjArrayKlass:用于表示引用类型的数组

类的加载过程

类的生命周期是由7个阶段组成,但是类的加载说的是前5个阶段
在这里插入图片描述
加载,验证,准备,初始化和卸载这5个阶段的顺序是确定的。类型的加载过程必须安装这个顺序按部就班的开始。也就是说开始是按照这个顺序,不过并不是前面完成后面才可以进行。

加载

  • 通过类的全限定名获取存储该类的class文件的二进制字节流(这里没有要求在那里获取,所以可以通过拓展,网络,zip 等等都可以获取到)

  • 将这个字节流锁代表的静态存储结构转化为方法区的运行时数据结构

  • 在内存中生成一个代表这个类的java.lang.Class对象,也就是InstanceKlass。作为运行时数据结构放入方法区
    上面3个点,是连接文件和Openjdk源码的关键。所以写的程序需要完成上面3个效果就可以了。

验证

  • 文件格式验证
    验证字节流是否符合Class 文件格式的规范,并且能被当前版本的虚拟机处理。
  • 元数据验证
    对字节码描述的信息进行语义分析。是否有父类,父类是否合理等等。。。
  • 字节码验证
    通过数据流分析,和控制流分析,确定语义是否合法,合理。第二阶段对元数据信息中的数据类型校验完后,要对Code 进行分析。
  • 符号引用验证
    这个阶段发生在虚拟机将符号引用转为直接引用的时候(这是在解析阶段中发生)。

准备

准备阶段是正式为类中定义的变量(静态变量)分配内存并设置变量初始值的阶段。所谓的初始化值就是零值,只有被final修饰的值是在编译的时候就已经分配了,准备阶段会显示初始化。

解析

将符号引用转化为直接引用。

  • 符号引用
    符号引用以一组符号来来描述所引用的目标,使用时候定位到目标即可。
  • 直接引用
    直接引用是可以直接指向目标的指针,相对偏移量,或者是句柄。
    解析动作主要是针对:类或者接口,字段,类方法,接口方法,方法类型等等。

初始化

初始化阶段会根据程序员通过代码指定的计划区初始化类变量和其他资源。运行clinit()方法。实例变量会在对象创建的时候在堆空间中分配实例变量空间并进行默认赋值。

  • clinit()生成过程
    • clinit()是由编译器自动收集类中所有变量的赋值动作和静态语句块。收集顺序是由语句在源文件在出现的顺序决定。
    • clinit()方法与类的构造函数不一样,它不需要显示调用父类构造器,因为jvm 会保证子类clinit() 执行前,父类已经执行。
    • 执行方法会同步加锁
      初始化开始条件
    • 遇到new, getstatic,putstatic,invokestatic
      • 遇到New关键字实例化对象
      • 读取获取设置一个类型的静态字段
      • 调用一个静态方法
    • 使用reflect 反射
    • 初始化类的时候,如果父类没有初始化,需要先初始化父类
    • 虚拟机启动的时候,加载初始化main 这个类
    • 使用JDK7加入的时候,新加入的动态语言支持时候。
    • 使用JDK8新加入默认方法,如果有接口实现这个类发生初始化,那么这个接口在其之前被初始化。

猜你喜欢

转载自blog.csdn.net/null_zhouximin/article/details/112493127
今日推荐