JVM类加载
加载
(1)通过一个类的全限定名来获取此类的二进制字节流;
(2)将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构;
(3)在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
验证
目的是确保Class文件的字节流中包含的信息是否符合《Java虚拟机规范》的全部约束要求,被当作代码运行后不会危害虚拟机自身安全。
准备
正式的为类中定义的变量(静态变量,被static修饰的变量)分配内存并设置类变量初始值。
解析
Java虚拟机将常量池内的符号引用替换为直接引用。
符号引用:以一组符号来描述所引用的目标,符号可以时任何形式的字面量,只要在使用时能无歧义的定位到目标即可。
直接引用:直接指向目标的指针、相对偏移量或者能间接定位到目标的句柄。
初始化
前面的类加载动作里,除了在类加载阶段用户应用程序可以通过自定义LoadClass()类加载的方式局部参与之外,其余动作全部由java虚拟机主导控制。在初始化阶段,Java虚拟机才真正开始执行类中编写的Java程序代码,将主导权交给应用程序。
初始化阶段就是执行类构造器()方法的过程。这个方法时Java编译器的自动生成物。
(1)()方法由编译器自动收集类中的所有类变量的赋值动作和静态代码块(static{}),合并长生了clinit()方法,收集顺序由他们在代码中出现的顺序决定。静态代码块可以访问出现在它前面的静态变量,但是对于后面的静态变量可以赋值,不可以访问。
(2)JVM会保证父类的clinit()方法先于子类的clinit()方法执行。
(3)JVM必须保证一个类的clinit()方法在多线程环境下被正确地加锁同步,如果多个线程去同时初始化一个类,那么只会有一个线程执行,其他线程阻塞。
双亲委派模型
加载一个Class类的顺序也是有优先级的,类加载器从最底层开始往上的顺序是这样的
- BootStrap ClassLoader:rt.jar
- Extension ClassLoader: 加载扩展的jar包
- AppClassLoader:指定的classpath下面的jar包
- Custom ClassLoader:自定义的类加载器
如果一个类加载器收到了类加载请求,那么它不会自己去加载这个类,而是把这个请求委派给父类加载器去完成。
这样所有地加载请求都到达了引导类加载器Bootstrap ClassLoader。
只有父类加载器反馈自己无法完成这个加载请求,他的搜索范围内没有找到这个类,子类加载器才会尝试自己去完成加载。
使用双亲委派模式去组织类的加载关系好处:
(1)保证了类的唯一性
(2)Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系。