sun.misc.Unsafe 正在被淘汰。这篇用真实运行回答:Unsafe 在新 JDK 上到底会发生什么,哪些东西它做不到,以及 VarHandle 与 MemorySegment / Arena 这些替代方案在性能和安全性上是否能顶上。
[JDK 17] allocateMemory/putLong/getLong:getLong = 42 [JDK 21] allocateMemory/putLong/getLong:getLong = 42 [JDK 27] allocateMemory/putLong/getLong:getLong = 42 | WARNING: A terminally deprecated method in sun.misc.Unsafe has been called | WARNING: sun.misc.Unsafe::allocateMemory has been called by Main (file:<jtmp>/j258_27_unsafe/out/) | WARNING: Please consider reporting this to the maintainers of class Main | WARNING: sun.misc.Unsafe::allocateMemory will be removed in a future release [JDK 27 --sun-misc-unsafe-memory-access=allow] allocateMemory/putLong/getLong:getLong = 42 [JDK 27 --sun-misc-unsafe-memory-access=warn] allocateMemory/putLong/getLong:getLong = 42 | WARNING: A terminally deprecated method in sun.misc.Unsafe has been called | WARNING: sun.misc.Unsafe::allocateMemory has been called by Main (file:<jtmp>/j258_27_unsafe/out/) | WARNING: Please consider reporting this to the maintainers of class Main [JDK 27 --sun-misc-unsafe-memory-access=debug] allocateMemory/putLong/getLong:getLong = 42 | WARNING: sun.misc.Unsafe::allocateMemory called by Main (file:<jtmp>/j258_27_unsafe/out/) | at Main.main(Main.java:9) | WARNING: sun.misc.Unsafe::putLong called by Main (file:<jtmp>/j258_27_unsafe/out/) [JDK 27 --sun-misc-unsafe-memory-access=deny] Exception in thread "main" java.lang.UnsupportedOperationException: allocateMemory | at jdk.unsupported/sun.misc.Unsafe.beforeMemoryAccessSlow(Unsafe.java:1822) | at jdk.unsupported/sun.misc.Unsafe.beforeMemoryAccess(Unsafe.java:1787) | at jdk.unsupported/sun.misc.Unsafe.allocateMemory(Unsafe.java:679)
[JDK 17]
普通类的字段:objectFieldOffset = 12
record 的字段:UnsupportedOperationException:can't get field offset on a record class: private final int Main$R.x
lambda(隐藏类)的捕获字段:UnsupportedOperationException:can't get field offset on a hidden class: private final int Main$$Lambda$1/0x0000008001005000.arg$1
VarHandle 读 record 字段 x = 7
VarHandle 写 record 字段:UnsupportedOperationException:null
VarHandle 对普通 volatile 字段 compareAndSet(0, 5) = true,值 5
[JDK 21]
普通类的字段:objectFieldOffset = 12
record 的字段:UnsupportedOperationException:can't get field offset on a record class: private final int Main$R.x
lambda(隐藏类)的捕获字段:UnsupportedOperationException:can't get field offset on a hidden class: private final int Main$$Lambda/0x000000f001006800.arg$1
VarHandle 读 record 字段 x = 7
VarHandle 写 record 字段:UnsupportedOperationException:set
VarHandle 对普通 volatile 字段 compareAndSet(0, 5) = true,值 5
[JDK 27]
普通类的字段:objectFieldOffset = 16
record 的字段:UnsupportedOperationException:can't get field offset on a record class: private final int Main$R.x
lambda(隐藏类)的捕获字段:UnsupportedOperationException:can't get field offset on a hidden class: private final int Main$$Lambda/0x00001ff001042000.arg$1
VarHandle 读 record 字段 x = 7
VarHandle 写 record 字段:UnsupportedOperationException:set
VarHandle 对普通 volatile 字段 compareAndSet(0, 5) = true,值 5long[](堆内数组) 写 + 读各 1048576 个 long:每个元素约 0.48 ns(7 轮中位数) Unsafe(堆外地址) 写 + 读各 1048576 个 long:每个元素约 0.51 ns(7 轮中位数) MemorySegment.get(JAVA_LONG, offset) 写 + 读各 1048576 个 long:每个元素约 0.48 ns(7 轮中位数) MemorySegment 的 VarHandle 写 + 读各 1048576 个 long:每个元素约 0.47 ns(7 轮中位数) DirectByteBuffer.getLong 写 + 读各 1048576 个 long:每个元素约 0.49 ns(7 轮中位数) LongBuffer.get(i) 写 + 读各 1048576 个 long:每个元素约 0.49 ns(7 轮中位数) byte[] + byteArrayViewVarHandle 写 + 读各 1048576 个 long:每个元素约 0.50 ns(7 轮中位数)
1 线程 AtomicLong.incrementAndGet 每次约 1.9 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 1 线程 VarHandle.getAndAdd 每次约 4.0 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 1 线程 VarHandle.compareAndSet 循环 每次约 8.3 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 1 线程 Unsafe.compareAndSwapLong 循环 每次约 8.2 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 1 线程 synchronized + long++ 每次约 5.6 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 1 线程 LongAdder.increment 每次约 5.0 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 4 线程 AtomicLong.incrementAndGet 每次约 17.0 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 4 线程 VarHandle.getAndAdd 每次约 43.5 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 4 线程 VarHandle.compareAndSet 循环 每次约 113.4 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 4 线程 Unsafe.compareAndSwapLong 循环 每次约 92.8 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 4 线程 synchronized + long++ 每次约 20.5 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数) 4 线程 LongAdder.increment 每次约 1.3 ns(= 总耗时 ÷ 总次数 20000000,5 轮中位数)
Unsafe.allocateMemory 100 × 4 MiB 并写满、不 freeMemory:RSS 42 MiB → 453 MiB;System.gc() 之后 455 MiB(增加 412 MiB)
Arena.ofConfined 分配 100 × 4 MiB 并写满、每次 close:RSS 42 MiB → 峰值 61 MiB → 结束 63 MiB(增加 20 MiB)
Arena.ofAuto 同样 100 × 4 MiB、不 close:RSS 63 MiB → 461 MiB;System.gc() 之后 317 MiB(增加 253 MiB)
访问已 close 的 Arena 的 MemorySegment:IllegalStateException:Already closed
越界读 MemorySegment:IndexOutOfBoundsException:Out of bound access on segment MemorySegment{ kind: native, address: 0x7990fe6cf0, byteSize: 16 }; attempting to access an element of length 8 at offset 16 which is outside the valid range 0 <= offset+length < byteSize (=16)
对照:Unsafe 释放后再 getLong 不会抛异常(这里不执行,避免崩溃)