用 curl 与自己起的 nginx(h2)验证 HTTP/2:ALPN 协商 h2、伪头部;10 个并发请求 HTTP/2 用 1 个连接、HTTP/1.1 用 10 个;流编号是奇数递增 [1, 3, 5, 7]…;用 Netty 的 HPACK 编码器,相同请求头第 1 次 136 字节、之后 7 字节(HTTP/1.1 文本 190 字节)。
实测 JVM 的 DNS 缓存(JDK 21,真实公网域名):默认第一次解析 70.58 ms、缓存命中约 0.485 ms、t=31 s 过期,与 30 秒默认值一致;networkaddress.cache.ttl=2 与 0 的效果;对不存在的域名默认也有 10 秒的负缓存(t=9 s 命中、t=11 s 过期)。
用真实的插件 jar 验证 URLClassLoader:接口由父加载器共享、插件类只在自己的加载器里;jar 自带接口且父加载器为 null 时 ClassCastException;两个加载器加载同一个 jar 得到不同的类;close() 释放文件描述符,引用清空后 GC 能卸载;插件起了线程时卸载不掉。
验证 ModuleLayer:启动层不含 greeter,新层的父层是 boot、有自己的类加载器;同一个 JVM 里同时运行 greeter 的 v1 与 v2(两个不同的 greet.Greeter 类);层内模块仍遵守 exports,ServiceLoader 可限定到某一层;重复创建得到不同的 Module,解析缺失模块抛 FindException。
用 -javaagent 实测 Instrumentation:premain 在 main 之前执行、此时已加载 809 个类、transformer 只被新加载的 6 个类调用;用 ASM 在 demo.Target 的方法入口出口插入计时(1795 µs 与 86 µs);transformer 抛异常没有任何输出、类按原样加载。
用 clang 编译一个真实的 JVMTI C agent:申请能力成功(可申请 68 项)、通过事件回调统计 ClassPrepare 172、ThreadStart 5、GC 9 次;VMDeath 时 GetLoadedClasses 661 个类、IterateOverInstancesOfClass 数出 app.Foo 1234 个实例、列出 6 个线程及状态。
真实验证局部变量lambda直接自引用真实编译失败(variable fib might not have been initialized);真实意外发现连static字段在同一条语句里自引用也真实编译失败(self-reference in initializer);真实验证数组包装、静态字段分两步赋值、具名方法引用三种写法都能正确实现递归。
真实记录Derived继承Base,各自都有实例初始化块、字段初始化、构造器时的真实完整执行顺序;真实验证同一个类里多个初始化块和字段初始化真实严格按源码书写顺序交替执行。
真实用两个线程复现ABA问题:threadA把AtomicReference<Integer>的值从100改成50再改回100,threadB用最初读到的100做CAS(999)真实依然成功;真实意外发现超出Integer缓存范围时单线程内CAS都会因自动装箱产生不同对象实例而真实失败;真实验证AtomicStampedReference靠版本戳真正解决了ABA问题。
真实用一个ReentrantLock创建notEmpty和notFull两个独立Condition,各挂起5个等待线程;真实调用notEmpty.signalAll()后,notEmpty组真实全部被唤醒(5),notFull组真实一个都没动(0);真实验证未持有锁时调用Condition.await()真实抛出IllegalMonitorStateException。
真实用8线程各调用500万次nextInt(),共享一个Random实例真实耗时7935ms,改用ThreadLocalRandom.current()真实只要43ms,真实快184.53倍;真实验证同一线程内两次ThreadLocalRandom.current()调用返回同一个实例,不同线程各自拿到独立、不需要竞争的随机数生成器。
真实验证Arrays.fill(int[][] arr2d, int[] sharedRow)这个合法调用真实把每一行都指向同一个数组对象,不是三个独立副本;真实修改arr2d[0][0]后,其余两行真实一起被改变;真实对比正确写法(for循环对每行单独调用Arrays.fill)才能得到三个真正独立的行数组。