类加载的过程
类加载的过程: 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括加载,验证,准备,解析,初始化,使用,卸载。
- 其中类加载:
- 加载
- 通过一个类的全限定名类获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在java堆中生成一个代表这个类的Class对象,作为方法区这些数据的访问入口
- 链接
- 验证
- 验证是链接阶段的第一步,
- 这一阶段的目的是 为了确保Class文件的字节流中包含的信息符合当前jvm的要求,并且不会危害jvm自身的安全。
- 验证内容:
- 文件格式:字节流内容是否符合Class文件格式的规范,并被当前虚拟机可处理
- 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合java规范(如数据类型)
- 字节码验证:主要是数据流和控制流的分析。在元数据验证后,这阶段对类的方法体进行校验分析。保证被校验累的方法在运行时不会做出危害虚拟机安全的行为。
- 符号引用验证:虚拟机将符号引用直接转化为直接引用的过程。此过程可以看做是对类自身以外(常量池中的各种符号引用)的信息进行匹配性的校验
- 准备
- 为类的静态变量分配内存 并将其初始化为默认值。这些内存都将在方法区中进行分配
- 该阶段不会分配类中的实例变量的内存,实例变量将会在对象实例化的时候随着对象一起分配在java堆中
- 解析
- 虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口,字段,类方法,接口方法四类符号引用进行。
- 类或接口的解析
- 字段解析
- 类方法解析
- 接口方法解析
- 虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口,字段,类方法,接口方法四类符号引用进行。
- 验证
- 类初始化
- 该过程是类加载的最后一步。前面的类加载过程,除了在加载阶段用户应用程序可以通过自定义加载器参与外,其余动作完全由虚拟机主导和控制。
- 在初始化阶段,才开始真正的执行类中定义的java代码。
- 加载
在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定)。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。
java中的方法绑定
- 静态绑定: 编译器确定 java当中的方法只有final static private 以及 构造方法属于静态绑定;
- 动态绑定:运行时绑定,在运行时根据具体对象的类型进行绑定,在java中,几乎所有的方法都是后期绑定
双亲委派模型:
-
(根)引导类加载器 Bootstrap ClassLoader
- 加载以下两者指定的目录,并且是虚拟机识别的(仅按照文件名识别,不合符的也不会被加载)。 该加载器是属于c++层,不会被java层引用到
- JAVA_HOME\lib目录
- -Xbootclasspath指定的目录
- 加载以下两者指定的目录,并且是虚拟机识别的(仅按照文件名识别,不合符的也不会被加载)。 该加载器是属于c++层,不会被java层引用到
-
Extension ClassLoader
- 加载以下目录
- JAVA_HOME\lib\ext
- java.ext.dirs指定
- 开发者可以直接使用该类加载器
- 加载以下目录
-
Application ClassLoader
- 负责加载用户路径上所指定的类。
-
自定义类加载器
-
双亲委派模型:
- 其要求除启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不以继承关系实现,而是用组合的方式来复用父类的代码。
- 工作过程:如果一个类加载器接收到了类加载的请求,它首先把这个请求委托给他的父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
- 优点:
- java类随着它的类加载器一起具备了一种带有优先级的层次关系。
- 保证一个类不会被重复加载