类加载——JVM

类加载

加载、连接、初始化、使用、卸载

连接又分为 验证准备解析

类的加载阶又相当于类的生命周期。

找到需要 加载的类 并把 类的信息 加载到 jvm的方法区 ,然后在 堆区实例化 一个 java.lang.Class 对象,作为方法区中这个类的信息的入口。

类加载 其实包括加载、连接、初始化三个阶段。

类加载强调一个jvm能够直接使用所需的类,所以类必须完成初始化。

狭义的类加载

1. Loading 阶段在做什么?

加载(Loading)阶段 :JVM 通过各种途径获取类的二进制字节流 ,并由 ClassLoader 将其"定义"为 JVM 可识别的类,最终在 JVM 内部生成并返回对应的 java.lang.Class 对象。

更具体一点:

  • ClassLoader 根据**类的全限定名(Fully Qualified Name)**定位并获取该类的二进制字节流

    (来源可能是 .classjar、网络、动态生成等)

  • 然后将字节流交给 JVM 去执行"defineClass"这一类的定义过程

  • JVM 完成类的内部结构创建后,返回对应的 java.lang.Class 实例

2. 类的字节流从哪里来?

类的加载方式非常灵活,常见来源包括:

  1. 从本地 .class 文件加载(常用)

    根据类的全路径名找到对应的 .class 文件,读取文件内容得到字节流。

  2. jar/war 等归档包中加载(常用)

    Java 项目打包后,大部分类都位于 jar 包中,类加载器会从包内读取字节流。

  3. 从网络加载(历史上常见)

    例如早期流行的 Applet,可以从远程网络获取字节流并加载。

  4. 运行时动态生成(常见于框架/设计模式)

    按规则即时生成字节码,例如:

    • 动态代理(JDK Proxy / CGLIB)

    • 字节码增强(ASM / ByteBuddy 等)

  5. 从非 .class 的其他格式转换得到(本质相同)

    例如某些自定义格式或加密/压缩的类数据,最终仍会转换为 JVM 可识别的字节流再加载。

    本质:最终目标仍然是"拿到合法的 class 字节码"。


3. 什么时候会触发加载?

不同 JVM 对"加载时机"的实现可能存在差异,具体取决于虚拟机实现。

连接

验证 - 准备 - 解析

  1. 验证(Verification)

    进行类的合法性校验

    对比如 字节码格式变量/方法的合法性数据类型的有效性继承与实现的规范性 等进行检查。

    确保被加载的类能够正常被 JVM 运行。

  2. 准备(Preparation)
    分内存、赋初值

    1. 为类的 静态变量(static) 分配内存,并设为 JVM 默认初始值

    2. 对于 非静态变量(实例变量) ,则不会为它们分配内存。

    注意 :这里的初始值是 JVM 默认赋值,而不是程序里写的赋值。

    规则

    • 基本类型 默认值为 0intlongshortcharbytebooleanfloatdouble

    • 引用类型 默认值为 null

    • 常量的默认值为程序中设定的值,例如:

    java 复制代码
    final static int a = 100;

    则准备阶段 a 的值就是 100

  3. 解析(Resolution)

    把常量池的 符号引用 转换为 直接引用

类的初始化

初始化是类加载过程的最后一个阶段 (Loading → Linking → Initialization)。

在这个阶段,JVM 会执行类的类初始化方法 <clinit>(),也就是把:

  • 静态变量的显式赋值static int a = 1;

  • 静态代码块static { ... }

按源码顺序合并到 <clinit>() 里执行。

注意:在 Linking 的 Preparation 阶段,静态变量只是被分配内存并设置默认值(0/null/false),真正的显式赋值与静态块逻辑是在初始化阶段执行。


什么时候会触发「初始化」?(主动使用 / Active Use)

JVM 对"如何加载"没有完全硬性规范,但对"何时初始化"有明确规则:
只有当类型被"主动使用(direct/active use)"时,才会触发初始化。常见触发点:

  1. new 创建该类实例

  2. 读/写该类的静态字段 (但编译期常量例外,见下)

  3. 调用该类的静态方法

  4. 反射调用上述 1/2/3(如 Class.forName、反射访问静态成员等)

  5. 初始化一个子类时,会先初始化其父类(这是"父类优先"的递归链)

  6. 作为程序入口执行,首次调用包含 main 的那个类时会初始化该类

一个关键例外:编译期常量不会触发初始化

如果是 static final 且是编译期可确定的常量 ,使用它可能被编译器直接内联 ,从而不触发类初始化

class A { static final int X = 1; // 编译期常量 static { System.out.println("A init"); } } System.out.println(A.X); // 很可能不打印 "A init"


初始化时具体"执行什么"?

初始化阶段只做一件事:执行 <clinit>()。因此:

  • 会执行:静态代码块、静态变量的显式赋值

  • 不会执行:实例代码块、实例字段赋值、构造器 (这些属于"对象初始化",由 new 触发并在对象创建时执行)

你原句里"非静态与非静态的静态语法均不执行"建议改成更标准的这句:

类初始化只执行静态初始化逻辑;所有实例初始化逻辑都不会在类初始化阶段执行。


初始化顺序(父类优先)

  • 如果该类有父类:先初始化父类,再初始化子类

  • 同一个类内部:按源码顺序执行静态赋值/静态块(它们共同组成 <clinit>()

你原来的"弗雷德初始化"应当是笔误,标准表述是:

初始化子类会触发父类初始化。

Class文件等概念

Class文件(.class)

  • (读取成)二进制字节流/字节码(byte[])
  • (ClassLoader defineClass) -> Class对象(java.lang.Class 实例)
  • (JVM 用它)-》才能new、反射、调用方法等。

1. Class文件 (.class)

  • 编译器(javac)把.java 编译后的产物,存在磁盘、jar包中。
  • 包含 常量池、字段表、方法表、属性、以及方法的字节码指令等结构。
    "存储介质上的标准化类表示"

2. 二进制字节流(binary bytes/ byte stream)

  • 把class文件都出来后得到的原始二进制数据
  • 常见形态:byte[]InputStream
    还没解析、只是字节序列

3. 字节码(byteCode)

  • class文件里方法体的那段JVM指令序列(比如aload_0, invokevirtual)
  • 区分:
    • 二进制字节流:外层raw bytes(文件、网络读出来的)
    • 字节码:里面方法Code属性中的JVM指令

JVM能执行、解释、编译的指令级表示

4. ClassLoader(类加载器)

  • 负责把 **"类名 -》字节数据"**变成JVM里的类定义的组件。
  • 关键动作
    • loadClass(name):按规则找到并加载(通常还会走双亲委派)
    • findClass(name):具体去哪找字节(classpath/jar/网络/自定义)
    • defineClass(bytes):把字节数组交给 JVM 定义成类
  • 一句话"搬运 + 定义类的工人/管道"

5.Class对象(Class instance)

  • 代码里能拿到的 java.lang.Class<?> 实例,比如 String.class 的那个对象。
  • 表示什么 :某个类在 JVM 中的运行时类型信息入口(反射、获取方法字段、创建实例等)。
  • 同一个类的唯一性 :在一个 ClassLoader 命名空间里,某个类通常对应 唯一的 Class对象;不同 ClassLoader 加载的"同名类"可以是不同的 Class对象(也就是"类隔离")。
  • 一句话"运行时类型信息的句柄/门把手"
相关推荐
chilavert3183 小时前
技术演进中的开发沉思-316 JVM:指令集(上)
jvm
期待のcode4 小时前
Java虚拟机的垃圾回收器
java·开发语言·jvm·算法
小旭95276 小时前
【Java 面试高频考点】finally 与 return 执行顺序 解析
java·开发语言·jvm·面试·intellij-idea
小白不会Coding6 小时前
一文讲清楚JVM字节码文件的组成
java·jvm·字节码文件
张张努力变强1 天前
C++类和对象(一):inline函数、nullptr、类的定义深度解析
开发语言·前端·jvm·数据结构·c++·算法
韩师学子--小倪1 天前
JVM SafePoint
jvm
BUTCHER51 天前
Java 启动服务时指定JVM(Java 虚拟机)的参数配置说明
java·开发语言·jvm
青槿吖1 天前
Java 集合操作:HashSet、LinkedHashSet 和 TreeSet
java·开发语言·jvm
情缘晓梦.1 天前
C++ 类和对象(完)
开发语言·jvm·c++