JVM中DirectBuffer在遭遇Full GC时其关联的堆外内存是否会同步释放的解密

老枫吖_8000

老枫吖_8000

2026-06-07

680人浏览

原创

directbuffer堆外内存不随full gc同步释放,因其释放依赖cleaner异步执行deallocator回调,而cleaner触发需gc判定对象不可达并由低优先级cleanerthread处理,存在显著延迟。

jvm中directbuffer在遭遇full gc时其关联的堆外内存是否会同步释放的解密

不会同步释放。DirectBuffer 关联的堆外内存是否释放,不取决于 Full GC 是否发生,而取决于 Cleaner 是否被执行——而 Cleaner 的执行是异步、延迟且不可控的。

为什么 Full GC 不等于堆外内存释放

DirectBuffer 本身是个轻量级堆内对象(通常不到百字节),它只保存地址、容量等元数据;真正的大块内存由 Unsafe.allocateMemory 向操作系统申请,完全脱离堆内存管理范围。JVM 的 GC 只负责回收这个“壳子”,不直接调用 free()。释放动作必须经由 Cleaner 绑定的 Deallocator 回调触发,而 Cleaner 是 PhantomReference 的一种封装,其清理流程依赖两个关键环节:

  • GC 必须先将 DirectBuffer 对象判定为不可达,并将其关联的 Cleaner 入队到 ReferenceQueue;
  • CleanerThread(单线程、低优先级)需从队列中取出并执行 clean(),最终调用 unsafe.freeMemory。

这两个环节都存在显著延迟:GC 可能因 Minor GC 频繁而长期不扫描老年代(Cleaner 多在老年代),CleanerThread 又可能被阻塞(如 native free 调用卡在磁盘 IO 或锁竞争中),导致堆外内存“挂尸”数分钟甚至更久。

知海图Chat
知海图Chat

知海图Chat是基于知乎与面壁智能合作大模型能力的中文知识问答工具。

下载

Full GC 反而可能暴露或加剧释放滞后问题

当 JVM 检测到堆外内存使用逼近 -XX:MaxDirectMemorySize 限制(例如通过 Native Memory Tracking),会主动插入 System.gc() 建议,试图加速 Cleaner 入队与执行。此时日志中出现的 Full GC 往往不是结果,而是“症状”:

  • GC 日志里触发原因显示为 “Ergonomics” 或 “System.gc()”,而非正常晋升阈值;
  • NMT 报告中 Internal/Other 区域持续增长,但堆内存占用平稳;
  • 堆 dump 显示大量 sun.misc.Cleaner 实例处于 ReferenceQueue 中未处理状态,或 dominator tree 显示它们 retain 大量堆外 Buffer。

如何确认堆外内存是否真正释放了

不能只看 GC 日志或堆内存曲线,要交叉验证三类信号:

  • 查 NMT 实时用量:运行 jcmd <pid> VM.native_memory summary scale=MB</pid>,重点关注 Internal 区域的 “Internal (reserved)” 和 “Other” 分项,若多次 Full GC 后该值未下降,说明 native 内存未释放;
  • 看 CleanerThread 状态:用 jstack <pid></pid> 找到 CleanerThread,若长期处于 RUNNABLE 但无 clean() 栈帧,大概率卡在 native free;
  • 验 Deallocator 执行痕迹:在测试环境开启 -XX:+PrintGCDetails -XX:+PrintReferenceGC,观察日志中是否有 “Cleaner” 或 “PhantomReference” 处理记录。

真正可控的释放手段

靠等待 GC 和 CleanerThread 是被动策略,高负载场景下极易失效。生产环境应主动干预:

  • 显式调用 buffer.clear() + buffer = null,尽快切断强引用,缩短 Cleaner 入队等待时间;
  • 对关键 DirectBuffer(如 Netty PooledByteBuf),优先复用池化对象,避免高频创建销毁;
  • 必要时用 jcmd <pid> VM.run_finalization</pid> 强制推进 pending reference 处理(仅限临时救急);
  • 设置合理 -XX:MaxDirectMemorySize 并配合 NMT 监控,早于 OOM 触发告警,而非等 Full GC 来兜底。

相关专题

更多
堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

2023.07.18

5047

5

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2023.08.10

2248

6

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

2026.09.30

60

10

LLVM RISC-V参数配置教程
LLVM RISC-V参数配置教程

本专题介绍LLVM对RISC-V基础ISA和扩展的支持方式,涵盖RV32、RV64、标准扩展、实验性扩展、厂商扩展、-menable-experimental-extensions和版本差异。

2026.09.30

40

14

LLVM IR中间表示入门指南
LLVM IR中间表示入门指南

本专题整理LLVM IR的核心概念,包括中间表示作用、模块结构、函数、基本块、SSA形式、类型系统和常见语法,帮助新手理解LLVM编译流程中的关键层。

2026.09.30

40

12

PDF转图片方法
PDF转图片方法

需要把 PDF 页面用于上传、预览、分享或图片归档时,PDF 转图片方法专题整理 JPG/PNG 格式选择、逐页导出、清晰度设置、批量下载和结果检查等流程,帮助用户稳定完成 PDF 图片化处理。

2026.09.30

40

26

PixTV AI视频生成与无限画布创作
PixTV AI视频生成与无限画布创作

PixTV专题整理AI视频与视觉内容创作相关功能使用教程,涵盖AI生图、视频生成、无限画布、多模型创作、素材管理、声音音乐及视频剪辑等功能,帮助用户快速掌握PixTV从创意到成片的完整制作方法。

2026.09.29

40

15

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

2026.09.23

240

15

Buffalo框架路由与请求处理实操指南
Buffalo框架路由与请求处理实操指南

本专题讲解Buffalo框架路由与请求处理机制,涵盖路由注册与分组、资源路由、Handler编写规范、Context上下文方法、参数绑定、中间件编写挂载、Session与Cookie读写、Flash消息及错误页面定制方法。

2026.09.23

160

15

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程