亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

深入JVM內核6 類裝載器

標簽:
Java

1 class装载验证流程

1.1 加载

  1. 装载类的第一个阶段

  2. 取得类的二进制流

  3. 转为方法区数据结构

  4. 在Java堆中生成对应的java.lang.Class对象

1.2 链接

(1) 验证

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

  • 文件格式的验证
    是否以0xCAFEBABE开头
    版本号是否合理

  • 元数据验证
    是否有父类
    继承了final类?
    非抽象类实现了所有的抽象方法

  • 字节码验证 (很复杂)
    运行检查
    栈数据类型和操作码数据参数吻合
    跳转指令指定到合理的位置

  • 符号引用验证
    常量池中描述类是否存在
    访问的方法或字段是否存在且有足够的权限

(2) 准备

分配内存,并为类设置初始值 (方法区中)

  • public static int v=1;

  • 在准备阶段中,v会被设置为0

  • 在初始化的<clinit>中才会被设置为1

  • 对于static final类型,在准备阶段就会被赋上正确的值

  • public static final  int v=1;

(3) 解析

符号引用替换为直接引用

  • 字符串引用对象不一定被加载

  • 指针或者地址偏移量引用对象一定在内存

1.3 初始化
  • 执行类构造器<clinit>
    static变量 赋值语句
    static{}语句

  • 子类的<clinit>调用前保证父类的<clinit>被调用

  • <clinit>是线程安全的

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

很显然是在链接的验证阶段的符号引用验证时会抛出这个异常,或者NoSuchMethodError等异常。

2 什么是类加载器ClassLoader

  • ClassLoader是一个抽象类

  • ClassLoader的实例将读入Java字节码将类装载到JVM中

  • ClassLoader可以定制,满足不同的字节码流获取方式

  • ClassLoader负责类加载过程中的加载阶段

2.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)
2.2 ClassLoader分类
  • BootStrap ClassLoader (启动ClassLoader)

  • Extension ClassLoader (扩展ClassLoader)

  • App ClassLoader (应用ClassLoader/系统ClassLoader)

  • Custom ClassLoader(自定义ClassLoader)

  • 每个ClassLoader都有一个Parent作为父亲

3 JDK中ClassLoader默认设计模式

webp


路径:
Bootstrap ClassLoader rt.jar /-Xbootclasspath
Extension ClassLoader  %JAVA_HOME%/lib/ext/*.jar
App ClassLoader Classpath完全自定义路径
Custom ClassLoader 完全自定义路径

底向上检查类是否被加载,一般情况下,首先先从AppClassLoader中调用findLoadedClass查看是否已经加载,如果没有加载,则会交给父类,ExtensionClassLoader去查看是否加载,还没加载,则再调用其父类,BootstrapClassLoader查看是否已经加载,如果仍然没有,自顶向下尝试加载类,那么从 BootstrapClassLoader到 AppClassLoader依次尝试加载。
从代码上可以很容易看出来,首先先自己查看这个类是否被调用,如果没有找到,则调用父亲的loadClass,直到BootstrapClassLoader(没有父亲)。

我们把这个加载的过程叫做双亲模式。

双亲委托机制的作用是防止系统jar包被本地替换

3.1 双亲模式的问题

这种模式下会有一个问题:
顶层ClassLoader,无法加载底层ClassLoader的类

Java框架(rt.jar)如何加载应用的类?

比如:javax.xml.parsers包中定义了xml解析的类接口
Service Provider Interface SPI 位于rt.jar
即接口在启动ClassLoader中。
而SPI的实现类,在AppLoader。

这样就无法用BootstrapClassLoader去加载SPI的实现类。

3.2 解决

JDK中提供了一个方法:

Thread. setContextClassLoader()
  • 上下文加载器

  • 是一个角色

  • 用以解决顶层ClassLoader无法访问底层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可以突破双亲模式的局限性

3.3 双亲模式的破坏

双亲模式是默认的模式,但不是必须这么做
Tomcat的WebappClassLoader 就会先加载自己的Class,找不到再委托parent
OSGi的ClassLoader形成网状结构,根据需要自由加载Class

破坏双亲模式例子-  先从底层ClassLoader加载

OrderClassLoader的部分实现

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {    // First, check if the class has already been loaded
    Class re=findClass(name);    if(re==null){
        System.out.println(“无法载入类:”+name+“ 需要请求父加载器");
        return super.loadClass(name,resolve);
    }
    return re;
}
protected Class<?> findClass(String className) throws ClassNotFoundException {
Class clazz = this.findLoadedClass(className);if (null == clazz) {    try {
        String classFile = getClassFile(className);
        FileInputStream fis = new FileInputStream(classFile);
        FileChannel fileC = fis.getChannel();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        WritableByteChannel outC = Channels.newChannel(baos);
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
         省略部分代码
        fis.close();        byte[] bytes = baos.toByteArray();

        clazz = defineClass(className, bytes, 0, bytes.length);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}return clazz;
}



作者:香沙小熊
链接:https://www.jianshu.com/p/52674b8e7da2


點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消