Java中堆外内存通过Unsafe类(JDK 9+为jdk.internal.misc.Unsafe)调用mmap/VirtualAlloc等系统调用分配,需手动freeMemory释放,无GC管理,易内存泄漏或崩溃,生产环境不推荐,优先使用ByteBuffer.allocateDirect()。

Java中直接分配和释放操作系统堆外内存,主要依靠sun.misc.Unsafe类(JDK 9+被封装限制)或jdk.internal.misc.Unsafe(模块化后替代),配合底层系统调用如mmap/munmap(Linux/Unix)或VirtualAlloc/VirtualFree(Windows)。这类操作绕过JVM堆管理,不经过垃圾回收,需手动控制生命周期,风险高、易引发内存泄漏或崩溃。
获取Unsafe实例(受限但可行)
由于Unsafe被JDK设为强封装(JDK 9+默认不可反射访问),需通过反射+特权机制获取:
- 使用
java.lang.reflect.Field定位私有静态字段theUnsafe - 调用
setAccessible(true)突破模块限制(需JVM启动参数--add-opens java.base/jdk.internal.misc=ALL-UNNAMED) - 注意:JDK 17+进一步收紧,部分方式失效;生产环境不推荐,仅用于学习或特定高性能框架(如Netty、Lucene)内部实现
分配堆外内存(allocateMemory / reallocateMemory)
Unsafe.allocateMemory(long bytes)调用malloc(glibc)或mmap(MAP_ANONYMOUS)申请未初始化的原生内存块:
- 返回值是内存起始地址(
long型指针),无类型信息,需配合putXxx/getXxx系列方法读写 - 例如:
long addr = unsafe.allocateMemory(1024);分配1KB -
reallocateMemory(long address, long newSize)可调整已分配内存大小(类似realloc),但旧内容不一定保留,需自行处理迁移
释放堆外内存(freeMemory)
必须显式调用unsafe.freeMemory(address),否则内存永不回收:
立即学习“Java免费学习笔记(深入)”;
- 该方法对应
free(malloc分配)或munmap(mmap分配),由Unsafe内部根据分配方式自动选择 - 释放后继续访问该地址会导致
Segmentation fault(Linux)或ACCESS_VIOLATION(Windows) - 建议配合
Cleaner(JDK 9+)或PhantomReference做兜底清理,但不能替代主动释放
安全与兼容性注意事项
直接操作堆外内存不是常规开发路径,务必权衡利弊:
- JVM版本差异大:JDK 8可用
sun.misc.Unsafe;JDK 11+需适配jdk.internal.misc.Unsafe并开放模块 - 无边界检查:越界读写不会抛
ArrayIndexOutOfBoundsException,而是破坏内存或导致JVM crash - 不支持对象结构:无法存储Java对象引用,只能存基本类型或字节序列;对象需序列化/反序列化处理
- 替代方案优先考虑
ByteBuffer.allocateDirect()——它内部用Unsafe但封装了生命周期管理,更安全可控


















