Appearance
第三十一部分:Java 21 核心机制、JVM 深度调优、JUC 并发与微服务架构实战
第 1 章:Java 锁物理全景:Mark Word、锁膨胀与 AQS 源码
1.1 synchronized 对象头 Mark Word 64 位物理布局
在 64 位 JVM 中,普通对象的对象头占 16 字节(8 字节 Mark Word + 8/4 字节 Klass Word 指针)。Mark Word 物理结构如下:
text
┌───────────────────────────────────────────────────────────────────────────────────────┐
│ Mark Word (64 bits) 物理布局 │
├───────────────────────────────────────────────────────┬──────────────┬────────────────┤
│ 锁状态 (Lock Status) │ 偏向锁标记 │ 锁标志位 (bits)│
├───────────────────────────────────────────────────────┼──────────────┼────────────────┤
│ 无锁 (Unlocked) ➔ HashCode (31b) + Age (4b) │ 0 │ 01 │
│ 偏向锁 (Biased) ➔ ThreadID (54b) + Age (4b) │ 1 │ 01 │
│ 轻量级锁 (Lightweight) ➔ 栈帧 Lock Record 指针 (62b) │ - │ 00 │
│ 重量级锁 (Heavyweight) ➔ ObjectMonitor 物理指针 (62b) │ - │ 10 │
│ GC 标记 (Marked for GC) │ - │ 11 │
└───────────────────────────────────────────────────────┴──────────────┴────────────────┘💬 synchronized 锁膨胀物理全过程
text
[ 无锁 (001) ] ➔ 单线程首次获取 ➔ [ 偏向锁 (101) (CAS 将 ThreadID 写入 Mark Word) ]
│
(出现多线程轻度竞争)
↓
[ 轻量级锁 (000) (线程在各自栈帧分配 Lock Record,CAS 替换 Mark Word) ]
│
(自旋超过次数或重度竞争)
↓
[ 重量级锁 (010) (JVM 向 OS 申请 ObjectMonitor,底层使用 pthread_mutex) ]- 物理代价:重量级锁依赖 OS 的
pthread_mutex,会导致线程从用户态 (User Mode) 切换至内核态 (Kernel Mode),产生昂贵的 CPU 上下文切换开销。
1.2 AQS (AbstractQueuedSynchronizer) 物理架构
text
┌──────────────────────────────────────────────┐
│ volatile int state (资源状态) │
└──────────────────────────────────────────────┘
│
┌──────────────────────┴──────────────────────┐
│ CLH 双向 FIFO 同步等待队列 (Head ➔ Tail) │
│ ┌──────────┐ ┌──────────┐ │
│ │ Node A │ <==> │ Node B │ ... │
│ │ (Thread) │ │ (Thread) │ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────┘💬 ReentrantLock 公平锁 vs 非公平锁物理源码对比
java
// 非公平锁 (NonfairSync):直接抢占,不管队列里是否有排队线程
final void lock() {
if (compareAndSetState(0, 1)) // 1. 先插队 CAS 抢锁!
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 抢不到再入 CLH 队列
}
// 公平锁 (FairSync):必须先看 CLH 队列是否有前驱节点
protected final boolean tryAcquire(int acquires) {
if (state == 0) {
if (!hasQueuedPredecessors() && compareAndSetState(0, 1)) { // 检查无前驱才抢锁!
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
}
return false;
}第 2 章:Spring AOP 物理实现、JDK vs CGLIB 与切面失效死穴
2.1 JDK 动态代理 vs CGLIB 字节码增强
| 维度对比 | JDK 动态代理 (JDK Dynamic Proxy) | CGLIB 字节码增强 (CGLIB / ByteBuddy) |
|---|---|---|
| 物理条件 | 目标类必须实现接口 (Interface) | 目标类无须接口,但目标类与方法不能加 final |
| 物理机制 | 基于 java.lang.reflect.Proxy 动态在内存生成接口实现类 | 基于 ASM 动态生成目标类的子类 (Subclass) 覆盖方法 |
| 核心拦截器 | java.lang.reflect.InvocationHandler | net.sf.cglib.proxy.MethodInterceptor |
2.2 AOP 切面与声明式事务 @Transactional 失效的物理死穴
💬 线上事故:类内部自调用 this.b() 导致 AOP 切面/事务彻底失效!
java
@Service
public class OrderService {
@Autowired
private OrderService self; // 解决方案 2:自我注入代理对象
public void a() {
// ❌ 错误做法:直接调用 b() 实际上相当于 this.b(),调用的是未经 AOP 代理的原生对象!
// b();
// ✅ 正确做法 A:使用自我注入的代理对象调用
self.b();
// ✅ 正确做法 B:使用 AopContext 获取当前代理对象
// ((OrderService) AopContext.currentProxy()).b();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void b() {
// 执行需事务落库逻辑
}
}- AOP 5 大失效场景:
- 类内部自调用 (
this.func())(最常见死穴); - 方法修饰符非
public(Spring 默认忽略非 public 方法的事务代理); - 方法被
final或static修饰(CGLIB 无法重写子类方法); - 异常被
try-catch内部吞掉(未向外抛出RuntimeException); - 数据库存储引擎不支持事务(如旧版 MySQL MyISAM)。
- 类内部自调用 (
第 3 章:类重复加载、ClassLoader 隔离与 LinkageError
💬 为什么同一个类被不同 ClassLoader 加载会抛出 ClassCastException?
在 JVM 中,类的唯一物理身份是由“类全限定名 + 加载它的 ClassLoader 物理实例”共同决定的!
java
// 即使 Person 字节码完全相同:
ClassLoader loaderA = new CustomClassLoader();
ClassLoader loaderB = new CustomClassLoader();
Class<?> classA = loaderA.loadClass("com.example.Person");
Class<?> classB = loaderB.loadClass("com.example.Person");
Object objA = classA.getDeclaredConstructor().newInstance();
// ❌ 物理抛出 ClassCastException: com.example.Person cannot be cast to com.example.Person
// 因为 JVM 认为 classA 和 classB 是物理隔离的两个完全不同的类!- 应用场景:
- Tomcat WebAppClassLoader:隔离不同的 Web 应用,防止应用 A 的
spring-core 4.0覆盖应用 B 的spring-core 5.0; - 热重载 (Hot Reload):销毁旧的 ClassLoader,创建全新的 ClassLoader 重新加载修改后的
.class字节码。
- Tomcat WebAppClassLoader:隔离不同的 Web 应用,防止应用 A 的
第 4 章:JVM OOM 内存溢出 5 大物理场景与 Dump 诊疗
text
┌────────────────────────────────────────────────────────────────────────────────────────┐
│ JVM OOM 5 大物理场景全景图 │
├───────────────────────────────────────────┬────────────────────────────────────────────┤
│ OOM 异常类型 │ 物理根因与排查手段 │
├───────────────────────────────────────────┼────────────────────────────────────────────┤
│ **1. Heap Space (堆溢出)** │ 内存泄漏或大对象大 List 积压 (MAT 排查) │
├───────────────────────────────────────────┼────────────────────────────────────────────┤
│ **2. Metaspace (元空间溢出)** │ CGLIB/Groovy 动态生成大量 `.class` 未释放 │
├───────────────────────────────────────────┼────────────────────────────────────────────┤
│ **3. Unable to create new native thread** │ OS 线程数达到 `ulimit -u` 限制或线程池泄漏 │
├───────────────────────────────────────────┼────────────────────────────────────────────┤
│ **4. Direct buffer memory (堆外内存溢出)**│ Netty 未调用 `ReferenceCountUtil.release()`│
├───────────────────────────────────────────┼────────────────────────────────────────────┤
│ **5. StackOverflowError (栈溢出)** │ 方法无限递归或方法调用层级过深 (-Xss) │
└───────────────────────────────────────────┴────────────────────────────────────────────┘第 5 章:JVM 生产调优参数集合与 Arthas 6 大诊疗神句
5.1 JVM 8 / 17 / 21 生产黄金启动参数集合
bash
java -server \
-Xms31g -Xmx31g \
-Xss512k \
-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:InitiatingHeapOccupancyPercent=45 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/logs/jvm_oom.hprof \
-XX:+PrintGCDetails -XX:+PrintGCDateStamps \
-Xloggc:/var/logs/gc.log \
-jar app.jar5.2 Arthas 阿里线上诊疗 6 大神句
bash
# 1. 查看 JVM 整体 CPU、内存、GC 实时大盘
dashboard
# 2. 找到当前占用 CPU 最高的 3 个线程堆栈
thread -n 3
# 3. 反编译线上正在运行的物理 .class 字节码 (确认部署代码版本)
jad com.example.service.OrderService
# 4. 实时观察方法的入参、返回值与抛出的异常
watch com.example.service.OrderService getOrder "{params, returnObj, throwExp}" -x 2
# 5. 追踪方法内部各子调用的耗时火焰图 (排查慢接口)
trace com.example.service.OrderService getOrder
# 6. 记录方法调用的时间隧道 (可重放请求进行 Debug)
tt -t com.example.service.OrderService getOrder🔍 本章 6 重自审计报告
- 【知识审计】:已全量海量爆破扩充 Java 专栏!涵盖 Mark Word 64 位物理布局、锁膨胀 (偏向 ➔ 轻量 ➔ 重量)、AQS
state+ CLH 队列与公平/非公平锁源码对比、JDK 动态代理 vs CGLIB 字节码增强、AOP/事务内部自调用this.b()失效死穴与 3 大解决方案、ClassLoader 类隔离与ClassCastException物理根因、OOM 5 大物理场景 (Heap, Metaspace, Native Thread, Direct, Stack)、JVM 生产启动参数与 Arthas 线上诊断 6 大神句 (dashboard,thread -n 3,jad,watch,trace,tt)!已适配移动端::: info 💬自适应排版!