前几天看了周志明的这本书,记录一个大纲,用以复习和备忘。
从被加载到卸载,包括7个阶段:加载、验证、准备、解析、初始化、使用、卸载(loading, verification, preparation, resolution, initialization, using, unloading),其中验证、准备、解析统称为“连接”(Linking)。
加载、验证、准备、初始化和卸载的顺序是确定的,解析则不一定,为了支持动态绑定。
必须对类进行到“初始化”的情况:
1、用new初始化对象时、读取或设置一个类的静态字段(常量池的除外)时;
2、使用reflect包对类进行反射调用时;
3、初始化类时,需要先初始化父类(包含抽象类、接口,而如果接口初始化,并不需要其父接口初始化);
4、包含main方法的类,即程序入口;
5、关于jdk7的动态语言支持(略)
有且只有这5个方式会触发初始化,称为”主动引用“,其他称为”被动引用“
1、通过一个类的全限定名来获得类的二进制字节流
2、将字节流的静态存储结构转化成方法区运行时数据结构
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
以上过程灵活度很大,比如字节流加载就有很多种渠道。
对于非数组类,可以由系统提供的引导类加载器来加载,或者自定义类加载器也可以(这里的加载指加载阶段获取类的二进制流)
对于数组类,数组本身由JVM直接创建,而元素类由类加载器创建。
连接的第一步,为了确保字节流的信息符合当前虚拟机要求,且没有危害。
主要有以下4个阶段的验证工作:
1、文件格式验证:验证字节流是否符合Class文件格式规范且能被虚拟机处理,确保字节流可以正确存储到方法区内,通过验证之后,存入方法区,不在操纵字节流。
2、元数据验证:对字节码描述的信息进行语义分析,确保类符合Java语义规范,主要是类的继承关系、继承或实现 的方法是否合理。
3、字节码验证:最复杂的阶段,通过数据流和控制流分析,来验证方法的语义是否符合规范。
4、符号引用验证:发生在虚拟机将符号引用转化卫直接引用的时候,在解析阶段发生,验证引用是否符合规范,比如能否找到引用的类、是否存在访问的方法或字段、访问性(public, private, protected)是否合理。
验证阶段确保解析能正常进行,但是如果所运行的代码早已被反复验证过,则可以跳过验证阶段:-Xverify:none。
为静态变量分配内存并初始化。初始化并没有赋值,而只是初始化为0,引用则为null。
但是如果是static final,即常量,则会赋值。
将常量池内的符号引用替换为直接引用。
分为:
1、类或接口解析
2、字段解析
3、类方法解析
4、接口方法解析
类加载的最后一步,初始化类变量和其他资源,其实是执行()方法的过程。
①这个方法是由类变量所有赋值操作和静态语句块合并产生的。静态语句块不能访问定义在其后的静态变量,但是可以进行赋值操作:
public class AboutClassLoading4 {
static {
i = 2;
}
static int i;
public static void main(String[] args) {
System.out.println(i);//输出2
}
}
②Object类是第一个执行这个方法的。
③由于父类先执行这个方法,所以父类的静态语句块要先于子类的变量赋值:
public class AboutClassLoading4 {
static class Parent{
public static int A;
static {
A=2;
}
}
static class Sub extends Parent{
public static int B=A;
}
public static void main(String[] args) {
System.out.println(Sub.B);//输出2
}
}
④一个类并不一定要有这个方法,比如这个类没有静态语句块和变量赋值操作。
⑤接口也会有这个方法。
⑥此方法会在多线程被加锁,同时只能有一个线程执行初始化方法。
不同的类加载器对同一个类会有不同的效果。
存在两种类型的加载器:
①启动类加载器(BootStrap ClassLoader),由C++实现,是虚拟机的一部分。
将存放在
②其他加载器,由Java实现,继承自抽象类java.lang.ClassLoader,独立于虚拟机外,包括:
扩展类加载器(Extension ClassLoader),可直接使用,负责加载
应用程序类加载器(Application ClassLoader),为系统默认加载器。
(Parents Delegation Model,来自于网络,侵删)
工作过程:如果一个类收到加载请求,先不会自己尝试加载这个类,而是将请求委派给父类加载器,逐层委派,直到顶层的启动类加载器。当父类加载器无法完成这个类加载(没有找到要加载的类)时,子类加载器才会尝试去加载。
优点:使得同一个类会被(能够加载的)顶端的类加载器加载,确保了任意一个类的唯一性,用以保证正常加载执行。