对于JVM来说,class文件的来源并不影响JVM对class文件的使用,也就是说JVM可以处理的class文件不一定是来自于java语言,也可以是其他运行在JVM上的语言,甚至是字节码编辑器直接生成的class字节文件、网络流以及运行时生成的class文件。也就是只要是JVM可以识别的class字节码文件,都可以运行。
JVM中类加载的生命周期是:加载,验证,准备,解析,初始化。
一、对类进行初始化的5种情况
1.使用new实例化对象的时候,读取或者设置一个类的静态字段(被final修饰的字段除外),调用一个类的静态方法时。
2.使用反射对类进行反射调用的时候。
3.初始化一个类时,他的父类还没有初始化就要先初始化其父类(接口不需要父类全部被初始化)。
4.main()所在的类
5。。。。。1.7动态语言支持时可能用到
二、类的加载过程
1.加载
1)通过类的全限定名来获得定义此类的二进制字节流
2)将这个字节流代表的静态存储结构转化为方法区运行时数据结构
3)在内存中生成一个代表这个类的java.lang.Class对象(在方法区里),作为这个类的访问入口
加载阶段既可以使用系统提供的引导类加载器来完成,也可以由用户自定义的类加载器去完成。
2.验证
为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
验证阶段大致包含4个阶段的检验:文件格式验证、元数据验证、字节码验证、符号引用验证
文件格式验证:主要目的是保证输入的字节流能正确地解析并存储于方法区中,格式上符合Java类型信息的要求。
元数据验证:确保元数据信息都符合Java语言规范
字节码验证:确定语义是合法的,符合逻辑的
符号引用验证:验证是否存在这些引用以及这些引用是否可以被访问
3.准备
准备阶段正式为类变量分配内存并设置类变量初始值的阶段,为类变量赋0值。
4.解析
解析阶段是虚拟机将常量池内的符号引用(可以是任何形式,只要能无歧义的定位目标即可,与虚拟机的内存布局无关)替换为直接引用(指针、偏移量、句柄、和虚拟机的内存布局有关)的过程。
5.初始化
执行< clinit>()方法的过程。< clinit>()是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的。非必须,父类的< clinit>()先调用,多线程环境被虚拟机加锁。
三、类加载器
上述的“加载”过程是由类加载器实现的。比较两个类是否相等,只有在两个类是由同一个类加载器加载的前提下才有意义。加载类的加载器不同,这两个类就必定不同。
双亲委派模型
从虚拟机的角度来讲,只存在两种不同的类加载器:1.启动器类加载器,用C++编写,是虚拟机的一部分。2.其他类加载器,由Java语言实现,独立于虚拟机外部。
从开发者角度可以分成以下几种类加载器,关系如图
这种类加载器之间的层次关系称为双亲委派模型,要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。
工作流程:如果一个类加载器收到了类加载请求,它想把这个请求委派给父类加载器去做,父类加载器无法完成时,自己才会加载。
好处:保证类在各种类加载器环境中都是同一个类,维持java程序稳定运行。