加载机制:
JVM把class文件加载到内存,并对数据进行校验、准备、解析、初始化,最终形成JVM可以直接使用的Java类型的过程。
1,加载阶段
加载是类加载过程中的一个阶段,不要将这2个概念混淆了。相对于类生命周期的其他阶段而言,加载阶段(加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,因为开发人员既可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器来完成加载。
在加载阶段,虚拟机需要完成以下3件事情:
1,通过类的全限定名来获取定义此类的二进制字节流
2,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3,在内存中生成一个代表这个类的对象,作为方法区这个类的各种数据的访问入口。
加载类文件的方式
1,从本地系统中直接加载
2,通过网络下载
3,从zip,jar等归档文件中加载
4,从专有数据库中提取.class文件
5,将Java源文件动态编译为.class文件
2,链接阶段(验证、准备、解析)
3.1 验证:确保被加载的类的正确性
1,文件格式验证:验证字节流是否符合类文件格式的规范,如:0xCAFEBABE开头、主次版本号等等。
2,元数据验证:对字节码进行语义分析如:这个类是否有父类,是否实现了父类的抽象方法等等。
3,字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的,如:类型转换有效等等。
4,符号引用验证:确保解析动作能正确执行;如:引用能找到对应的类和方法等等。
验证阶段是非常重要的,但不是必须的。可以采用-Xverify:none参数来关闭大部分的类验证措施。
3.2 准备:为类的静态变量分配内存,并将其赋默认值(方法区中)
只对static修饰的静态变量进行内存分配、赋默认值(如0、0L、null、false等)。
对final的静态字面值常量直接赋初值(赋初值不是赋默认值,如果不是字面值静态常量,那么会和静态变量一样赋默认值)。
3.3 解析:将常量池中的符号引用替换为直接引用(内存地址)的过程
符号引用就是一组符号来描述目标,如:包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。如指向方法区某个类的一个指针。
4. 初始化:为类的静态变量赋初值
1,定义静态变量时指定初始值。如 public static String a="aa";
2,在静态代码块里为静态变量赋值。如 static{ a="bb"; }
注意:只有对类的主动使用才会导致类的初始化。
5. clinit 与 init
在编译生成class文件时,会产生两个方法,一个是类的初始化方法clinit, 另一个是实例的初始化方法init。
clinit:是类构造器,主要作用是在类加载过程中的初始化阶段进行执行,执行内容包括静态变量初始化和静态块的执行。
init:是实例构造器,主要作用是在类实例化过程中执行,执行内容包括成员变量初始化和代码块的执行。
clinit相关:
1. 如果类中没有静态变量或静态代码块,那么clinit方法将不会被生成。
2. 在执行clinit方法时,必须先执行父类的clinit方法。
3. clinit方法只执行一次。
4. static变量的赋值操作和静态代码块的合并顺序由源文件中出现的顺序决定。
init相关
1. 在执行init方法时,必须先执行父类的init方法。
2. init方法每实例化一次就会执行一次。
3. init方法先为实例变量分配内存空间,再执行赋默认值,然后根据源码中的顺序执行赋初值或代码块
类的引用
1,new一个类的对象。
2,调用类的静态成员(除了final常量)和静态方法。
3,使用java.lang.reflect包的方法对类进行反射调用。
4,当虚拟机启动,先启动main方法所在的类。
5,当初始化一个类,如果其父类没有被初始化,则先会初始化他的父类
被动引用
1,当访问一个静态域时,只有真正声明这个域的类才会被初始化。
例如:通过子类引用父类的静态变量,不会导致子类初始化。
2,通过数组定义类引用,不会触发此类的初始化。
3,引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)
类加载器
系统自带三个类加载器
Bootstrap ClassLoader > Extention ClassLoader > Application ClassLoader > 自定义
1,Bootstrap ClassLoader
(1)加载核心库(JAVA_HOME/jre/lib/rt.jar, sun.boot.class.path路径下的内容)
(2)是用原生代码(C语言)来实现的,并不继承自 java.lang.ClassLoader。
2,Extention ClassLoader
(1)加载扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容)
(2)由sun.misc.Launcher$ExtClassLoader实现。
3,Application ClassLoader
(1)它根据应用的类路径(classpath,java.class.path 路径下的内容)来加载 Java 类。
(2)由sun.misc.Launcher$AppClassLoader实现。
4,自定义类加载器
通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
双亲委托机制
1,某个特定的类加载器接收到类加载的请求
2,将加载任务委托给自己的父类,直到最高级父类引导类加载器(bootstrap class loader)
3,如果父类能够加载就加载
4,不能加载则返回到子类进行加载。
5,如果都不能加载则报错。ClassNotFoundException
双亲委托机制是为了保证 Java 核心库的类型安全。这种机制保证不会出现用户自己能定义java.lang.Object类等的情况。