JVM加载机制分为加载、验证、准备、分析、初始化五个部分。
加载:加载是加载过程中的一个阶段,它将在内存中生成代表这一类的java.lang.Class对象作为方法区各种数据的入口。请注意,您不必从Class文件中获取。它不仅可以从zip包中读取(如从jar包和war包中读取),还可以在运行过程中计算和生成(动态代理)或其他文件(如将JSP文件转换为相应的Class类)。
验证:本阶段的主要目的是确保Class文件字节流中包含的信息是否符合当前虚拟机的要求,并且不会危及虚拟机本身的安全。
准备:准备阶段是类变量正式分配内存并设置类变量的初始阶段,即将这些变量使用的内存空间分配到方法区域。
分析:分析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用是Class文件:
CONSTANT_Class_info、CONSTANT_Field_info、CONSTANT_Method_info等常量类型
符号引用:符号引用与虚拟机实现的布局无关,引用的目标不必加载到内存中。由于符号引用的字面量形式在Java虚拟机规范的Class文件格式中明确定义,因此各种虚拟机实现的内存布局可能会有所不同,但它们可以接受的符号引用必须一致。
直接引用:直接引用可以是指向目标的指针,相对偏移或可以间接定位到目标的句柄。如果有直接引用,则引用的目标必须存在于内存中。
初始化:初始化阶段是类加载的最后阶段。在之前的类加载阶段之后,除了可以在加载阶段定制类加载器外,其他操作都由JVM主导。在初始阶段,Java程序代码在类中定义的真正执行开始。
类构造器<client>:初始化阶段是执行类结构<client>方法的过程.<client>该方法是由编译器自动收集类中类变量的赋值操作和静态句块的句子组成的。虚拟机会保证子<client>在实施方法之前,父类<client>该方法已经实施。如果一个类中没有静态变量赋值或静态句块,那么编译器就不能为这个类生成<client>()方法。
注意以下集中情况不会初始化:
1.通过子类引用父类静态字段,只会触发父类的初始化,而不会触发子类的初始化
2.定义对象数组,不会触发此类的初始化
3.常量在编译器中存储在调用常量池中,本质上没有直接引用定义常量的类,也不会触发定义常量的类。
4.通过类名获取Class对象,不会触发类的初始化
5.通过Class.当forname加载指定类别时,如果指定参数initialize为false,则不会触发类别初始化。事实上,这个参数告诉虚拟机是否应该初始化类别。
6.通过Classloader默认的loadClass方法,不会触发初始化操作。
类加载器
为了让应用程序决定如何获得所需的类别,JVM提供了三种类型的加载器:
启动式加载器(Bootstrap ClassLoader):JAVA_负责加载HOME在\lib目录中,或通过-Xbootclaspath参数指定的路径,并被虚拟机认可(按文件名识别,如rt.jar)的类
扩展加载器(Extension ClassLoader):JAVA_负责加载HOME\lib\ext目录,或通过java.ext.dirs系统变量指定路径中的类库。
应用程序加载器(Application ClassLoader):负责加载用户路径(classpath)上的类库。
JVM通过父母指定的模型加载,当然,我们也可以继承Java.lang.实现Classloader自定义的类加载器。
父母分配机制:当一个类被类加载请求时,首先不会尝试自己加载这个类,而是将这个请求分配给父亲来完成,每个层次的加载器都是如此,所以所有的加载请求都应该传输到启动加载,只有当父亲加载器反馈不能完成请求(在加载路径下找不到加载),子加载器才会尝试自己加载。
双亲委派的一个好处是,例如,加载位于rt.类java.lang.Object,无论哪个加载器加载这个类,最终都委托给顶层的启动加载器加载,从而保证使用不需要的类加载最终得到相同的Object对象。