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

Java 模块系统:module-info.java 如何限制访问

用 lib、app、other 三个真实模块验证模块封装:未导出的包编译期就 not visible;exports … to 只对指定模块开放;反射访问未导出的包抛 IllegalAccessException,opens 的包可以 setAccessible;--add-opens 可临时打开;JDK 的 java.base 有 54 个无限制导出包、67 个限定导出包。

Java 模块系统 module-info exports opens 真实验证 动画演示
Java 内部原理 2026-10-02

字节码:class 文件常量池到底保存了什么

用 javap -v 验证 class 文件:魔数 cafebabe、JDK 21 的 major 65;一个只有 main 的类常量池有 84 项(Utf8 42 项为主);重复字面量只存一份、小整数不进常量池;static final 常量被编译进使用方,只重编译常量所在的类时使用方仍是旧值;字符串常量超过 65535 字节 javac 报错。

Java 字节码 常量池 javap class 文件 真实验证 动画演示
Java 内部原理 2026-10-02

invokedynamic:动态调用点如何被 JVM 重写

用 ASM 生成带自定义 bootstrap 的 invokedynamic:每个调用点只在第一次执行时链接;MutableCallSite 的 setTarget 换目标而不重新链接;bootstrap 抛异常得到 BootstrapMethodError 且失败也被缓存;javap 看调用点与 BootstrapMethods 在类文件里的结构。

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

Semaphore:许可证数量如何控制并发

真实验证 Semaphore:许可数 1/3/6 时 12 个任务的最大并发是 1/3/6、耗时 1279/433/218 ms;release 不检查上限(Semaphore(2) release 3 次变成 5);没有所有者,别的线程可以 release;Semaphore(0) 可当信号;排队线程先进先出。

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

BlockingQueue:生产者消费者如何阻塞与唤醒

真实验证 BlockingQueue:满队列上 offer 返回 false、add 抛 IllegalStateException、put 阻塞(线程 WAITING,park 在 AQS 条件队列里);空队列上 poll 返回 null、remove 抛 NoSuchElementException、take 阻塞;五种队列的吞吐(SynchronousQueue 只有约 0.41 百万次/秒);毒丸、drainTo、PriorityBlockingQueue。

Java BlockingQueue 生产者消费者 并发 真实验证 动画演示
Java 内部原理 2026-10-02

ConcurrentLinkedQueue:无锁队列如何入队出队

真实验证 ConcurrentLinkedQueue:迭代器弱一致、不允许 null、不阻塞;size() 是遍历(1,000 个 2,099 ns → 1,000,000 个 2,648,479 ns)而 isEmpty() 不是;每个生产者内部保持先进先出;在高度竞争、近乎空的队列上吞吐随线程数从 88.4 降到 8.7 百万次/秒,低于同负载的 LinkedBlockingQueue。

Java ConcurrentLinkedQueue 无锁队列 并发 真实验证 动画演示
Java 内部原理 2026-10-02

IdentityHashMap:为什么它使用 == 而不是 equals

真实验证 IdentityHashMap:内容相同的两个 String 是两个键(HashMap 只有一个),装箱 Integer 100 同一个对象、1000 不同;内部是键值相邻的 Object[],没有 Node;键是 100 元素 List 时比 HashMap 快 2.8 倍;对重写了 equals 的节点按身份去重得到 3 个而不是 2 个。

Java IdentityHashMap HashMap equals 真实验证 动画演示
Java 内部原理 2026-10-02

BigDecimal:金额计算为什么会出现 0.1 + 0.2

真实验证 BigDecimal:double 的 0.1 + 0.2 = 0.30000000000000004、new BigDecimal(0.1) 展开二进制误差;BigDecimal 是无标度整数 + scale,加减乘除的 scale 规则,除不尽抛 ArithmeticException;equals 比较 scale 所以 2.0 ≠ 2.00(HashSet 找不到);七种舍入模式对照与舍入时机。

Java BigDecimal 精度 舍入 真实验证 动画演示
Java 内部原理 2026-10-02

java.time:LocalDateTime、Instant、ZoneId 的区别

真实验证 java.time:同一个 LocalDateTime 在四个时区是四个 Instant;夏令时让 02:30 不存在、01:30 出现两次;plusDays(1) 与 plus(24 小时) 差一小时、跳时当天 Duration 是 PT23H 而 Period 是 P1D;月末加月的结果、非法日期的异常、圣保罗的「一天开始是 01:00」。

Java java.time Instant ZoneId 夏令时 真实验证 动画演示
Java 内部原理 2026-10-02

System.arraycopy:数组复制为什么能被 JVM 特殊优化

真实验证 System.arraycopy:重叠区间安全、类型不匹配时部分复制后抛 ArrayStoreException、越界不复制;复制 100 万个 int 时 arraycopy/copyOf/clone 约 0.069 ns/元素、手写循环 0.177;-Xint 下 arraycopy 0.157 ns/元素而手写循环 7.554(慢约 48 倍)。

Java System.arraycopy 数组 JIT 真实验证 动画演示
Java 内部原理 2026-10-02

写屏障与卡表:GC 如何知道哪些对象被修改

验证写屏障与卡表的效果和代价:老年代对象持有的年轻对象在 5 次年轻代 GC 后仍存活,引用置空后被回收;1 亿次引用存储的 ns/次:Serial 0.52、Parallel 0.519、G1 1.124、ZGC 0.756(int 存储约 0.379)。

Java 写屏障 卡表 垃圾回收 真实验证 动画演示
Java 内部原理 2026-10-02

VarHandle 内存序:opaque、acquire、release、volatile

用 jcstress 在 Apple Silicon 的 JDK 21 上跑消息传递测试(154 秒):普通读写、Opaque、Release/Acquire、volatile 四种写法都没有出现违反顺序的结果(普通与 Opaque 没有观察到不代表有保证);另一个测试里,热循环中的普通读 3 秒也看不到另一个线程的写入,而 Opaque/Acquire/volatile 读约 305 ms 后就看到了。

Java VarHandle 内存序 jcstress volatile 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号