面试高频考点:偏向锁为什么要延迟启动?启动期到底哪些对象在抢锁?后来为什么整个偏向锁都被删了?
本文所有结论均附 OpenJDK 源码 / JEP 原文出处,不杜撰。
一、一句话结论
BiasedLockingStartupDelay=4000(默认 4 秒)不是"专门针对类加载器对象"的特殊逻辑,而是一个全局时间开关 :JVM 启动后的前 4 秒内,所有新分配对象的 Mark Word 直接是无锁状态(不允许匿名偏向);4 秒到点后,才对当前已加载的所有类 和未来新分配的对象开放偏向锁能力。
它的唯一目的:规避 JVM 启动阶段大量偏向锁撤销触发的安全点(Safepoint)STW,避免拖慢启动速度。
二、HotSpot 源码原始注释(最权威证据)
文件:hotspot/src/share/vm/runtime/biasedLocking.cpp,BiasedLocking::init() 方法:
cpp
void BiasedLocking::init() {
// If biased locking is enabled, schedule a task to fire a few
// seconds into the run which turns on biased locking for all
// currently loaded classes as well as future ones. This is a
// workaround for startup time regressions due to a large number of
// safepoints being taken during VM startup for bias revocation.
// Ideally we would have a lower cost for individual bias revocation
// and not need a mechanism like this.
if (UseBiasedLocking) {
if (BiasedLockingStartupDelay > 0) {
EnableBiasedLockingTask* task =
new EnableBiasedLockingTask(BiasedLockingStartupDelay);
task->enroll();
} else {
VM_EnableBiasedLocking op(false);
VMThread::execute(&op);
}
}
}
逐行翻译这段注释的关键信息:
| 原文 | 含义 |
|---|---|
workaround for startup time regressions |
这是一个临时补丁,用来解决启动时间回退问题 |
a large number of safepoints being taken during VM startup for bias revocation |
根因是启动阶段大量偏向撤销触发安全点 STW |
Ideally we would have a lower cost for individual bias revocation and not need a mechanism like this |
理想情况下应该降低单次偏向撤销的开销,就不需要这套机制了 |
源码自己都承认这是 workaround,不是什么优雅设计。这是面试时可以直接引用的金句。
4 秒到点时具体做了什么?
cpp
void VM_EnableBiasedLocking::doit() {
// 1. 遍历系统字典中所有已加载的类,把它们的 prototype_header 改为偏向模式
SystemDictionary::classes_do(enable_biased_locking);
// 2. 标记全局开关,未来新分配的对象也走偏向模式
_biased_locking_enabled = true;
}
两个动作:
- 已加载类 :修改类的
prototype_header(类的原型对象头),之后该类 new 出来的新对象会继承匿名偏向的 Mark Word。 - 未来对象:全局开关打开,新分配对象默认带匿名偏向标记。
注意:4 秒前已经分配好的对象,Mark Word 不会被回溯修改,它们终身是无锁状态,不会进入偏向。只有 4 秒之后 new 出来的对象才有资格走偏向锁。
三、启动期到底哪些对象在被多线程抢锁?
3.1 核心判断标准
一个对象会在启动期引发锁竞争,必须同时满足:
- 对象在 JVM 启动极早期被创建(落在 0~4 秒窗口内)
- 是全局单例(所有线程共享同一个对象,不是每个线程各 new 一个)
- 会被多条不同线程交替访问(不是固定一个线程独占)
3.2 典型对象清单
① 各类基础类的 Xxx.class 对象(最典型)
以 java.lang.Integer 为例:
- 谁在抢:主线程、JIT 编译线程、Finalizer 线程、ReferenceHandler 线程,几乎同时第一次访问 Integer
- 抢的是什么 :JVM 规范规定,每个类 C 有一个唯一的初始化锁
LC,HotSpot 的实现就是用Class对象本身当锁 - 为什么必须加锁 :
<clinit>()静态初始化方法全局只能执行一次,多线程同时触发类初始化时,必须用锁互斥
JLS 12.4.2 原文(类初始化详细过程第一步):
"Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC."
伪代码等价:
java
synchronized (Integer.class) { // 多个线程抢同一个 Class 对象锁
if (Integer 已初始化完成) return;
Integer.<clinit>(); // 只跑一次:初始化 IntegerCache 缓存池
标记初始化完成;
}
关键细节 :每个类的 <clinit> 竞争只发生一次(类初始化完成后就不再抢了),但启动阶段有几十上百个 JDK 基础类(Integer、Long、Boolean、Double、String、Math、ArrayList、HashMap......)扎堆并发加载,每个类各自发生一次瞬时竞争,积少成多。
同样的逻辑适用于:Long.class、Boolean.class、String.class、Math.class、ArrayList.class、HashMap.class 等所有启动期被多线程首次访问的类。
② ClassLoadingLock 对象(JDK 7+)
ClassLoader.loadClass() 不再对 this 加锁,而是调用 getClassLoadingLock(className),内部维护一张哈希表:key 是类全限定名,value 是 new Object() 锁对象。
- 谁在抢:多个线程同时加载同一个类名时,拿到同一个锁对象
- 特征:对象在类加载过程中创建,落在启动窗口内
③ JVM 内部全局锁对象(C++ 层,映射到 Java 监视器)
- StringTable / SymbolTable 内部锁:启动期大量字符串常量 intern、符号插入,多线程并发操作哈希表
- 引用队列锁 :
java.lang.ref包静态初始化创建的锁对象,协调应用线程、Finalizer 线程、ReferenceHandler 线程 - SystemDictionary 内部锁:类名到 Class 对象的注册表,多线程并发查找插入
④ 应用代码早期创建的 static 全局锁对象(容易被忽略)
java
public class App {
// 诞生于 0~4 秒窗口,哪怕是业务代码 new 的,同样禁止匿名偏向
public static final Object GLOBAL_LOCK = new Object();
}
机制只看对象分配时间,不看对象属于 JDK 内部还是业务代码。这一点是面试常考的认知误区。
3.3 这些对象的共同特征总结
┌─────────────────────────────────────────────────┐
│ 启动期高竞争锁对象的共同特征 │
├─────────────────────────────────────────────────┤
│ 1. 创建时间极早 → 落在 0~4s 偏向锁关闭窗口内 │
│ 2. 全局单例 → 所有线程共享同一个对象 │
│ 3. 多线程交替 → 不是固定一个线程长期持有 │
│ 4. 线程来源杂 → 主线程 + JIT + 守护线程都来碰 │
└─────────────────────────────────────────────────┘
四、如果没有 4 秒延迟会发生什么?(完整因果链)
假设设置 -XX:BiasedLockingStartupDelay=0,拿 Integer.class 走一遍:
JVM安全点 Integer.class对象 线程B(JIT线程) 线程A(主线程) JVM安全点 Integer.class对象 线程B(JIT线程) 线程A(主线程) #mermaid-svg-kkszQwgYeSkDvFp3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kkszQwgYeSkDvFp3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kkszQwgYeSkDvFp3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kkszQwgYeSkDvFp3 .error-icon{fill:#552222;}#mermaid-svg-kkszQwgYeSkDvFp3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kkszQwgYeSkDvFp3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kkszQwgYeSkDvFp3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kkszQwgYeSkDvFp3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kkszQwgYeSkDvFp3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kkszQwgYeSkDvFp3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kkszQwgYeSkDvFp3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kkszQwgYeSkDvFp3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kkszQwgYeSkDvFp3 .marker.cross{stroke:#333333;}#mermaid-svg-kkszQwgYeSkDvFp3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kkszQwgYeSkDvFp3 p{margin:0;}#mermaid-svg-kkszQwgYeSkDvFp3 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kkszQwgYeSkDvFp3 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-kkszQwgYeSkDvFp3 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kkszQwgYeSkDvFp3 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-kkszQwgYeSkDvFp3 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-kkszQwgYeSkDvFp3 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-kkszQwgYeSkDvFp3 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-kkszQwgYeSkDvFp3 .sequenceNumber{fill:white;}#mermaid-svg-kkszQwgYeSkDvFp3 #sequencenumber{fill:#333;}#mermaid-svg-kkszQwgYeSkDvFp3 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-kkszQwgYeSkDvFp3 .messageText{fill:#333;stroke:none;}#mermaid-svg-kkszQwgYeSkDvFp3 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kkszQwgYeSkDvFp3 .labelText,#mermaid-svg-kkszQwgYeSkDvFp3 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-kkszQwgYeSkDvFp3 .loopText,#mermaid-svg-kkszQwgYeSkDvFp3 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-kkszQwgYeSkDvFp3 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-kkszQwgYeSkDvFp3 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-kkszQwgYeSkDvFp3 .noteText,#mermaid-svg-kkszQwgYeSkDvFp3 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-kkszQwgYeSkDvFp3 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kkszQwgYeSkDvFp3 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kkszQwgYeSkDvFp3 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-kkszQwgYeSkDvFp3 .actorPopupMenu{position:absolute;}#mermaid-svg-kkszQwgYeSkDvFp3 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-kkszQwgYeSkDvFp3 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-kkszQwgYeSkDvFp3 .actor-man circle,#mermaid-svg-kkszQwgYeSkDvFp3 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-kkszQwgYeSkDvFp3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 对象分配,Mark Word = 匿名偏向(101) Mark Word = 已偏向A(101+线程A指针) 发现锁已偏向A,A已退出同步块 Mark Word 退回无锁(001) 抢锁,CAS成功,对象偏向线程A执行<clinit>,释放锁来抢同一把锁触发全局安全点STW,暂停所有Java线程遍历线程栈,撤销偏向标记升级轻量级锁CAS抢轻量级锁成功
每一次"别的线程来抢一个已被偏向的对象",都要走一次全局安全点 STW。启动期几十个类各来一次,STW 次数爆炸,启动时间显著拉长。
这就是源码注释说的 "a large number of safepoints being taken during VM startup for bias revocation"。
五、4 秒延迟的本质:不是识别对象,是按时间一刀切
#mermaid-svg-TucgDjx8fBn9u0Pn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TucgDjx8fBn9u0Pn .error-icon{fill:#552222;}#mermaid-svg-TucgDjx8fBn9u0Pn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TucgDjx8fBn9u0Pn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TucgDjx8fBn9u0Pn .marker.cross{stroke:#333333;}#mermaid-svg-TucgDjx8fBn9u0Pn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TucgDjx8fBn9u0Pn p{margin:0;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge{stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .section--1 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section--1 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section--1 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section--1 text{fill:#ffffff;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth--1{stroke-width:17;}#mermaid-svg-TucgDjx8fBn9u0Pn .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:#ffffff;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-0 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-0 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-0 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-0 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-0{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-0{stroke-width:14;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-1 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-1 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-1 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-1 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-1{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-1{stroke-width:11;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-2 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-2 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-2 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-2 text{fill:#ffffff;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-2{stroke-width:8;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:#ffffff;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-3 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-3 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-3 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-3 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-3{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-3{stroke-width:5;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-4 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-4 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-4 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-4 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-4{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-4{stroke-width:2;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-5 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-5 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-5 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-5 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-5{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-5{stroke-width:-1;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-6 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-6 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-6 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-6 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-6{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-6{stroke-width:-4;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-7 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-7 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-7 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-7 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-7{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-7{stroke-width:-7;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-8 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-8 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-8 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-8 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-8{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-8{stroke-width:-10;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-9 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-9 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-9 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-9 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-9{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-9{stroke-width:-13;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-10 rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-10 path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-10 circle,#mermaid-svg-TucgDjx8fBn9u0Pn .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-10 text{fill:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .node-icon-10{font-size:40px;color:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .edge-depth-10{stroke-width:-16;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-TucgDjx8fBn9u0Pn .lineWrapper line{stroke:black;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled circle,#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:lightgray;}#mermaid-svg-TucgDjx8fBn9u0Pn .disabled text{fill:#efefef;}#mermaid-svg-TucgDjx8fBn9u0Pn .section-root rect,#mermaid-svg-TucgDjx8fBn9u0Pn .section-root path,#mermaid-svg-TucgDjx8fBn9u0Pn .section-root circle{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-TucgDjx8fBn9u0Pn .section-root text{fill:#ffffff;}#mermaid-svg-TucgDjx8fBn9u0Pn .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-TucgDjx8fBn9u0Pn .edge{fill:none;}#mermaid-svg-TucgDjx8fBn9u0Pn .eventWrapper{filter:brightness(120%);}#mermaid-svg-TucgDjx8fBn9u0Pn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 0s JVM启动 新对象 Mark Word =无锁(001) 禁止匿名偏向 4s EnableBiasedLockingTask触发 已加载类的prototype_header改为偏向模式 全局开关_biased_locking_enabled= true >4s 新对象 Mark Word =匿名偏向(101) 允许走偏向锁逻辑 对象分配时间 vs 偏向锁能力
两个常见误区:
| 误区 | 真相 |
|---|---|
| 4 秒后,老对象也会自动变成可偏向 | ❌ 不会。只有 4 秒后 new 出来的对象才有匿名偏向标记,老对象终身无锁 |
| 4 秒延迟是专门识别、过滤 JVM 内部对象 | ❌ 没有特殊识别逻辑,纯粹按对象分配时间一刀切。业务代码在启动前 4 秒 new 的对象同样不能偏向 |
六、后来为什么把整个偏向锁都删了?(JEP-374)
6.1 版本时间线
| 版本 | 事件 | 出处 |
|---|---|---|
| JDK 6 | 引入偏向锁,默认开启 | --- |
| JDK 15(2020.09) | 默认关闭偏向锁,废弃所有相关参数 | JEP-374 |
| JDK 18 | 彻底删除偏向锁代码,参数不再识别(obsolete) | JDK-8256425 |
原本计划 JDK 16 删除,后延期到 JDK 18(JDK-8256253)。
被废弃的参数清单(JEP-374 原文列出):
- Product:
BiasedLockingStartupDelay、BiasedLockingBulkRebiasThreshold、BiasedLockingBulkRevokeThreshold、BiasedLockingDecayTime、UseOptoBiasInlining - Diagnostic:
PrintBiasedLockingStatistics、PrintPreciseBiasedLockingStatistics
6.2 废弃的四大原因(JEP-374 Motivation 原文归纳)
① 硬件变化:CAS 原子指令成本大幅下降
JEP 原文:"The cost of executing atomic instructions has decreased on modern processors since the introduction of biased locking into HotSpot."
偏向锁诞生的初衷是省去 CAS 原子操作。当年 CAS 很贵,今天 CAS 已经很便宜,偏向锁省下来的那点开销微不足道。
② 受益场景萎缩:只有老应用还在吃红利
JEP 原文:"Many applications that benefited from biased locking are older, legacy applications that use the early Java collection APIs, which synchronize on every access (e.g., Hashtable and Vector)."
- 老应用:
Hashtable、Vector------每次访问都 synchronized,单线程场景下偏向锁收益大 - 现代应用:
HashMap、ArrayList(Java 1.2 引入,单线程无锁)或ConcurrentHashMap(Java 5 引入,高并发),本身就减少了不必要的 synchronized
③ 线程池架构的应用,关闭偏向锁反而更快
JEP 原文:"applications built around a thread-pool queue and worker threads generally perform better with biased locking disabled. (SPECjbb2015 was designed that way)"
现代微服务基本都是"线程池 + Worker 线程"架构。同一个锁对象在不同 Worker 线程之间频繁换手,一旦竞争就触发偏向撤销 STW,P99 延迟恶化。SPECjbb2015 基准测试就是关闭偏向锁后性能更好。
④ 维护成本太高,阻碍 JVM 演进
JEP 原文:"Biased locking introduced a lot of complex code into the synchronization subsystem and is invasive to other HotSpot components as well. This complexity is a barrier to understanding various parts of the code and an impediment to making significant design changes."
偏向锁的状态机极其复杂:匿名偏向、已偏向、批量重偏向(bulk rebias)、批量撤销(bulk revoke)、epoch 机制、线程死亡处理......代码侵入线程、GC、解释器、JIT 各个模块。Project Loom(虚拟线程)要改造同步子系统时,偏向锁是巨大障碍。
6.3 关键澄清:不是启动期锁竞争被解决了
误区:JDK 优化了,启动期不再多线程初始化基础类,所以不需要 4 秒延迟了。
真相 :类加载并发逻辑、
<clinit>锁机制几乎没有改动。启动期Integer.class等对象依旧会被多线程争抢,只是现在直接走轻量级锁 CAS,不再有"偏向→撤销→STW"这条路径,自然也就不需要 4 秒这个补丁了。
七、全链路因果图(面试默写版)
#mermaid-svg-Y2ibSJGbjSjw8GRS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Y2ibSJGbjSjw8GRS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Y2ibSJGbjSjw8GRS .error-icon{fill:#552222;}#mermaid-svg-Y2ibSJGbjSjw8GRS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Y2ibSJGbjSjw8GRS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Y2ibSJGbjSjw8GRS .marker.cross{stroke:#333333;}#mermaid-svg-Y2ibSJGbjSjw8GRS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Y2ibSJGbjSjw8GRS p{margin:0;}#mermaid-svg-Y2ibSJGbjSjw8GRS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Y2ibSJGbjSjw8GRS .cluster-label text{fill:#333;}#mermaid-svg-Y2ibSJGbjSjw8GRS .cluster-label span{color:#333;}#mermaid-svg-Y2ibSJGbjSjw8GRS .cluster-label span p{background-color:transparent;}#mermaid-svg-Y2ibSJGbjSjw8GRS .label text,#mermaid-svg-Y2ibSJGbjSjw8GRS span{fill:#333;color:#333;}#mermaid-svg-Y2ibSJGbjSjw8GRS .node rect,#mermaid-svg-Y2ibSJGbjSjw8GRS .node circle,#mermaid-svg-Y2ibSJGbjSjw8GRS .node ellipse,#mermaid-svg-Y2ibSJGbjSjw8GRS .node polygon,#mermaid-svg-Y2ibSJGbjSjw8GRS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Y2ibSJGbjSjw8GRS .rough-node .label text,#mermaid-svg-Y2ibSJGbjSjw8GRS .node .label text,#mermaid-svg-Y2ibSJGbjSjw8GRS .image-shape .label,#mermaid-svg-Y2ibSJGbjSjw8GRS .icon-shape .label{text-anchor:middle;}#mermaid-svg-Y2ibSJGbjSjw8GRS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Y2ibSJGbjSjw8GRS .rough-node .label,#mermaid-svg-Y2ibSJGbjSjw8GRS .node .label,#mermaid-svg-Y2ibSJGbjSjw8GRS .image-shape .label,#mermaid-svg-Y2ibSJGbjSjw8GRS .icon-shape .label{text-align:center;}#mermaid-svg-Y2ibSJGbjSjw8GRS .node.clickable{cursor:pointer;}#mermaid-svg-Y2ibSJGbjSjw8GRS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Y2ibSJGbjSjw8GRS .arrowheadPath{fill:#333333;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Y2ibSJGbjSjw8GRS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y2ibSJGbjSjw8GRS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Y2ibSJGbjSjw8GRS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y2ibSJGbjSjw8GRS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Y2ibSJGbjSjw8GRS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Y2ibSJGbjSjw8GRS .cluster text{fill:#333;}#mermaid-svg-Y2ibSJGbjSjw8GRS .cluster span{color:#333;}#mermaid-svg-Y2ibSJGbjSjw8GRS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Y2ibSJGbjSjw8GRS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Y2ibSJGbjSjw8GRS rect.text{fill:none;stroke-width:0;}#mermaid-svg-Y2ibSJGbjSjw8GRS .icon-shape,#mermaid-svg-Y2ibSJGbjSjw8GRS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Y2ibSJGbjSjw8GRS .icon-shape p,#mermaid-svg-Y2ibSJGbjSjw8GRS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Y2ibSJGbjSjw8GRS .icon-shape .label rect,#mermaid-svg-Y2ibSJGbjSjw8GRS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Y2ibSJGbjSjw8GRS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Y2ibSJGbjSjw8GRS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Y2ibSJGbjSjw8GRS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} JDK6 引入偏向锁
目的: 单线程反复拿锁时省去CAS
问题: 别的线程来抢已偏向对象时
必须触发全局安全点STW做撤销
JVM启动期: 几十上百个基础类
被主线程+JIT+守护线程并发初始化
每个类的Xxx.class都是全局单例锁对象
多线程争抢 → 大量偏向撤销 → 大量STW
启动时间严重回退
打补丁: BiasedLockingStartupDelay=4000
前4秒新对象禁止匿名偏向
4秒后: 已加载类prototype_header改偏向模式
新对象允许匿名偏向
时代变化
硬件: CAS变便宜了
应用: 线程池架构, 锁频繁换手
偏向撤销STW反而恶化P99
代码: 状态机太复杂, 阻碍虚拟线程等新特性
JEP-374: JDK15默认关闭
JDK18: 彻底删除代码, 参数obsolete
4秒延迟随偏向锁一起消失
启动期竞争直接走轻量级锁CAS
八、面试高频 Q&A
Q1:偏向锁为什么要延迟 4 秒启动?
答 :不是为了等什么,而是为了避开 JVM 启动阶段的高竞争期 。启动期大量基础类的 Class 对象、ClassLoadingLock、JVM 内部全局锁对象会被主线程、JIT 线程、守护线程并发争抢。如果一启动就开启偏向锁,这些对象会频繁经历"偏向→被别的线程抢→安全点 STW 撤销"的循环,大量 STW 拖慢启动。所以前 4 秒新对象直接无锁,4 秒后启动期高竞争基本结束,再开放偏向锁。
Q2:4 秒延迟是专门针对类加载器对象吗?
答 :不是。它是按对象分配时间的全局一刀切开关,不识别对象类型。类加载器相关锁对象确实是启动期高竞争对象的典型代表,但业务代码在前 4 秒 new 的对象同样被禁止偏向。
Q3:4 秒后,之前创建的对象会变成可偏向吗?
答 :不会。4 秒到点时只做两件事:修改已加载类的 prototype_header、打开全局开关。已经分配好的对象 Mark Word 不会被回溯修改,它们终身是无锁状态。只有 4 秒之后 new 出来的对象才有匿名偏向标记。
Q4:偏向锁撤销为什么需要安全点 STW?
答:撤销偏向时需要遍历所有线程的栈,找到并修改那些正在使用该偏向锁的栈帧中的锁记录(确保递归锁等场景的一致性)。遍历线程栈必须在所有线程都暂停的安全点才能安全进行,所以是全局 STW。这也是偏向锁撤销代价高的根本原因。
Q5:JDK 15 之后偏向锁怎么了?
答 :JDK 15 通过 JEP-374 默认关闭偏向锁并废弃相关参数;JDK 18 彻底删除偏向锁代码,UseBiasedLocking、BiasedLockingStartupDelay 等参数不再被识别。原因是现代 CPU 的 CAS 开销降低、线程池架构下偏向锁反而制造延迟抖动、代码复杂度阻碍虚拟线程等新特性演进。
Q6:启动期 Integer.class 的锁竞争会持续很久吗?
答 :不会。每个类的 <clinit> 只执行一次,初始化完成后其他线程直接返回,不再争抢。但启动期有几十上百个基础类扎堆并发初始化,每个类瞬时竞争一次,累积起来的偏向撤销 STW 次数就很可观了。
九、参考来源
- OpenJDK 源码:
hotspot/src/share/vm/runtime/biasedLocking.cpp---BiasedLocking::init()原始注释 - JEP 374: Deprecate and Disable Biased Locking --- http://openjdk.org/jeps/374
- JDK-8256425: Obsolete Biased Locking in JDK 18 --- https://bugs.openjdk.org/browse/JDK-8256425
- JLS 12.4.2 Detailed Initialization Procedure --- 类初始化锁
LC规范 - OpenJDK Wiki: Biased-locking Synchronization ---
BiasedLockingStartupDelay参数说明