类加载初始化
类加载-初始化
1类加载过程图
当class文件,经过loading(加载阶段)后,把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。
进入linking(连接)阶段,在verification(验证)阶段,对文件的格式、字节码之类的进行验证,在preparation(准备)阶段,则只会对类中的静态变量赋值默认值(即被static修饰的变量)。默认值指对象的0值,例如String的默认值为null,int为0。在resolution(解析)阶段,将类、方法、属性等符号引用解析为直接引用,常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用。
(注解:
符号引用(Symlxiuc References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量, 只要使用时能无歧义地定位到目标即可。
直接引用(Direct References):直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用是与虚拟机实现的内存布局相关的 , 同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同. 如果有了直接引用, 那引用的目标必定已经在内存中存在
)
在initializing(初始化)阶段,为静态变量赋值为初始值。
2类加载器(双亲委派机制)
Java的类加载器是分层的,不同的类加载器分别加载不同的类。
双亲委派:
当加载一个类,首先查看自己的自定义加载器是否有这个内存,如果没有,自定义加载器并不是马上加载,而是会去他的父亲方向的加载器去查看是否已经加载到内存中。当还没有加载时,继续采用同样的机制,往上查找,当发现最顶层的启动类即Bootstrap的内存中仍然没有加载时,则查看自己能不能加载这个类,如果能加载则加载,如果不能加载时,则会往下,让其子加载器进行加载。到了最后自定义类加载器,如果自定义加载器仍然不能加载,则会抛出ClassNotFoundException。
为什么JVM的类加载要采用双亲委派的加载机制?
- 节省内存
- 主要是为了安全,避免用户自己编写的类动态替换 Java的一些核心类,比如 String
- 避免了类的重复加载
下面通过代码演示
package classLoader;
|
类加载全过程
- 使用类加载调用loadClass方法即可。
- 自定义ClassLoader重写findClass即可(模板方法模式)
extends ClassLoader
overwrite findClass() -> defineClass(byte[] -> Class clazz)
(3)打破双亲委派机制,重写loadClass
JDK1.2之前,自定义ClassLoader都必须重写loadClass()
ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
热启动,热部署
tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本)
jvm默认的模式为混合模式