加载、验证、准备、解析、初始化、使用、卸载。
java支持动态语言绑定,解析可能在初始化之后。其他的顺序是固定的。
文件格式验证
:验证字节流是否符合class文件规范。是否以魔数0XCAFEBABY开头,主次版本号是否在jvm的处理范围,常量是否有不存在的类型等。元数据验证
:对字节流描述的信息进行分析,验证是否符合java语言语法规范。是否有父类,继承链是否正确,与父类的字段是否有冲突,抽象父类和接口的方法是否都实现等。字节流验证
:分析数据流和控制,分析语义是否合法、符合逻辑。特别是对方法体进行验证,例如验证跳转指令是否跳转到方法体之上。符号引用验证
:确保之后的解析阶段可以将符号引用转换为直接引用。主要是验证是否有无法访问到类、接口、字段、方法等。给类静态变量分配内存,并赋予默认零值。
将常量池中的符号引用转换为直接引用。主要是类、接口、方法、字段等。符号引用就是字面常量符号。直接引用,能直接指向目标,偏移量或句柄。
执行clinit()
,为静态变量赋予正确的初始值。若类无静态变量声明和静态代码块,该类可无clinit()
。若接口无静态变量声明,则该接口可无clinit()
。
clinit()
是按顺序收集静态变量声明和静态代码块得到的。
jvm中类由全限定名和类加载器唯一确定。
启动类加载器(BootstrapClassLoader)
c++实现,为jvm的一部分。加载jre/lib下的几个核心jar包,例如rt.jar等。尝试获取启动类加载器的话会返回null。
扩展类加载器(ExtClassLoader)
继承自java.lang.ClassLoader,加载jre/lib/ext和java.ext.dirs系统属性指定的路径下的类。
应用类加载器(系统类加载器 AppClassLoader)
继承自java.lang.ClassLoader,加载java命令指定的-classpath、系统属性java.class.path、环境变量CLASSPATH下的类。ClassLoader.getSystemClassLoader()
返回应用类加载器。应用类加载器是默认加载器。parent为扩展类加载器。
自定义类加载器
双亲委派模型
类加载请求到达类加载器时,先委托给父类加载器加载,若父类加载器还有父类加载器则递归向上委托,最终委托给启动类加载器。当父类加载器在其搜索范围找不到类时才由子类加载器自己加载。
ClassLoader的loadClass方法定义了双亲委派模型的逻辑。
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } }
双亲委派模型好处
类加载机制
线程上下文加载器
线程上下文加载器用于加载线程中运行代码中引用的类。默认的线程上下文加载器为应用类加载器。默认子线程会继承父线程的线程上下文加载器。通过Thread对象的setContextClassLoader
方法可以设置线程上下文加载器。