jvm专题系列-6、类装载器

6 篇文章 0 订阅

一、class装载验证流程

加载

链接

        验证

        准备

        解析

初始化

1.1 加载

装载类的第一个阶段,主要做以下3件事

  1. 取得类的二进制流
  2. 转为方法区数据结构
  3. 在Java堆中生成对应的java.lang.Class对象

1.2 链接 -> 验证

目的:保证Class流的格式是正确的

  • 文件格式的验证 
  • 是否以0xCAFEBABE开头 
  • 版本号是否合理
  • 元数据验证 
  • 是否有父类 
  • 继承了final类? 
  • 非抽象类实现了所有的抽象方法 
  • 字节码验证 (很复杂) 
  • 运行检查 
  • 栈数据类型和操作码数据参数吻合 
  • 跳转指令指定到合理的位置
  • 符号引用验证 
  • 常量池中描述类是否存在 
  • 访问的方法或字段是否存在且有足够的权限

1.3 链接 -> 准备

分配内存,并为类设置初始值 (方法区中)
    public static int v=1;
    在准备阶段中,v会被设置为0
    在初始化的<clinit>中才会被设置为1
    对于static final类型,在准备阶段就会被赋上正确的值
    public static final  int v=1;
 

1.4 链接 -> 解析

1.5 初始化

执行类构造器<clinit>
    static变量 赋值语句
    static{}语句
子类的<clinit>调用前保证父类的<clinit>被调用
<clinit>是线程安全的

1.6 问题

Java.lang.NoSuchFieldError错误可能在什么阶段抛出?

二、什么是类装载器ClassLoader

ClassLoader是一个抽象类
ClassLoader的实例将读入Java字节码将类装载到JVM中
ClassLoader可以定制,满足不同的字节码流获取方式
ClassLoader负责类装载过程中的加载阶段
​

三、JDK中ClassLoader默认设计模式

3.1 默认设计模式

ClassLoader的重要方法
    public Class<?> loadClass(String name) throws ClassNotFoundException
        载入并返回一个Class
    protected final Class<?> defineClass(byte[] b, int off, int len)
        定义一个类,不公开调用
    protected Class<?> findClass(String name) throws ClassNotFoundException
        loadClass回调该方法,自定义ClassLoader的推荐做法
    protected final Class<?> findLoadedClass(String name) 
        寻找已经加载的类

3.2 分类

△ BootStrap ClassLoader (启动ClassLoader)
△ Extension ClassLoader (扩展ClassLoader)
△ App ClassLoader (应用ClassLoader/系统ClassLoader)
△ Custom ClassLoader(自定义ClassLoader)
每个ClassLoader都有一个Parent作为父亲

3.3 协同工作模式

3.4 示例1

直接运行以上代码:   
    I am in apploader
加上参数 -Xbootclasspath/a:D:/tmp/clz
    I am in bootloader
    此时AppLoader中不会加载HelloLoader
        I am in apploader 在classpath中却没有加载
        说明类加载是从上往下的

3.5 示例2

3.6 问题1

能否只用反射,仿照上面的写法,将类注入启动ClassLoader呢?

3.7 问题2

Thread. setContextClassLoader()
    上下文加载器
    是一个角色
    用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题
    基本思想是,在顶层ClassLoader中,传入底层ClassLoader的实例
​
解决
    static private Class getProviderClass(String className, ClassLoader cl,
        boolean doFallback, boolean useBSClsLoader) throws ClassNotFoundException
{
    try {
        if (cl == null) {
            if (useBSClsLoader) {
                return Class.forName(className, true, FactoryFinder.class.getClassLoader());
            } else {
                cl = ss.getContextClassLoader();
                if (cl == null) {
                    throw new ClassNotFoundException();
                }
                else {
                    return cl.loadClass(className); //使用上下文ClassLoader
                }
            }
        }
        else {
            return cl.loadClass(className);
        }
    }
    catch (ClassNotFoundException e1) {
        if (doFallback) {
            // Use current class loader - should always be bootstrap CL
            return Class.forName(className, true, FactoryFinder.class.getClassLoader());
        }
…..
​
代码来自于 javax.xml.parsers.FactoryFinder  展示如何在启动类加载器加载AppLoader的类
上下文ClassLoader可以突破双亲模式的局限性
​
双亲模式的破坏
    双亲模式是默认的模式,但不是必须这么做
    Tomcat的WebappClassLoader 就会先加载自己的Class,找不到再委托parent
    OSGi的ClassLoader形成网状结构,根据需要自由加载Class

3.8 破坏双亲模式例子

先从底层ClassLoader加载

四、热替换

当一个class被替换后,系统无需重启,替换的类立即生效
例子:
    geym.jvm.ch6.hot.CVersionA
    
public class CVersionA {
    public void sayHello() {
        System.out.println("hello world! (version A)");
    }
}
​
DoopRun 不停调用CVersionA . sayHello()方法,因此有输出:
    hello world! (version A)
在DoopRun 的运行过程中,替换CVersionA 为:
​
public class CVersionA {
    public void sayHello() {
        System.out.println("hello world! (version B)");
    }
}
替换后, DoopRun 的输出变为
    hello world! (version B)
​
​
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步道师就是我

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值