目录
1. 概述
java中,类的加载、连接和处事话在程序运行期间完成。
类加载机制:虚拟机吧描述类的数据从Class文件加载到内存,并对数据进行校验、转化解析和初始,最终形成可以被虚拟机直接使用的Java类型。
2. 类加载的时机
类的生命周期:
- 加载、验证、准备、解析、初始化、使用、卸载
- 加载、验证、准备、初始化、卸载顺序是去顶的,会按部就班的开始。
- 连接(linking)包括:验证、准备、解析
有且只有5种情况必须立即对垒进行初始化:(类加载需要在此之前)<主动引用>
- 遇到new、getstatic、putstatic、invokestatic四条字节码指令时
- 对类进行反射调用时
- 初始化一个类发现其弗雷还没有初始化
- JVM启动时,main所在的类
- 使用JAVA7的动态语言支持时
<被动引用>,不会触发初始化,比如:
- 通过之类引用父类的静态字段,不会导致之类的初始化
- 通过数组定义来引用类,不会触发此类的初始化
- 常量在编译阶段会存入调用类的常量池,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。
注:在一个接口初始化是,并不要求其父接口全部完成初始化,只有在真正使用到父接口的时候才会初始化。
3. 类加载过程
3.1 加载
完成三件事:
- 通过类的全限定名来获取定义此类的二进制字节流
- 将字节流说代表的静态存储结构转化为方法区运行时数据结构
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类各种数据的访问入口。
3.2 验证
目的:保证Class文件的字节流中包含的信息符合当前虚拟机的要求,不会危害虚拟机自身的安全。
4个阶段:
- 文件格式验证
字节流符合Class文件规范
目的:保证输入的字节流能被正确的就诶系并存储于方法区之内。
之后的样子基于方法区的存储结构进行。
- 元数据验证
字节码描述的信息进行语义分析。
目的:保证不存在不符合Java鸳鸯规范的元数据信息。
- 字节码验证
目的:通过数据流和控制流分析确定程序予以是合法的、符合逻辑的。
- 符号引用验证
目的:确保解析动作能够正常执行。
3.3 准备
- 正式为类变量分配内存并设置类变量初始值。
- 在方法区进行。
- 只包括类变量不包括实例变量
3.4 解析
虚拟机将常量池中的符号引用替换为直接引用的过程。
- 符号引用:一组符号来描述引用的目标
- 直接引用:直接指向目标的指针、相对便宜和间接定位到目标的句柄
3.5 初始化
初始化阶段是执行类构造器<clinit>()方法的过程:
- <clinit>()方法 由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生。
- <clinit>()方法 与类构造函数不同。不需要显示的调用父类构造器,因为虚拟机会保证之类<clinit>()方法执行之前,父类的<clinit>()方法以及执行完毕
- <clinit>()方法 不是必须的,可能没有
- 执行接口的<clinit>()方法不需要先执行父类的<clinit>()方法。
- 虚拟机会爆炸一个类的<clinit>()方法在多线程环境下被正确的加锁、同步。
4.类加载器
类加载器:实现“通过一个类的全限定名来获取描述该类的二进制字节流”这一动作的代码模块。
4.1 类和类加载器
任何一个类都需要加载它的类加载器和这个类本身怿童确立其在java虚拟机中的唯一性。
每一个类加载器东有一个独立的类名称空间。
4.2 双亲委派模型
JVM角度,类加载器分为:
- 启动类加载器
- 其他类加载器
java开发人员角度,类加载器分为:
- 启动类加载器
- 扩展类加载器
- 应用程序类加载器
类加载器之间的层次关系成为类加载器的双亲委派模型。
工作过程:
- 每一个类加载器收到类加载的请求,他首先不会自己尝试加载这个类,而是将这个类委派给父类加载器去完成,每一个层次的类加载器都是这样去做。因此所有的加载请求都应该常送到顶层的启动类加载器中,只有当父类就爱在其反馈自己玩法完成这个加载请求时,之加载器才会尝试自己去加载。
好处:
- java类随着他的加载器一起具备了一种带优先级的层次关系。
4.3 破环双亲委派模型
- 发生在双亲委派模型出现前。
妥协:新加了findClass()
- 模型自身缺陷
引入线程上下文类加载器
- 用户对程序动态性的追求导致的