类的生命周期

一个类被加载到内存到拆卸出内存为止,它的整个生命周期包括7个阶段:加载–>验证–>准备–>解析–>初始化–>使用–>拆卸。其中验证、准备和解析3个阶段称为连接(Linking)。

类的加载时机

什么时候触发类的加载?其实JVM规范并没有严格规定类加载的时机,而是由具体的虚拟机实现自由把我时机。但是虚拟机规范严格规定了有且只有5中情况必须立即对类进行“初始化”(加载、验证、准备自然在此之前)。

  • 遇到new,getstatic,putstatic或invokestatic这4条字节码指令时,如果类没有被初始化,则需要触发其初始化。这4条指令对应的场景就是使用new创建一个对象时,获取类静态变量时,设置类的静态变量时,调用类的静态方法时。
  • 使用反射对类的进行调用时。
  • 当初始化一个类时,如果其父类还未初始化则先初始化父类。
  • 当虚拟机启动时,自动初始化主类(就是包含main方法的类)。
  • 当使用JDK1.7的动态语言时,如果一个java.lang.invoke.Methodhandle实例最后解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,如果这个方法句柄对应的类没有初始化,则需要进行初始化。

这5中场景称为对类的主动引用,换句话说当对类进行主动引用时就会触发类的初始化,除此之后其他对类的引用方式都不会触发类的初始化,称为被动引用。

例如下面就是对类一个被动引用的例子:

public class SuperClass{
		static{
				System.out.println("SuperClass init!");
		}
        public static int value = 123;
}
public class SubClass extends SuperClass{
		static{
				System.out.println("SubClass init!");
		}
}
public class Test{
		public static void main(String[] args){
				System.out.println(SubClass.value);
		}
}

这段代码执行的结果为"SuperClass init!",“123”,这说明SubClass并未被初始化,只有父类SuperClass触发了初始化。对于静态变量只有定义该变量的类才会初始化。

类的加载过程

类的生命周期中前5个阶段就是类的加载过程:加载、验证、准备、解析和初始化。

加载:就是根据类的全限定名获取class文件的字节流,转化为方法区的数据结构,并在内存中生成Class对象。

验证:验证分为3方面的验证,分别为文件格式的校验、元数据的校验和字节码校验。

  • 其中文件格式校验包含的校验点有:是否以0XCAFEBABE魔数开头,主次版本号是否在虚拟机的允许范围之内等等。
  • 元数据校验,验证点有这个类是否继承了父类(除了Object类,所有的类都应有父类),这个类是否继承了不允许继承的类(例如使用final修饰的类),如果这个类不是抽象类是否实现了父类的抽象方法或接口等等。
  • 字节码验证,主要是通过数据流和控制流分析,确定程序语义是否是合法的且符合逻辑的。

准备:准备阶段是正式为类变量分配内存并设置初始值的阶段,这些类变量所使用的内存均在方法区分配。这里的类变量指的是使用“static”修饰的变量。设置的初始值是数据类型的零值,并不是源码中指定的值,例如private static int v = 123,v被初始化为0而不是123(初始化成123是在“初始化”阶段)。

解析:解析阶段将常量池中的符号引用替换成直接引用的过程。

**初始化:**初始化是类加载过程的最后一个阶段,从此阶段开始才开始真正执行类中定义的Java代码。在准备阶段已经进行过一次类变量的初始值设置(初始为变量类型对应的零值),该阶段就是执行<clinit>()方法的过程。方法是由编译器帮我们生成的。在编译阶段,编译器会按照顺序收集static静态代码块、static变量赋值语句形成方法。

类加载器

从JVM角度来看只有两种类加载器:启动类加载器(Bootstrap ClassLoader)和其他类加载器。启动类加载器在HotSpot虚拟机中是使用C++实现的,主要用于加载JAVA_HOME/lib目录下的类。其他类加载器都是使用Java语言实现,全部继承自java.lang.ClassLoader类。

从开发者角度分为3中类加载器

  • 启动类加载器,上边已经介绍过。
  • 扩展类加载器,用于加载JAVA_HOME/lib/ext目录下的类。
  • 应用程序类加载器,它负责加载用户classpath下的类。

双亲委派模型:当一个类加载器收到类的加载请求时,它总是委托其父类来加载此类,这是一种安全机制,能够有效避免加载到与java类库同名的外部类。
在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐