连载中 4/20

类加载机制:Class 文件是怎么变成可用对象的

2026-09-12 · 175 阅读 · 0 评论 · 0 赞

Class 文件长什么样

编译器把 .java 翻译成 .class,一个严格遵守格式的二进制文件:开头四个字节是魔数 0xCAFEBABE(JVM 认这个口令才往下读),接着是版本号、常量池、字段表、方法表……JVM 对来源不挑:磁盘文件、网络字节流、JAR 包、运行时动态生成(动态代理)都可以——只要格式对,从哪来不重要。这份宽容是 Java 生态繁荣的底座,也是字节码注入这类攻击的入口。

五步流水线

加载:读入字节流,在方法区生成类的运行时数据,堆里生成对应的 Class 对象。验证:魔数、格式、语义、字节码的安全性逐层检查,防止恶意字节码搞事。准备:给静态变量分配内存并设零值——注意 static int value = 10 在这一步是 0 不是 10,赋 10 的动作发生在初始化阶段;但 static final int VALUE = 10 是例外,final 常量在准备阶段就直接赋值(ConstantValue 属性)。解析:把常量池里的符号引用换成直接引用。初始化:执行 <clinit>——编译器把静态变量赋值语句和 static 块按源码顺序拼成的方法。

public class Config {
    static Config INSTANCE = new Config();     // ① 先 new,此时 version=0
    static int version = 1;                    // ② 再赋值,version=1

    private Config() {
        System.out.println("ctor sees version = " + version);  // 输出 0!
    }
}
// 构造函数在"初始化"进行到一半时被调用——字段顺序换了结果就换

初始化的触发时机

初始化是"惰性"的,只有六种主动引用会触发:new、读写静态字段(非 final)、调用静态方法、反射调用、初始化子类时连带父类、main 所在的主类。对应地有被动引用:通过子类引用父类的静态字段(只初始化父类)、定义数组 User[] arr(不初始化 User)、引用编译期常量(已进常量池)。最经典的坑:把配置类当成"用就被加载",实际靠常量引用根本没触发初始化,排查半天发现逻辑压根没跑。

clinit 自带线程安全

// 静态内部类单例:为什么天生线程安全且懒加载
public class Holder {
    private Holder() {}
    private static class Inner {
        static final Holder INSTANCE = new Holder();
    }
    public static Holder getInstance() {
        return Inner.INSTANCE;   // 第一次访问才触发 Inner 的初始化
    }
}
// JVM 保证 <clinit> 只执行一次且全程持锁——
// 等于用类加载机制免费获得了双重检查锁的效果

这个保证也有一体两面:如果某个类的 <clinit> 里写了耗时逻辑,第一个触发它的线程会把锁一直占着,其他线程全部阻塞——"静态块卡死全线程"是真实存在的事故形态

一道顺序题收尾

面试常客:父类静态块、子类静态块、父类构造、子类构造的执行顺序?答案是静态先行(父先子后)、构造随后(父先子后)——因为子类初始化前必须先完成父类初始化,而 new 子类时构造函数第一行永远先调 super()。理解了五步流水线,这类题不用背。下一篇看加载这道工序的执行者——类加载器,以及那个著名的双亲委派模型。

503

10 年全栈工程师 · 503咖啡馆主理人

#类加载机制#clinit#静态变量#初始化顺序#静态内部类单例

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 4 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞