11、类加载器
类加载:
- 加载:将类的字节码载入方法区中,内部采用C++的instanceKlass描述java类,它的重要field有:
- _java_mirror即java的类镜像,例如对String来说,就是String class,作用是把Klass暴露给java使用
- _super即父类
- _fields即成员变量
- _methods即方法
- _constants即常量池
- _class_loader即类加载器
- _vtable虚方法表
- _itable接口方法表
- 如果这个类还有父类没有加载,先加载父类
- 加载和链接可能是交替运行的
- 链接:
- 验证:验证类是否符合JVM规范,安全检查
- 准备:为static变量分配空间,设置默认值
- static变量在jdk7前储存于instanceKlass末尾,从jdk7开始,储存于,_java_mirror末尾
- static变量分配空间和赋值是两个步骤,分配空间在准备阶段完成,赋值在初始化阶段完成
- 如果static变量是final的基本类型,以及字符串常量,那么编译阶段就确定了,赋值在准备阶段完成
- 如果变量是final,但属于引用类型,那么赋值也会在初始化阶段完成
- 解析:将常量池中的符号引用解析为直接引用
- 初始化:初始化即调用<clinit>()v,虚拟机会保证这个类的构造方法线程安全。
发生的时机:
-
- 概括的说,类的初始化是懒惰的
- main方法所在的类,总会被首先初始化
- 首次访问这个类的静态变量或静态方法时
- 子类初始化,如果父类还没有初始化会引发
- 子类访问父类的静态变量,只会触发父类的初始化
- Class.forName
- new会导致初始化
不会导致类初始化的情况
- 访问类的static final静态常量(基本类型和字符串)不会触发初始化
- 类对象class不会触发初始化
- 创建该类的数组不会触发初始化
- 类加载器的loadClass方法
- Class.forName的参数2为flase时
- 类加载器:第一遍只是检查有没有加载,第二遍查自己加载的包下有没有,有就加载,没有就让子加载器加载(双亲委派)
- 启动类加载器:
- 扩展类加载器
- 双亲委派模式:指调用类加载器的loadClass方法时,查找类的规则
- 检查该类是否已经加载
- 如果有上级的话委派上级loadClass
- 如果没有上级了,则委派BoottrapClassLoader
- 每一层找不到,调用findClass方法(每个类加载器自己扩展)来加载
- 线程上下文加载器
- 自定义类加载器:
什么时候需要自定义加载器
-
- 想加载非classpath随意路径中的类文件
- 都是通过接口来使用实现,希望解耦时,常用在框架设计
- 这些类希望予以隔离,不同应用的同名类都可以加载,不冲突,常见于tomcat容器
步骤
- 继承ClassLoader父类
- 要遵从双亲委派机制,重写findClass方法(注意不是重写loadClass方法,否则不会走双亲委派机制)
- 读取类文件的字节码
- 调用父类的defineClass方法来加载类
- 使用调用该类加载器的loadClass方法