知其然,知其所以然
Java 内部原理 2026-10-02

synchronized:从对象锁到字节码监视器

javap 反汇编验证 synchronized 块是 monitorenter + 两次 monitorexit(正常与异常路径)、synchronized 方法只带 ACC_SYNCHRONIZED 标志;用 Unsafe 直接读对象头,在 JDK 17、21、27 上观察无锁、轻量级锁、重量级锁(wait 触发膨胀)的 mark word;块里抛异常时锁被释放。

Java synchronized 字节码 对象头 真实验证 动画演示
Java 内部原理 2026-10-02

AQS:ReentrantLock 是怎么排队和唤醒线程的

用反射直接读 AbstractQueuedSynchronizer 的 state 与等待队列(哨兵 → T1 → T2 → T3),等待线程 WAITING 并 park 在 AQS.acquire;排队线程先进先出(公平/非公平各 30/30);非公平 tryLock() 插队 31 次、公平 tryLock() 也能插队 7 次、tryLock(0, SECONDS) 只有 1 次。

Java AQS ReentrantLock 并发 真实验证 动画演示
Java 内部原理 2026-10-02

线程池:execute() 如何决定进队列还是创建线程

真实运行 ThreadPoolExecutor:execute 的决策顺序是核心线程 → 队列 → 非核心线程 → 拒绝;无界队列时 max 永远用不上;非核心线程空闲 keepAlive 后回收;execute 抛异常会换线程,submit 抛异常被存进 Future 而悄悄消失。

Java 线程池 ThreadPoolExecutor 并发 真实验证 动画演示
Java 内部原理 2026-10-02

垃圾回收:对象什么时候进入老年代

用 Serial GC 和 -Xlog:gc+age 真实观察对象何时进入老年代:保留对象在幸存区里 age 1→2→3,GC #4 晋升;幸存区装不下时有效阈值自动降为 1,对象提前晋升;PretenureSizeThreshold 让 2 MB 数组一出生就在老年代;幸存区太小时 3 MB 对象第一次 GC 就进了老年代。

Java 垃圾回收 分代 晋升 Serial GC 真实验证 动画演示
Java 内部原理 2026-10-02

G1 垃圾收集器:Region、记忆集和停顿目标

用 JDK 21 的 G1 日志实测:Region 大小随 -Xmx 变成 1M / 4M / 16M;MaxGCPauseMillis 从 50 ms 降到 5 ms,Eden 从 51.7 MB 缩到 22.2 MB、年轻代 GC 次数从 711 涨到 1692;一个完整并发标记周期的停顿序列;老年代大数组指向新对象时,年轻代停顿中位数从 0.65 ms 涨到 5.59 ms。

Java G1 垃圾回收 Region 记忆集 真实验证 动画演示
Java 内部原理 2026-10-02

JIT 编译:热点代码如何变成本地机器码

用 -XX:+PrintCompilation 与同一个 stdout 里的计时,验证一个方法先被解释执行(300 ns/次)、再被 C1 编译(约 57 ns/次)、最后被 C2 编译(8.7 ns/次);四种运行方式 -Xint / 只 C1 / 默认分层 / 只 C2 的 300 万次调用耗时分别是 554 / 31 / 23 / 24 ms。

Java JIT C1 C2 PrintCompilation 真实验证 动画演示
Java 内部原理 2026-10-02

Java NIO:Selector 如何同时管理成千上万个连接

真实运行:1 个线程的 Selector 服务端处理 3000 个保持打开的连接用 134 ms、Java 线程数 7、RSS 53 MB;每连接一线程的版本创建 3000 个线程、RSS 287 MB;select 水平触发下不读数据或常开 OP_WRITE 会让 selectNow 在 200 ms 内空转 60 多万次。

Java NIO Selector 网络 真实验证 动画演示
Java 内部原理 2026-10-02

Netty 粘包拆包:TCP 为什么没有消息边界

真实验证 TCP 没有消息边界:50 字节 × 2000 次写入被合并成 855 次 read(平均 116 字节),20000 字节的消息被拆成不整的读取 34 次;Netty 不加解码器时 2000 条消息只触发 50 次 channelRead,加 LengthFieldBasedFrameDecoder / LineBasedFrameDecoder / FixedLengthFrameDecoder 后恰好 2000 次。

Java Netty TCP 粘包拆包 真实验证 动画演示
Java 内部原理 2026-10-02

Java 序列化:一个对象如何变成字节流

真实验证 Java 序列化:71 字节的流里只有 7 字节数据,其余是魔数与类描述;transient/static 不进流、字段初始化器不运行、不可序列化父类的无参构造器被调用;共享引用被保留;没有显式 serialVersionUID 时加一个字段就让旧数据读取失败,显式声明后读取成功但新字段是 null。

Java 序列化 Serializable serialVersionUID 真实验证 动画演示
Java 内部原理 2026-10-02

Lambda:invokedynamic 如何生成函数对象

javap 与运行时验证 lambda 的实现:lambda 没有类文件、函数体变成私有静态合成方法加一条 invokedynamic(引导方法 LambdaMetafactory.metafactory);不捕获变量的 lambda 是同一个对象、捕获变量的每次求值新建;运行时类是隐藏类 Main$$Lambda/0x…;第一个 lambda 位置首次求值约 3 ms,多加载 90 个类。

Java Lambda invokedynamic 字节码 真实验证 动画演示
Java 内部原理 2026-10-02

Stream:惰性求值、流水线和短路操作

用带日志的流水线验证 Stream 的执行方式:没有终结操作什么都不发生;每个元素依次走完整条流水线;sorted() 是屏障;limit/findFirst/anyMatch 让无限流或百万元素流只处理很少的元素;limit 的位置改变上游产生的元素数;大小已知时 count() 会跳过 peek 与 map(JDK 17、21、27 一致)。

Java Stream 惰性求值 短路 真实验证 动画演示
Java 内部原理 2026-10-02

StampedLock:乐观读到底是不是真正无锁

真实验证 StampedLock:乐观读只拿版本号、写锁期间与写锁之后旧 stamp 都失效;验证之前确实会读到 x+y≠100 的数据(2417 次),validate 通过后 0 次;8 个读线程下乐观读 2756 百万次/秒,读锁只有 1.9~2.4;不可重入。

Java StampedLock 乐观读 并发 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号