做者:不洗碗工做室 - Markluxjava
出处:Marklux's Pub程序员
版权归做者全部,转载请注明出处bash
本部分整理自《深刻理解JVM虚拟机》网络
类的生命周期数据结构
一个类从被加载到虚拟机内存中开始,到被卸载出内存为止,整个生命周期包括了 加载、验证、准备、解析、初始化、使用和卸载7个阶段。其中 验证、准备、解析 3部分统称为连接,以下图:spa
整个顺序并非彻底固定的,其中解析阶段能够在初始化以后再开始,这样即可以实现Java的运行时绑定(动态绑定)机制。code
类的加载时机cdn
JVM虚拟机规范并无对类的加载时机作出严格的要求,只规定了如下五种状况须要马上触发类的初始化:对象
MethodHandle
实例最后的解析结果包含REF_getStatic
,REF_putStatic
,REF_invokeStatic
的方法句柄,且这个方法句柄对应的类没有初始化,则须要先对其进行初始化。其他条件下,能够由JVM虚拟机自行决定什么时候去加载一个类。blog
主动引用和被动引用
上面五种条件也被称为对类的主动引用,除此以外其余引用类的方式都不会触发初始化,即类的被动引用,举个例子:
public class Father {
static {
System.out.println("father init.");
}
public static int val = 123;
}
public class Son extends Father {
static {
System.out.println("son init.");
}
}
复制代码
当咱们访问Son.val
时,会发现并无输出son init.
对于静态字段,只有直接定义这个字段的类才会被初始化,所以经过子类来引用父类的静态字段,子类至关因而被动引用,也就不会被初始化了。
下面简单的介绍一下整个加载过程当中,每一个阶段JVM都执行了什么操做:
加载过程是Java的一大特色,类的来源能够多种多样,压缩包、网络字节流、运行时动态计算生成(reflect)等等...这也造就了Java语言强大的动态特性。
java.lang.Class
对象,做为方法区这个类的各类数据的访问入口这一过程主要是为了确保Class的字节流中包含的信息符合虚拟机标准,以避免形成破坏
这一阶段将会为类变量分配内存并设置其初始值,注意此时进行内存分配的仅包括类变量(static修饰),而且初始值一般状况下是数据类型的零值而不是设定值,以下例
public static int val = 123;
复制代码
在这一阶段变量val
的赋值是0而不是123,由于此时还没有执行任何Java方法,而对val
复制的putstatic
指令在初始化阶段后才会执行。
固然也有特殊状况,以下
public static final int val = 123;
复制代码
加上final关键字修饰后,Java编译时会为val
生成ConstantValue
属性,这时准备阶段就会根据设置将其值设置为123。
此阶段虚拟机将常量池内的符号替换为直接引用,主要包含如下动做:
这时类加载过程的最后一步,这部分开始真正的执行Java代码,也就是说,这个阶段能够由程序员参与。
此阶段其实就是执行类构造器<clinit>()
方法的过程。
类加载器(Class Loader)是Java虚拟机的一大创举,它将“获取类的二进制字节流”这个过程交给了开发人员本身去实现,只要编写不一样的Class Loader,应用程序自己就能够用相应的方式来获取本身须要的类。
对于任意一个类,都须要由加载它的类加载器和这个类自己一同确立其在虚拟机中的惟一性。
通俗的讲,就是即使同一个Class文件,被不一样的类加载器加载以后,获得也不是同一个“类”(equals方法返回false)。
从虚拟机角度讲,只有两种类加载器,一种是启动类加载器(Bootstrap ClassLoader),在hotpot上使用C++实现,属于虚拟机的一部分;另外一种则是全部其余类的加载器,这些加载器是独立于虚拟机的,由Java语言实现的,从开发者角度看,能够分为如下两类:
扩展类加载器(Extension ClassLoader)
应用程序类加载器(Appliaction ClassLoader)
固然开发人员也能够本身编写类加载器,最终不一样的类加载器之间的层次关系以下图所示:
这就是Java中著名的双亲委派模型,它要求除了顶级的BootStrap加载器以外,其余类加载器都必须有父类加载器,工做流程以下:
若是一个类加载器收到了类加载的请求,他首先不会本身去尝试加载这个类,而是将这个请求委派给父类加载器去完成,只有当父加载器反馈本身没法完成加载请求时,子加载器才会本身去尝试加载这个类。
这样作的好处是,Java类随着它的类加载器一块儿具有了一种带有优先级的层次关系。举个例子,好比java.lang.Object
这个类,不管哪一个类加载器加载时,最终都会委派给Bootstrap加载器去加载,这就保证了整个系统运行过程当中的Object
都是同一个类。
不然,若是用户本身编写了一个java.lang.Object
类,并放在程序的classpath中,最终系统将会出现多个不一样的Object类,整个Java体系就变得一团混乱了。