本文讲讲当java文件已经被编译成class字节码后虚拟机如何加载。
1. 加载等级
JVM对class的加载采用双亲委派模型:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。使用双亲委派模型的意义在于,可以防止内存中出现多份同样的字节码。
JVM平台提供三层ClassLoader,分别为Bootstrap ClassLoader、ExtClassLoader和AppClassLoader。
Bootstrap ClassLoader
最高层加载器,并不遵守双亲委派加载机制,只是一个类的加载工具。
ExtClassLoader
低一层级的类加载器,负责java.ext.dirs目录下的类的加载。
AppClassLoader
父类为ExtClassLoader,服务所有普通类,负责java.class.path目录(其实就是classpath)。
类的加载流程如下:
2. 类加载过程
主要分为加载、连接和初始化三个阶段。
加载阶段就是类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区内,然后转换成一个与目标类型对应的java.lang.Class对象实例。
连接阶段将已加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。分为验证、准备和解析三个步骤。
初始化阶段将类中所有被static关键字标示的代码都执行一遍。
3. 类加载方式
主要分为显示加载和隐式加载两种。
显示加载一个类有如下方式:
1.Class.forName();
2.ClassLoader.loadClass()
3.ClassLoader.findClass()
而隐式加载一个类,通常在如下情况下发生:
1.new一个对象;
2.属性引用某个类;
3.继承了类或实现接口;
4.方法参数引用了某类;
其实工程在引用一个jar包时,JVM并不会加载其中所有的类,只会加载其中使用到的。
4. 加载的异常情况
ClassNotFoundException:显示加载一个类找不到时。
NoClassDefFoundError:隐式加载一个类找不到时。
5. 加载和卸载
java中的类和类的加载器同样需要存储空间,在Hotspot中它们存储在堆的永久代(PermGen)中。有加载就有卸载,当满足如下条件时,类将被卸载;
1.在java堆上没有对表示该类加载器的java.lang.ClassLoader对象的引用;
2.java堆没有对表示类加载器加载的类的任何java.lang.Class对象的引用;
3.在java堆上该类加载器加载的任何类的所有对象都不再被引用;
这里补充个知识,JVM判断一个类是同一个类的条件:
1.这个类的完整类名一致;
2.加载这个类的ClassLoader是同一个实例;
下篇文章说说JVM内存结构。