news 2026/4/16 18:15:23

直接内存的释放原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
直接内存的释放原理

直接内存的释放核心依赖 Cleaner 机制(虚引用) ,配合 JVM 垃圾回收触发,无需手动调用 Native 方法,具体原理如下:
一、释放的核心依赖:Cleaner 与虚引用
1. Cleaner 的本质:sun.misc.Cleaner是 Java 中的虚引用实现,继承自PhantomReference,专门用于跟踪对象生命周期并触发资源释放。
2. 关联逻辑:创建DirectByteBuffer时,JVM 会同时创建一个Cleaner对象,将其与DirectByteBuffer绑定。
◦ Cleaner会持有两个关键引用:一是指向DirectByteBuffer的虚引用,二是指向释放直接内存的回调函数(Deallocator)。
◦ DirectByteBuffer本身存储在 Java 堆中,仅作为直接内存的 “引用句柄”,不存储实际数据。
二、释放的完整流程
1. 触发条件:当DirectByteBuffer对象失去所有强引用(如引用被置为null),且 JVM 执行 GC 时,该对象会被标记为可回收。
2. 虚引用触发:GC 发现DirectByteBuffer可回收后,会将关联的Cleaner对象加入到 “引用队列”(ReferenceQueue)。
3. 执行释放回调:JVM 内部有一个后台线程(ReferenceHandler),会持续扫描引用队列。
◦ 当检测到队列中的Cleaner时,会调用其clean()方法。
◦ clean()方法会触发Deallocator的run()方法,通过 Native 调用(如Unsafe.freeMemory())释放对应的直接内存。
4. 最终回收:DirectByteBuffer对象被 GC 从堆中回收,直接内存被操作系统回收,完成整个释放流程。
三、关键细节说明
1. 无需手动调用释放方法:只要DirectByteBuffer的强引用被释放,GC 触发后会自动通过Cleaner完成直接内存释放,无需手动调用freeMemory()。
2. 释放的不确定性:释放时机依赖 GC 触发时机,若DirectByteBuffer长期被强引用(如静态变量持有),GC 不会回收该对象,Cleaner也不会触发,会导致直接内存泄漏。
3. 显式辅助释放:对于大容量直接内存,可主动置空DirectByteBuffer引用(buffer = null),配合System.gc()(仅建议,不依赖)提示 JVM 触发 GC,加速释放,但System.gc()可能被-XX:+DisableExplicitGC参数禁用。
4. 特殊情况:直接使用 Unsafe 分配:若通过Unsafe.allocateMemory()手动分配直接内存,不会关联Cleaner,必须手动调用Unsafe.freeMemory()释放,否则会造成永久内存泄漏。
四、常见误区澄清
• 误区 1:DirectByteBuffer被 GC 回收 = 直接内存释放?
不成立。DirectByteBuffer的回收是前提,真正释放直接内存的是Cleaner的回调,两者是 “触发与执行” 的关系。
• 误区 2:手动调用buffer.clear()能释放直接内存?
不能。clear()仅重置缓冲区的读写指针,不会释放DirectByteBuffer的强引用,也不会触发直接内存释放。

直接内存的释放流程是怎样的?

直接内存的释放流程核心是 “强引用失效→GC 标记→Cleaner 回调→Native 释放”,全程由 JVM 协同 Cleaner 机制自动完成,具体步骤如下:
1. 前提:DirectByteBuffer 失去强引用
• 应用程序中持有DirectByteBuffer的强引用被销毁(如引用变量置为null、超出作用域)。
• 此时DirectByteBuffer对象成为 GC 可达性分析中的 “不可达对象”,仅保留与Cleaner的虚引用关联。
2. GC 触发并标记可回收对象
• JVM 执行垃圾回收(Minor GC 或 Full GC),扫描到DirectByteBuffer已不可达。
• GC 不会直接回收DirectByteBuffer,而是先检测到其关联的Cleaner(虚引用),并将Cleaner对象加入预设的 “引用队列”(ReferenceQueue)。
3. 后台线程处理引用队列
• JVM 启动时会创建一个优先级最高的后台线程ReferenceHandler,专门循环扫描引用队列。
• 当线程发现队列中新增Cleaner对象时,会调用其clean()方法,触发释放逻辑。
4. 执行 Native 方法释放直接内存
• Cleaner的clean()方法会调用内部关联的Deallocator(释放器)的run()方法。
• Deallocator通过Unsafe.freeMemory()(Native 调用),直接向操作系统申请释放对应的堆外内存(直接内存)。
5. 最终回收堆中 DirectByteBuffer 对象
• 直接内存释放完成后,DirectByteBuffer对象不再有任何引用关联,GC 会在本次或下次回收中,将其从 Java 堆中彻底回收。
• 整个流程结束,直接内存和堆内存均完成释放。
关键补充
• 释放时机依赖 GC:若 GC 未触发,即使DirectByteBuffer不可达,直接内存也不会立即释放,可能出现 “堆内存空闲但直接内存占用高” 的情况。
• 手动辅助释放:可通过buffer = null+System.gc()(仅提示 GC,不保证立即执行)加速流程,但需注意System.gc()可能被-XX:+DisableExplicitGC禁用。
• 手动分配场景:若通过Unsafe.allocateMemory()分配直接内存,无Cleaner关联,需手动调用Unsafe.freeMemory(),否则会造成内存泄漏。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 12:16:55

Windows Cleaner终极指南:3步彻底解决C盘爆红问题

Windows Cleaner终极指南:3步彻底解决C盘爆红问题 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服! 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner 还在为C盘爆红而烦恼吗?每次开机都看到那…

作者头像 李华
网站建设 2026/4/16 10:19:13

SerialPlot终极实战:从零精通串口数据可视化

SerialPlot终极实战:从零精通串口数据可视化 【免费下载链接】serialplot Small and simple software for plotting data from serial port in realtime. 项目地址: https://gitcode.com/gh_mirrors/se/serialplot 当你面对源源不断的串口数据流时&#xff0…

作者头像 李华
网站建设 2026/4/16 10:20:57

告别输入法切换困扰:深蓝词库转换工具助你轻松迁移个性化词库

还在为更换输入法而烦恼吗?精心积累多年的个性化词库数据,难道每次都要重新开始?输入法词库转换工具的出现,彻底解决了这一痛点,让你在不同平台间实现跨平台词库迁移的无缝衔接。 【免费下载链接】imewlconverter ”深…

作者头像 李华
网站建设 2026/4/16 10:15:05

解析Java一维数组:线性数据存储的基础工具

在Java编程体系中,一维数组是最基础的线性数据结构,它以连续的内存空间存储相同数据类型的元素集合,是处理批量同类型数据的核心工具。一维数组的使用遵循“声明—初始化—访问”的基本流程。声明时需指定数据类型与数组标识,格式…

作者头像 李华
网站建设 2026/4/16 10:13:40

Windows Cleaner高效清理指南:彻底解决C盘空间不足的完整方案

Windows Cleaner高效清理指南:彻底解决C盘空间不足的完整方案 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服! 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner 还在为电脑运行缓慢而烦恼?C…

作者头像 李华