JVM系列之四:类装载器

class装载验证流程

加载

– 装载类的第一个阶段

– 取得类的二进制流

– 转为方法区数据结构

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

链接

– 验证

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

· 文件格式的验证

· 是否以0xCAFEBABE开头

· 版本号是否合理

· 元数据验证

· 是否有父类

· 继承了final类?

· 非抽象类实现了所有的抽象方法

· 字节码验证 (很复杂)

· 运行检查

· 栈数据类型和操作码数据参数吻合

· 跳转指令指定到合理的位置

· 符号引用验证

· 常量池中描述类是否存在

· 访问的方法或字段是否存在且有足够的权限

 

– 准备

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

· public static int v=1;

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

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

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

· public static final  int v=1;

 

– 解析

· 符号引用替换为直接引用

初始化

– static变量 赋值语句

– 执行类构造器<clinit>

· static变量 赋值语句

· static{}语句

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

– <clinit>是线程安全的

 

什么是类装载器ClassLoader

 

ClassLoader是一个抽象类

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

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

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

 

JDKClassLoader默认设计模式

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)

• 寻找已经加载的类

分类

– BootStrap ClassLoader (启动ClassLoader

• 只加载rt.jar中的类或者通过-Xbootclasspath设置该路径下的所有类

– Extension ClassLoader (扩展ClassLoader

• 只加载ext里面的所有类

– App ClassLoader (应用ClassLoader/系统ClassLoader

• 加载ClassPath下的所有类,也就是自己编写的类

– Custom ClassLoader(自定义ClassLoader)

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

协同工作

 

双亲委派模式

使用双亲委派模型来组织类加载器之间的关系,好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系。

例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。

相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,用户编写了一个java.lang.Object的类,并放在程序的ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中已有类重名的Java类,将会发现可以正常编译,但是永远无法被加载运行

双亲委派模型的系统实现:

双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却很简单,实现集中在java.lang.ClassLoaderloadClass() 
方法中,在其方法中,主要判断逻辑如下:先检查是否已经被加载过,

· 若没有被加载过,则接着判断父加载器是否为空。 

若不为空,则调用父类加载器loadClass()方法。

若父加载器为空,则默认使用启动类加载器作为父加载器

· 如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。

 

 

问题

 

 

解决

– 上下文加载器

– 是一个角色

– 用以解决顶层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可以突破双亲模式的局限性。

 

打破常规模式

双亲模式的破坏

– 双亲模式是默认的模式,但不是必须这么做

– TomcatWebappClassLoader 就会先加载自己的Class,找不到再委托parent

– OSGiClassLoader形成网状结构,根据需要自由加载Class

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值