当程序准备运行某个类,但该类还未被加载到内存中时,会经过以下三个步骤进行类的加载:
类的加载(Load)→类的连接(Link)→类的初始化(Initialize)
- 加载:类经过javac.exe编译的.class字节码文件读入内存(将静态数据转换成堆中方法区的运行时数据结构),并为之创建一个java.lang.Class对象作为方法区中类数据的访问入口(引用的地址),需要访问和使用类数据只能通过这个Class对象;此过程由类的加载器完成;
- 链接:将java类的二进制代码合并到JVM的运行状态中的过程;
- 验证:确保加载的类符合JVM规范;
- 准备:正式为类变量(static)分配内存并设置变量默认初始值(非任何显示赋值),这些内存都在方法区中分配;
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
- 初始化:JVM负责对类进行初始化;
- 执行类构造器
()方法的: 此方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的(类构造器是构造类信息的,并非new对象构造器) - 如其父类为进行初始化,则初始化操作从先从父类进行;
虚拟机会保证一个类的
()方法在多线程环境中被正确加锁和同步; 类加载器ClassLoader的作用:
除了上面提到的作用,还有一个类缓存机制:一旦某个类被加载到内存中,将位置加载(缓存)一段时间,相当于一个缓存了一个Class对象,无论此类创建多少个实例,都是从这唯一的结构中获取信息;GC也可以回收这些Class对象;
JVM规范定义的类的加载器类型如下:
测试代码:
@Test public void test1(){ //获取一个系统类加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); //获取系统类加载器的父类加载器,即扩展类加载器 ClassLoader extensionClassLoader = systemClassLoader.getParent(); System.out.println(extensionClassLoader); //获取扩展类加载器的父类加载器,即引导类加载器 ClassLoader bootstapClassLoader = extensionClassLoader.getParent(); //引导类加载器用于加载java核心库,无法直接获取,故输出null System.out.println(bootstapClassLoader); }
- 执行类构造器