类加载过程的步骤
加载->验证->准备->解析->初始化->使用->卸载
- 加载:将硬盘上的字节码通过IO加载到内存上,注意是使用到时才会去加载,例如执行main方法,new 对象,反射等。会在方法区生成这个类的Class对象。
- 验证:验证字节码的准备性。
- 准备:给静态变量分配空间,并赋上默认值。
- 解析:符号引用替换为直接引用
- 初始化:给类的静态变量赋值,执行静态代码块。
类被加载到方法区(元空间)后主要包含,运行时常量池,字段信息,方法信息,类加载器的引用,class实例的引用等信息。
class实例:类在加载到方法区时,会在堆创建一个class类型对应的对象放到堆中,作为开发人员访问方法区中类定义的入口和切入点。
类加载器和双亲委派机制
上面的类加载主要通过类加载器实现的,Java有如下几种类加载器:
- 引导类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等。
- 扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR 类包。
- 系统类加载器:负责加载classpath路径下的类,主要是用户自己写的类。
- 自定义类加载器:可以自己定义类的加载器,一般会继承ClassLoader。
类加载器示例:
public class TestClassload {
public static void main(String[] args) {
//引导类加载器,因为用C实现的,所以为null。
System.out.println(String.class.getClassLoader());
//扩展类加载器ExtClassLoader
System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
//系统类加载器AppClassLoader
System.out.println(TestClassload.class.getClassLoader().getClass().getName());
System.out.println("=================================================");
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
//获取appClassLoader的父加载器
ClassLoader extClassloader = appClassLoader.getParent();
ClassLoader bootstrapLoader = extClassloader.getParent();
System.out.println("引导类加载器 : " + bootstrapLoader);
System.out.println("扩展类加载器 : " + extClassloader);
System.out.println("系统类加载器 : " + appClassLoader);
System.out.println("=================================================");
System.out.println("bootstrapLoader加载以下文件:");
URL[] urls = Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i]);
}
System.out.println("==================