JVM类加载机制
JVM类加载机制类的生命周期类的加载时机类的加载过程类加载器类的生命周期一个类被加载到内存到拆卸出内存为止,它的整个生命周期包括7个阶段:加载–>验证–>准备–>解析–>初始化–>使用–>拆卸。其中验证、准备和解析3个阶段称为连接(Linking)。类的加载时机什么时候触发类的加载?其实JVM规范并没有严格规定类加载的时机,而是由具体的虚拟机实现自由把我时机。但
类的生命周期
一个类被加载到内存到拆卸出内存为止,它的整个生命周期包括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类库同名的外部类。
更多推荐
所有评论(0)