详解类加载机制
类的加载指将编译好的Class类文件中的字节码读入内存中,将其放在方法区内并创建对应的Class对象。类的加载分为加载、链接、初始化,其中链接又包括验证、准备、解析三步。如下图所示
- 加载是文件到内存的过程。通过类的完全限定名查找此类字节码文件,并利用字节码文件创建一个Class对象。
- 验证是对类文件内容验证。目的在于确保Class文件符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种:文件格式验证,元数据验证,字节码验证,符合引用验证。
- 准备阶段是进行内存分配。为类变量也就是类中由static修饰的变量分配内存,并且设置初始值。初始值是0或者null,而不是代码中设置的具体指,代码中设置的值是在初始化阶段完成的。另外这里也不包含用final修饰的静态变量,因为final在编译的时候就会分配。
- 解析主要是解析字段、接口、方法。主要是将常量池中的符合引用替换为可以直接使用的过程。直接引用就是直接指向目标的指针,相对偏移量等。
- 初始化,主要完成静态块执行与静态变量的赋值。这是类加载的最后阶段,若被加载类的父类没有初始化,则先对父类进行初始化。
只有对类主动使用时,才会进行初始化,初始化的触发条件包括在创建类的实例时、访问类的静态方法或者静态变量时、Class.forName()反射类时、或者某个子类被初始化时。
如上图所示,浅绿的两个部分表示类的生命周期,就是从类的加载到类实例的创建与使用,再到类对象不再被使用时可以被GC卸载回收。这里要注意一点,由Java虚拟机自带的三种类加载器加载的类在虚拟机的整个生命周期中是不会被卸载的,只有用户自定义的类加载器的类才可以被卸载
详解类加载器
如上图所示,Java自带的三种类加载器分别是:Bootstrap启动类加载器、扩展类加载器和应用加载器(也叫系统加载器)。图右边的桔黄色文字表示各类加载器对应的加载目录。启动类加载器加载java home 中 lib 目录下的类,扩展加载器负责加载ext目录下的类,应用加载器加载classpath指定目录下的类。除此之外,可以自定义类加载器。
Java的类加载使用双亲委派模式,即一个类在加载类时,先把这个请求委托给自己的父类加载器去执行,如果父类加载器还存在父类加载器,就继续向上委托,直到顶层的启动类加载器,如上图中蓝色向上的箭头。如果父类加载器能够完成类加载,就成功返回,如果父类加载器无法完成加载,那么子加载器才会尝试自己去加载。如图中的桔黄色向下的箭头。
这种双亲委派模式的好处,可以避免类的重复加载,另外也避免了Java的核心API被篡改。