前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >笔记06 - Class对象在执行引擎中初始化的过程

笔记06 - Class对象在执行引擎中初始化的过程

作者头像
码农帮派
发布2020-12-29 09:49:09
8390
发布2020-12-29 09:49:09
举报
文章被收录于专栏:码农帮派

一个class文件被加载到内存中需要经过三个步骤:装载、链接、初始化。其中链接可以细分为验证、准备、解析三个步骤。

装载

装载是指JVM查找到class文件、读取class文件生成字节流,并根据字节流创建java.lang.Class对象的过程。

这一过程需要完成的三件事:

  • 1. ClassLoader通过一个类的全名(包名+类名)来查找class文件,读取该文件并生成字节流。其中class字节码文件的来源不一定是class文件,也可以是jar包、zip包,也可以是来源于网络的字节流;
  • 2. 把class字节码文件中的各个部分(常量池、类/接口、集合等等)解析成JVM中特定的数据结构,并存储到方法区中。
  • 3. 在内存中创建java.lang.Class对象
  • 加载完class字节码文件之后,程序在运行过程中创建的类对象会使用这个Class类型的类对象进行创建。

加载时机

一个项目在编译的时候通常会生成大量的class文件,当程序运行的时候,JVM并不会将这些class文件全部加载到内存中,不同的虚拟机中加载class的时机并不相同,但是下面两种情况一般会对class进行装载操作。

隐式装载:在程序运行过程中,当碰到通过new等方式进行对象创建的时候,系统会隐式的调用ClassLoader去装载对象的class文件到内存中;

显式装载:在代码中主动调用Class.forName等方法也会触发class的装载,这种方法通常称为显式装载。

链接

链接的过程分为3步:验证、准备和解析。

验证

验证是链接的第一步,目的是保证class文件中字节流的正确性。包括文件格式的验证(验证字节码文件中16进制数据的正确性,比如魔数等等)、元数据验证、字节码检验、符号引用检验。

准备

准备是链接的第二步,这一阶段是为了给类中的静态变量分配内存,并给静态变量设置为零值。e.g.

代码语言:javascript
复制
piblic static int value = 100;

在准备阶段,JVM会为value分配内存,并为其设置初始值为0.而真正的值100,会在后续的初始化阶段进行设置。此阶段进行内存分配仅包括类变量,而不包括实例变量(实例变量会在对象实例化的时候随着对象内存的分配一起分配到堆内存中)。

类的静态常量

代码语言:javascript
复制
public static final int value = 100;

以上代码中声明的静态常量会在准备阶段就为value分配内存,并设置为100。

Java中基本类型的零值:

基本类型(int、long、short、char、boolean、float、double)的默认值为0

引用数据类型的零值为null

解析

解析是链接的最后一步,这一阶段的任务是将常量池中的符号引用转化为直接引用,也就是将16进制码中的符号引用关系转化成JVM中内存引用。在这一阶段,JVM会将常量池中的类、接口、字段名、方法名转换成具体的内存地址。

初始化

这是class加载的最后一步,这一阶段主要是执行类构造器的方法的过程,并真正初始化类变量(静态变量)。

初始化的时机

class的装载,JVM并没有严格的规定具体执行的时机,但是对于初始化阶段,JVM严格的规定了class初始化的时机,主要有以下几种情况会触发class的初始化:

  • 1. 虚拟机启动时,初始化包括main方法的主类;
  • 2. 遇到new指令创建对象实例的时候,要是目标对象类没有被初始化则执行初始化流程;
  • 3. 遇到访问静态方法或静态字段的时候,如目标对象类没有被初始化则执行初始化流程;
  • 4. 子类的初始化过程要是发现其父类还没有被初始化,则需要首先执行父类的初始换流程;
  • 5. 使用反射API进行反射调用,如目标对象类没有初始化则执行初始化流程;
  • 6. 首次调用java.lang.invoke.MethodHandle实例,需要初始化MethodHandle指向的方法所在的类。

初始化类变量

在初始化过程中,只会初始化与类相关的静态赋值语句,也就是使用static关键字修饰的信息,而没有static修饰的语句会在实例化对象的时候才执行。

上面会触发类初始化的6种情况称为是主动引用,除了上述6种情况之外的引用方式称为被动引用,被动引用不会触发class的初始化。

最为典型的被动引用,在子类中调用父类的静态变量:

上面的代码可以看到,Child继承了Parent类,如果直接使用Child来访问Parent的value静态变量,则不会初始化Child类。

下面的代码中通过Child类访问了Child的父类Parent的静态变量:

打印结果:

从上面的打印结果可以看出,JVM只初始化了Parent,而Child并没有初始化。

class初始化过程中对象的创建顺序

类对象的初始化顺序为:

静态变量/静态代码块 -> 普通代码块 -> 构造函数

  • 1. 父类的静态变量和静态代码块
  • 2. 子类的静态变量和静态代码块
  • 3. 父类的普通成员变量和普通代码块
  • 4. 父类的构造函数
  • 5. 子类的普通成员变量和普通的代码块
  • 6. 子类的构造函数
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农帮派 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档