首页 > 技术文章 > 类加载器与类加载过程

changtong1819 2021-04-15 22:25 原文

类加载器与类加载过程详解:

相关面试题

  1. 如何判断两个 class 对象是否相同?
    在JVM 中表示两个 class 对象是否为同一个类存在两个必要条件:

    • 类的完整类名必须一致,包括包名。
    • 加载这个类的 ClassLoader(指 ClassLoader 实例对象)必须相同。
      换句话说,在 JVM 中,即使这两个类对象(class对象)来源同一个 Class 文件,被同一个虚拟机所加载,但只要加载它们的 ClassLoader 实例对象不同,那么这两个类对象也是不相等的。
      JVM 必须知道一个类型是由启动加载器加载的还是由用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么 JVM 会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。当解析一个类型到另一个类型的引用的时候,JVM 需要保证这两个类型的类加载器是相同的。
  2. 什么情况下类会被初始化(即上面的类加载过程的初始化阶段)
    类被主动使用时才进行初始化:

    • 创建类的实例
    • 访问某个类或接口的静态变量,或者对该静态变量赋值
    • 调用类的静态方法
    • 反射(比如:Class.forName("***"))
    • 初始化一个类的子类
    • Java 虚拟机启动时被标明为启动类的类
    • JDK 7 开始提供的动态语言支持:调用到静态句柄,也会被初始化
  3. 什么是类的加载?类的生命周期?

    • JVM把通过类名获得类的二进制流之后,把类放入方法区,并创建入口对象的过程被称为类的加载。
    • 生命周期:加载、链接、初始化、使用、卸载“五个阶段,链接又可以分为”验证、准备、解析“三个阶段。
  4. 什么时候会为变量分配内存?
    在准备阶段为静态变量分配内存。

  5. JVM的类加载机制是什么?
    双亲委派机制,类加载器会先让自己的父类来加载,父类无法加载的话,才会自己来加载。

  6. 双亲委派机制可以打破吗?为什么
    可以打破,比如JDBC使用线程上下文加载器打破了双亲委派机制。原因是JDBC只提供了接口,并没有提供实现。

  7. 双亲委派机制内容、代码实现原理?
    原理见上面的绘图,源码分析如下,重点在判断、递归

protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先检查这个classsh是否已经加载过了
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    // c==null表示没有加载,如果有父类的加载器则让父类加载器加载
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        //如果父类的加载器为空 则说明递归到bootStrapClassloader了
                        //bootStrapClassloader比较特殊无法通过get获取
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {}
                if (c == null) {
                    //如果bootstrapClassLoader 仍然没有加载过,则递归回来,尝试自己去加载class
                    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) {
                resolveClass(c);
            }
            return c;
        }
    }

推荐阅读