抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

JVM加载class流程

Source -> .class -> ClassLoader -> RuntimeData(运行时区域, JVM的内存结构模型)

加载 class 具体流程

程序启动起来就是运行时, 任何程序都有运行时. 所有都编译成机器码的问题在于跨平台. 编译成不同平台机器码的过程叫做交叉编译.

GC 除了是内存回收工具, 还是 内存分配工具, 也就是说是 JVM Heap 的管控者.

本地库接口是任何操作语言都必须有的. 因为操作系统掌控着所有资源, 一定要和操作系统打交道. 除非找到安全漏洞, 绕过操作系统.

ClassLoader.class 文件导入 JVM , 形成运行时的类. 类的装载流程:

  • 加载 : 通过 ClassLoader 加载 class 文件字节码, 生成 Class 对象.
  • 链接: 就是将已经读入内存的类的二进制合并到 JVM 运行时环境中. loadClass() 方法中的 resolve 参数就是判定是否要解析该类。
    • 验证 : 检查加载的 class 的正确性(符合规范)和安全性.
    • 准备 : 为类变量分配存储空间并设置类变量初始值(默认值).
    • 解析 : JVM 将常量池内的符号引用转换为直接引用(JVM或其它运行时环境所能直接使用的形式).
  • 初始化 : 执行类变量赋值静态代码块, 为类的静态变量赋值.
  • 使用
  • 卸载

注意: loadClassforName 的区别

  • Class.forName 得到的 Class 是已经初始化完成了的。
  • ClassLoader.loadClass 的得到的 Class 是还没有解析的。

像 mysql-connector 就需要使用 forName 的方式加载, 因为是在 static 代码块里面进行初始化的.

Spring 的 IOC 就使用到 loadClass, 延迟加载, 不需要初始化和链接的步骤, 这样可以加快加载速度, 把类的初始化放在需要用到的时候才初始化.

执行引擎执行 byteCode 和 处理 GC

执行引擎调用本地库接口, 执行本地方法. 本地方法执行时使用 Native Stack(可能形成 Native Memory)

JIT 运行时优化. 处理器不同, 执行的指令会有所区别, 所以可以使用该处理器的一些高效指令.

类的加载方式

  • 隐式加载 : new
  • 显式加载 : loadClass, forName

双亲委派模型

class 默认的加载方式. ClassLoader 主要用于装载阶段, 其主要作用从系统外部获得 Class 二进制数据流. 它是 Java 的核心组件, 所有的 Class 都由 ClassLoader 进行加载, ClassLoader 负责通过将 Class 文件的二进制数据流装载进系统, 然后交给 JVM 进行链接(校验, 准备, 解析), 初始化, 使用, 卸载等操作.

ClassLoader 接收的是 .class, 产出 byteCode.

只有一个 ClassLoader

需求多样

  • 从文件
  • 从内存
  • TCP/UDP/HTTP
  • 从其它Library
  • 其它策略
    • 缓存策略
    • 安全策略
    • 统计策略
    • 代理策略

考虑封装, 继承, 组合, 考虑各种复用手段.

版本问题

树状结构, 将Module1, Module2分开了.

安全策略

FooBar 之间是不可见的.

  • 委托父类加载
  • 父子之间信任关系

MetaPrograming: 运行时生成 Class, 然后执行. 像 AspectJ 这种 AOP 框架, 就很需要.

需要什么模型?

树状关系

  • Root Class Loader
  • Leaf Class Loader

委托关系

  • Class Loader 委托父 Class Loader 完成工作
  • 缓存设置到父节点

ClassLoader 四层模型

BootStrap Class Loader: 加载核心类, 例如: Standard JDK. 使用 C++ 编写的.

Extensions Class Loader: 加载扩展类. 例如JRE目录下面的 lib/ext(JDK8以及之前). JDK 9 之后是 PlatformClassLoader. 因为整个 JDK 基于模块化进行构建了.

Application Class Loader: classpath

Custom Class Loader: 用户自定义

加载流程

先判断自己有没有加载, 如果没有向上传递, 委托给上一层加载. 如果上一层尝试加载失败, 给下一层尝试加载.

双亲: 指父亲和母亲…但是 ClassLoader 就一个父亲…

委派倒是可以理解, 向上一层委托. 避免多份字节码的加载.

ClassLoader.java源码分析

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    // 加锁, 多线程, 防止
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        // 查找缓存中是否存在, 如果存在就返回
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 调用父级的 classLoader
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    // 找 BootStrap 的, 顶层
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            // 委托父级没有找到. 自己找
            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                long t1 = System.nanoTime();
                // 这个方法是没有实现的, 直接抛异常, 需要子类去实现重写
                c = findClass(name);

                // this is the defining class loader; record the stats
                PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                PerfCounter.getFindClasses().increment();
            }
        }
        // resolve 是否要链接(解析)这个类
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

所以, 自定义类加载器, 要实现ClassLoader类的findClass方法, 然后调用defineClass转成class对象.

远程加载class.

如果要深入探讨, 探索下 OSGi(Open Service Gateway Initiative, 开放服务网关协议) 模型, 是 Java动态化模块系统的一系列规范.

评论