类的加载过程分为:加载、链接和初始化三个过程
加载过程就是查找该类的字节流并通过字节流来创建类的过程,这个过程需要借助类加载器来完成。类加载器分为启动类加载器(BootStrap ClassLoader),扩展类加载器(Extension ClassLoader),应用类加载器(Application ClassLoader),和用户自定义的类加载器,启动类加载器加载最为基础最为重要的类,例如放在jre的lib目录下的jar包,以及-Xbootclasspath参数指定的类,扩展类加载器加载较为次要,但是很通用的类,例如放在jre的lib/ext目录下的jar包,应用类加载器主要用来加载classpath路径下的类。
加载过程遵循双亲委派模型,当类加载器接收到一个加载请求时,首先不会自己去加载,而是将请求转发给父类,在父类加载器未找到所请求的类的情况下,才会尝试执行加载过程,如下所示,应用类加载器的父类加载器是扩展类加载器,扩展类加载器的父类加载器是启动类加载器,需要注意的是这里提到的父类加载器是“父-类加载器”而不是“父类-加载器”,三种加载器不存在继承关系,启动类加载器是C++实现的,在jvm中不存在实例,所以返回null,AppClassLoader和ExtClassLoader是sun.misc.Launcher类的内部类,它们都继承了URLClassLoasder,URLClassLoader是加载器顶层接口ClassLoader的子类。使用双亲委派模型的目的是保证安全,即保证最为基础的类有启动类加载器来加载,避免去加载用户自定义的和基础类名字相同的类。
//sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(Thread.currentThread().getContextClassLoader());
//sun.misc.Launcher$ExtClassLoader@4f023edb
System.out.println(Thread.currentThread().getContextClassLoader().getParent());
//null
System.out.println(Thread.currentThread().getContextClassLoader().getParent().getParent());
链接过程是将创建好的类合并入JVM,使之能够执行的过程。链接分为、、三个过程:
验证过程检验加载的类是否符合JVM的约束;
准备阶段JVM为类的静态字段分配内存,静态字段的初始化则发生在后面的初始化阶段;
在class文件被加载到java虚拟机之前,这个类无法知道其他类及其方法和字段的地址,甚至不知道自己的方法和字段的地址。因此每当需要引用这些字段和方法时,java编译器会生成一个符号引用,在运行阶段这个符号引用能无歧义的定位到具体位置上,这个就是解析的过程。
初始化过程中,被final修饰的静态变量,并且器类型是基本类型或者字符串时,则该值被标记为常量值,常量值的初始化是由虚拟机直接完成的,其他的静态变量和静态代码块被统一放在clinit()中进行初始化,该方法是加锁的,保证线程安全。
类加载链接的目的是为了在JVM中构建类的结构;
类加载过程是针对单个类的,不同的类加载过程通常发生在不同的阶段;
链接过程不是取得被加载的类的地址,而是被加载的类引用的其他方法的地址。
网友评论