Java类加载过程
具体过程如下:
编译
首先Java源代码会被编译器编译成class文件以供计算机执行(详细过程可参考编译原理)。java编译一个类时,若这个类所依赖的类还没有被编译,编译器会自动的先编译这个所依赖的类,然后引用。
类的加载
类的加载过程为:
加载 --> 链接(验证 --> 准备 --> 解析) --> 初始化
加载过程的任务主要是根据类的权限和路径名查找并导入我们的class文件,将class文件转换成一个二进制的字节流。将静态字节流所代表的静态存储结果转化为方法区的运行时数据结构;在内存中生成一个java.lang.Class对象。
链接过程分为三个步骤
首先进行对各个方面进行验证(文件格式、符号引用等),以确保字节码的合法性,确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证完成后进行准备过程,主要为静态变量分配内存、并将变量初始成默认值(注意,这一步在变量赋值之前,所有变量都会被赋予初始值)。解析阶段是虚拟机将class常量池内的符号引用替换为直接引用的过程,通俗的讲就是将我们声明的那些引用符号(如Student oStu2 = new Student();)变成实实在在的指针。
最后是初始化阶段,对静态变量进行赋值,然后执行类的初始化语句(static{}代码块)详细过程是:
- 如果类还没有被加载,那就先进行加载和链接。
- 如果类存在父类,并且父类还没有初始化,那么先初始化父类。
- 如果类中存在初始化语句,顺序执行初始化语句。
类初始化的时机
- 创建类的实例(New、反射、序列化和反序列化、克隆)
- 访问类的静态变量或者对静态变量进行赋值。
- 主动调用类的静态方法。
- Class.forName(包类名)
- 完成子类的初始化之前。
- 该类是程序的引导入口。
类加载器
Bootstrap ClassLoader
JVM自带的引导类加载器,由C/C++实现,主要负责加载Java的核心类库(如resource.jar)以及以java、javax、sun等开头的包名类(如java.lang包)
Extension ClassLoader
由Java语言编写,指定Bootstrap ClassLoader为Parent的加载器(getParent方法获取Bootstrap ClassLoader,注意这里不是父类和子类的关系)它是用来读取Java的一些扩展类库,如读取JRE/lib/ext/*.jar中的包等。
Application Classloader
Java程序的默认加载器,指定Extension ClassLoader为Parent的加载器,加载Java应用类(我们写的类)。
User ClassLoader
用户自定义的类加载器。基用于加载非Classpath中(如从网络上下载的jar或二进制)的jar及目录。
双亲委派机制
一个类被加载的过程当中,先由上次类加载器加载,如果无法加载在交给下层处理,直到User ClassLoader,如还不能加载则抛出classNotFoundException异常。
举个例子:
public class String {
static{
System.out.println("我是String类");
}
}
public class test {
public static void main(String [] args){
String s = new String();
//这里不会打出"我是String类",因为在new String()时先由Bootstrap ClassLoader进行加载,则java.lang.String类会被加载
}
}
双亲委派机制一方面可以避免类的重复加载,另一方面可以保护程序安全,防止Java语言环境被破坏。
运行时数据区
程序计数器
与线程同时存在,记录一个线程中程序执行的位置,控制程序随着CPU时间的切换不断往下执行。
虚拟机栈
当前执行线程的独占空间,以栈的数据结构出现。线程私有,它保存一个线程方法的调用状态,压入栈的单位为栈帧。程序执行时main()方法的栈帧先入栈,如果main()方法中调用了A方法,那么在执行到A方法时它的栈帧入栈,等A方法执行完成后栈帧出栈,继续执行main方法。
本地方法栈
该栈为C的栈结构,用来存储Java执行过程中的Native方法(也就是底层用C来实现的方法)。
堆
是一块不连续的内存空间,JVM运行过程中最大的一块内容,被所有线程共享,用来存放new出来的对象以及数组。
方法区
是堆的一部分,用来存放字面量(文本字符串、八种基本类型的值、被声明为final的常量等)以及符号引用(类和方法的全限定名、字段的名称和描述符、方法的名称和描述符),它是所有的线程共享的一块区域,当方法区不能满足内存分配需求时会报出OutOfMemoryError。
执行引擎
解释器
将Class字节码的指令翻译成机器可以看懂的指令。
即时编译器
可以侦测并保存程序中的热点代码,在其反复调用时可以避开解释器直接执行。
垃圾回收器
直接对共享内存进行管理,回收不需要的变量。