Java中Unsafe分配堆外内存本质是调用malloc等系统调用,需手动释放以防泄漏;通过反射获取Unsafe实例后,用allocateMemory分配、freeMemory释放;因不参与GC,应结合Cleaner管理生命周期,生产环境优先选用NIO Direct Buffer等标准方案。

Java 中通过 Unsafe 分配堆外内存,本质是调用底层操作系统(如 malloc)申请原生内存,绕过 JVM 堆管理,但需手动控制生命周期——分配后必须显式释放,否则直接导致内存泄漏。
获取 Unsafe 实例(受限但可行)
由于 Unsafe.getUnsafe() 仅允许 Bootstrap 类加载器加载的类调用,常规应用需通过反射绕过访问限制:
- 使用
Field.getDeclaredField("theUnsafe")获取私有静态字段 - 调用
setAccessible(true)突破封装 - 通过
get(null)获取单例实例(JDK 8/11/17 均适用,但 JDK 21+ 进一步收紧,需配合--add-opens启动参数)
分配与释放堆外内存
Unsafe 提供两个核心方法:
-
allocateMemory(long bytes):返回内存起始地址(long类型指针),类似 C 的malloc -
freeMemory(long address):根据地址释放对应内存块,等价于free
例如分配 1KB 并写入一个 int:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
long addr = unsafe.allocateMemory(1024); unsafe.putInt(addr, 123); // 写入 int 到起始位置 // ... 使用后必须释放 unsafe.freeMemory(addr);
生命周期管理的关键约束
堆外内存不参与 GC,Unsafe 不提供自动清理机制,因此必须严格遵循“谁分配、谁释放”原则:
- 避免在对象 finalize 方法中释放——JVM 不保证其及时执行,且已标记为废弃
- 推荐结合
Cleaner(JDK 9+)注册清理动作,将释放逻辑绑定到对象不可达时的异步回调 - 若使用 NIO
ByteBuffer.allocateDirect(),其底层虽也用Unsafe,但已内置 Cleaner 封装,无需手动干预
安全与兼容性提醒
Unsafe 是非公开 API,行为不受 Java 规范约束:
- 不同 JDK 版本可能修改方法签名或语义(如 JDK 21 中部分方法被迁移到
MemorySegment和MemoryAccess) - 生产环境应优先考虑标准替代方案:NIO Direct Buffer、
VarHandle+MemorySegment(JDK 14+)、或第三方库如 Netty 的PooledByteBufAllocator - 若必须用
Unsafe,务必配套单元测试验证分配/释放成对、无重复释放或野指针访问

















