主要内容总结:
- 关于Volodymyr Gubarkov:2024 年 2 月,在https://github.com/xonixx/gc_less仓库中进行了无 GC(无堆)Java 的一系列实验,使用
sun.misc.Unsafe
和java.lang.foreign.MemorySegment
。 - 为何进行实验:出于好玩和好奇,始于想检查著名的
sun.misc.Unsafe
方法在最新 Java 中是否仍可行,还想尝试像在 C 中那样用 Java 编程。 实验方法:
- sun.misc.Unsafe:该类在最新 Java 23 版本仍受支持,但很危险,容易使 JVM 崩溃,但其有与堆外内存管理相关的方法族。
- 数据结构实现:作为无 GC 编程的实际应用,实现了数组、数组列表、栈、哈希表等基本数据结构,通过模板生成不同 Java 类型的实现,模板本身是可运行代码且对应
long
专门化版本,方便测试确保所有专门化实现正确,生成脚本为gen.awk。 - 启用 Epsilon GC:为确保不意外使用堆,启用了 Epsilon GC 设置,它不实现实际的内存回收机制,堆耗尽时 JVM 会关闭。
- 一些实现细节:以类似 OOP 的“奇怪”方式实现数据结构,所有方法静态,接受
long address
作为参数,代替this
;try-with-resources
结合Cleaner
和Ref
类负责释放内存,Ref
类用于注册对象清理;实现了内存泄漏检测,通过在内存分配时记录位置,释放时丢弃来检测。 - 可视化演示:手动内存管理离堆的好处之一是确定性内存使用,Main4类提供了循环中分配和释放的可视化演示。
- java.lang.foreign.MemorySegment:最近出现 JEP“Deprecate Memory-Access Methods in sun.misc.Unsafe for Removal”,提供了更安全的替代方案
java.lang.foreign.MemorySegment
类,它是“管理”的原生内存 API,虽看起来更重,但实验表明基于MemorySegment
的哈希表实现堆内存消耗为 O(1),这是因为MemorySegment
是值类,在 JIT 编译时会被内联消除所有分配。还实现了类似 Python 的离堆哈希表实现,通过重新分配连续内存来管理数据增长。 - 带有限堆的压力测试:创建实验程序,在固定 20MB 堆大小的 Epsilon-GC 下,比较
Unsafe
、Python 风格和MemorySegment
三种哈希表实现,结果三种实现都通过测试。 - 基准测试:创建AccessSpeedTest.java测量读写速度,
int[]
和MemorySegment
在速度上无明显差异;IntHashtableSpeedTest测试不同哈希表实现的速度,表明无 GC 算法通常比常规 GC 全算法慢,特定情况下map<int,int>
的 Python 风格实现比内置HashMap
快 8 倍。
- 总结收获:在 Java 中可以使用手动内存管理,如需要确定性内存消耗;速度不是使用离堆算法的好理由,相同算法离堆时慢 3 倍以上,可能是 JIT 编译器对常规 Java 代码有更好的优化机会;建议在实现中优先使用
java.lang.foreign.MemorySegment
而不是sun.misc.Unsafe
。如果发现错误或有其他反馈,可发邮件至 mailto:xonixx@gmail.com。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。