知其然,知其所以然
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 真实验证 动画演示
Java 内部原理 2026-10-02

final 字段:为什么构造完成后有特殊可见性

验证 final 字段的可见性保证:构造器里 this 逃逸时另一个线程读到 final 字段是默认值 0;反射可以修改实例 final 字段,编译期常量会被内联而读不到新值,static final 与 record 字段则抛 IllegalAccessException;用 jcstress 做数据竞争发布,final 与普通字段在这台机器上都没有读到默认值(无法区分)。

Java final 字段 安全发布 jcstress 真实验证 动画演示
Java 内部原理 2026-10-02

安全发布:对象构造完成后如何被其它线程正确看到

验证对象的几种发布方式:普通静态字段发布的对象,JIT 热循环里的读线程 3 秒仍看不到;volatile、AtomicReference、synchronized getter、ConcurrentHashMap 约 207 ms 后都看到了完整的对象;Thread.start、Thread.join 与类初始化器发布不需要额外同步。

Java 安全发布 可见性 volatile 真实验证 动画演示
Java 内部原理 2026-10-02

HttpClient:连接复用、异步请求和 HTTP/2

用 JDK 21 的 HttpClient 对 nginx(TLS 端口 HTTP/2、明文端口 HTTP/1.1)做实测:偏好 HTTP_2 访问明文端口退回 HTTP_1_1;同一个 HttpClient 20 个请求用 1 个连接、每次新建用 20 个;50 个并发请求 HTTP/2 只用 1 个连接、HTTP/1.1 用 50 个;超时与取消的异常。

Java HttpClient HTTP/2 连接复用 nginx 真实验证 动画演示
Java 内部原理 2026-10-02

JSSE TLS 握手:Java 如何验证证书和协商密钥

用 JDK 21 的 JSSE 做真实的 TLS 实验:协商出 TLSv1.3 与 TLS_AES_256_GCM_SHA384;自签名证书不在信任库、主机名不符、CA 签发的证书过期时分别抛出什么错误;协议版本没有交集时 protocol_version 告警;同一个 SSLContext 的会话复用;javax.net.debug 里的握手消息顺序。

Java JSSE TLS 证书 握手 真实验证 动画演示
Java 内部原理 2026-10-02

JAR 文件:manifest、签名和多版本 JAR

真实验证 JAR:Main-Class 缺失时的报错、java -jar 不使用外部类路径;Class-Path 相对 jar 位置、manifest 每行最多 72 字节自动折行;jarsigner 签名增加 .SF 与 .EC,篡改 class 后 SHA-384 digest error;多版本 JAR 在 JDK 17、21、27 上分别选 versions/17、21、21 的实现。

Java JAR manifest jarsigner 多版本 JAR 真实验证 动画演示
Java 内部原理 2026-10-02

正则表达式:回溯引擎为什么会灾难性回溯

在 JDK 21 上实测正则回溯:经典的 (a+)+b 等贪婪写法已经不会指数爆炸,但懒惰量词、(.*a){12}、带反向引用的模式仍然每多 4 个字符慢十几倍,n = 28 超过 2 秒;原子组、占有量词或改写模式可以解决;(a|b)* 对 4000 字符的输入栈溢出,[ab]* 不会;用自定义 CharSequence 给匹配加截止时间。

Java 正则表达式 回溯 ReDoS 真实验证 动画演示
Java 内部原理 2026-10-02

Formatter:格式化字符串背后的解析过程

真实运行 String.format:说明符语法(宽度、补零、千分位、%x、%e、%b、%c)、舍入是 HALF_UP(%.0f 把 2.5 变成 3)、八种出错异常、Locale 对千分位与小数点(de_DE 的 1.234.567 / 1234,50)和大写(土耳其语 i → İ)的影响、参数序号 %2$s 与 %<s、Formattable。

Java Formatter String.format Locale 真实验证 动画演示
京ICP备2026067502号-1京公网安备11010502063487号