参考博文:https://www.cnblogs.com/jswang/p/7699643.html
http://www.importnew.com/25295.html
JVM类加载过程
一个类运行分为以下几步:装载、链接、初始化;
装载:
- 通过类的完全限定名,产生一个代表该类型的二进制数据流;
- 解析这个二进制数据流为方法区的内部结构;
- 构建一个表示该类型的java.lang.Class类的实例
注意:如果一个类装载器在预装载的时遇到错误或缺失class文件,需要等到程序首次主动使用这个类时才会报错。
链接:
- 验证:确认类型符合Java语言的语义,检查类的兼容性,以及符合引用的验证;
- 准备:为类变量分配内存,设置初始默认值;
- 解析:符号引用转换成直接引用,其中符号引用以一组符号描述所引用的目标,符号可以任何形式的字面量;直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄,如果有了直接引用,那么引用目标一定已经存在内存中。(类或接口的解析、字段解析、类方法解析和接口方法解析)
初始化:
初始化是类加载的最后一个阶段,到了初始化加载阶段才开始真正执行类中自定义的Java代码程序,执行类构造器<client>方法过程,以下会触发类初始化操作:
- 当创建某个类实例(如通过new或者反射、克隆、反序列化等)
- 当调用某个类的静态方法
- 当使用某个类或接口的静态字段时;
- 当通过反射访问某个类的方法时;
- 当初始化某个子类;
- 当运行启动类(包含main方法的类)
Java编译器会收集所有的变量初始化语句和类型的静态初始化器,将这些放到一个特殊的方法中client中。
其中,static代码块的执行发生在初始化阶段。初始化阶段,jvm主要完成静态变量的初始化,静态代码块的执行。
static final关键字修饰的属性:
static修饰,表示该属性属于类,全局只有一个;final修饰的属性表示为常量,一旦初始化无法修改,static final修饰的属性表示一旦给值,就不可修改,并且可以通过类名访问,在类的加载过程中的准备阶段赋值。
类加载器:
JVM 提供了三种类加载器:
- 启动类加载器:负责加载JAVA_HOME\lib目录中的,或者通过-Xbootclasspath参数指定路径中的类;
- 扩展类加载器:负责加载JAVA_HOME\lib\ext目录中的类库;
- 应用程序类加载器:负责加载用户路径classpath上的类库。
JVM通过双亲委派模型进行类加载,也可以继承java.lang.ClassLoader实现自定义的类加载器。
当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务会传到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试加载任务。
采用双亲加载机制好处:比如加载一个类,不管哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同类加载器最终得到都是同一个Object对象。