Java:类加载器

参考

JavaGuide

简介

  • 类加载器的主要作用是加载Java类的字节码即.class文件到JVM中。
  • 每个Java类都有一个引用指向加载它的类加载器。
  • 数组类是JVM直接生成而非加载的。
  • 根据需要动态加载,已加载的类放到ClassLoader中,相同二进制名称的类只会被加载一次。

JVM内置类加载器

一共有三种

  • BootstrapClassLoader:最顶层的加载类,由C++实现,主要用于加载JDK内部的核心类库。
  • ExtensionClassLoader:扩展类加载器,主要负责加载%JRE_HOME%/lib/ext目录下的jar包。
  • AppClassLoader:面向用户的类加载器:负责加载当前应用classpath下的jar包和类。

除了BootstropClassLoader是JVM自身的一部分外,其他所有的类加载器都在JVM外部实现,并继承自ClassLoader抽象类。

每个ClassLoader可以通过getParent()获取其父ClassLoader,即加载它的ClassLoader,BootstrapClassLoader的父是null。类加载器之间的父子关系一般不是以继承的关系来实现的,而通常使用组合关系来复用父加载器的代码。

public abstract class ClassLoader {
  ...
  // 包含一个父类加载器实例
  private final ClassLoader parent;
  protected ClassLoader(ClassLoader parent) {
       this(checkCreateClassLoader(), parent);
  }
  ...
}

自定义类加载器

继承ClassLoader抽象类,有两个关键方法。

  • loadClass:加载指定二进制名称的类,默认使用双亲委派模型。
  • findClass:根据名称查找类,默认为空方法。

默认的loadClass如下:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        //首先,检查该类是否已经加载过
        Class c = findLoadedClass(name);
        if (c == null) {
            //如果 c 为 null,则说明该类没有被加载过
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    //当父类的加载器不为空,则通过父类的loadClass来加载该类
                    c = parent.loadClass(name, false);
                } else {
                    //当父类的加载器为空,则调用启动类加载器来加载该类
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                //非空父类的类加载器无法找到相应的类,则抛出异常
            }

            if (c == null) {
                //当父类加载器无法加载时,则调用findClass方法来加载该类
                //用户可通过覆写该方法,来自定义类加载器
                long t1 = System.nanoTime();
                c = findClass(name);

                //用于统计类加载器相关的信息
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            //对类进行link操作
            resolveClass(c);
        }
        return c;
    }
}

双亲委派模型:某个类加载器需要查找类或资源时,会先判断是否已被加载过,如被加载过则直接返回,没被加载过则将查找任务委托给其父类加载器,一个新类的加载最终会被传送到顶层的BootstrapClassLoader中,如果仍未完成加载,才会去调用自己的findClass方法去加载。

双亲委派模型的好处在于避免类的重复加载,而且防止核心API被篡改,比如不会加载用户自定义的Object类,因为顶层加载器已经加载过了。

自定义加载器时,如果不需打破双亲委派模型,重写findClass即可,如果需要打破,两个都要重写。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值