0

0

Java中的本地内存(Off-heap Memory)如何申请_DirectByteBuffer用法

P粉602998670

P粉602998670

发布时间:2026-03-01 12:21:34

|

723人浏览过

|

来源于php中文网

原创

directbytebuffer 通过 unsafe.allocatememory 直接申请堆外内存,不经过 jvm 堆,不受 gc 自动管理,需依赖 cleaner 机制释放;未及时释放会导致 direct buffer memory oom。

java中的本地内存(off-heap memory)如何申请_directbytebuffer用法

DirectByteBuffer 是怎么申请本地内存的

它不走 JVM 堆,而是调用 Unsafe.allocateMemory 直接向操作系统要内存,所以不受 GC 管理,也不算在 -Xmx 里。但代价是:你得自己管释放,否则就是隐形内存泄漏。

常见错误现象:OutOfMemoryError: Direct buffer memory —— 不是因为堆满了,而是 MaxDirectMemorySize(默认等于 -Xmx)被耗尽了,且大量 DirectByteBuffer 实例没被及时回收。

  • 必须显式调用 buffer.clear()buffer = null 不起作用,真正释放靠的是 Cleaner 机制,依赖 GC 触发
  • 如果频繁创建大块 direct buffer(比如每次网络读写都 new 一个 1MB 的 ByteBuffer.allocateDirect(1024*1024)),GC 压力会陡增,Cleaner 队列可能积压
  • 调试时可加 JVM 参数 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps,观察是否频繁出现 “Cleaner” 相关的 finalizer activity

为什么不能直接 new DirectByteBuffer()

它的构造函数是 package-private 的,JDK 不让你直接 new。所有标准途径都经过 ByteBuffer.allocateDirect(),这个方法内部会走 Bits.reserveMemory() 做配额检查,再委托给 Unsafe 分配。

使用场景:NIO 文件通道(FileChannel.map())、网络 socket 读写(SocketChannel.read(buffer))、Netty 的 PooledByteBufAllocator 底层等——这些地方需要零拷贝或避免堆内复制开销。

立即学习Java免费学习笔记(深入)”;

PhotoG
PhotoG

PhotoG是全球首个内容营销端对端智能体

下载
  • allocateDirect(n) 会尝试分配 n 字节,但实际分配的内存通常略大于 n(对齐填充,常见 64B 或页对齐)
  • 不同 JDK 版本对齐策略不同:JDK 8 默认按 64B 对齐;JDK 17+ 可能更激进,尤其在开启 -XX:+UseLargePages
  • 别试图反射绕过构造限制——DirectByteBuffer(long, int) 构造器虽存在,但跳过配额检查,容易触发 OutOfMemoryError 而不报具体原因

如何安全释放 DirectByteBuffer 占用的内存

不能靠等待 GC,也不能手动调 Cleaner.clean()(它已 deprecated)。正确做法是确保 buffer 对象尽早不可达,并主动触发 Cleaner 执行(仅限调试/紧急场景)。

性能影响:Cleaner 是基于 ReferenceQueue 的异步清理机制,如果 buffer 对象生命周期长(比如被缓存、被 long-lived 对象引用),对应内存就一直不归还。

  • 最稳妥方式:用完立即置为 null,并避免在 long-lived 对象中长期持有 DirectByteBuffer
  • 调试时可强制触发:先获取 buffer 的 cleaner(((DirectBuffer) buffer).cleaner()),再调 cleaner.clean() —— 但生产环境禁用,因可能破坏内部状态
  • JDK 14+ 引入 Buffer.clear() 不再清底层内存,只重置 position/limit;真正释放仍依赖 Cleaner,这点和 heap buffer 行为不一致,容易误判

DirectByteBuffer 和堆内 ByteBuffer 性能差异在哪

不是“一定更快”,而是“在特定路径下避免拷贝”。比如 SocketChannel.write(directBuffer) 可直接交由 OS sendfile 或 epoll 写入,而 heap buffer 必须先 copy 到一个临时 direct buffer 中(JDK 自动做,叫 “unfolding”),多一次 memcpy。

容易踩的坑:小数据量 + 高频分配时,direct buffer 的分配/释放开销远超 heap buffer,反而更慢。

  • 典型阈值:单次传输 > 4KB 且 buffer 复用率低时,direct 更合适;反之 heap buffer 更轻量
  • Netty 默认用池化 direct buffer,就是为摊平分配成本;自己手写 NIO 服务时若没池化,allocateDirect 调用本身就成了瓶颈
  • 注意 native stack:某些 JNI 调用(如 OpenSSL)会把 direct buffer 地址传入 C 层,若此时 Java 层 buffer 已被 Cleaner 回收,C 层继续访问就会 crash —— 这类 bug 很难复现,但后果严重

真正的难点不在申请,而在生命周期管理。只要 buffer 还被某个线程栈、静态集合、甚至 ThreadLocal 持有,内存就不会还给 OS,而你从 jstat 或 VisualVM 里根本看不到这部分占用。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

248

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

947

2024.03.01

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

910

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

595

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

294

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

210

2025.08.29

堆和栈的区别
堆和栈的区别

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

429

2023.07.18

堆和栈区别
堆和栈区别

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

599

2023.08.10

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

24

2026.02.28

热门下载

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

精品课程

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

共23课时 | 4万人学习

C# 教程
C# 教程

共94课时 | 10.5万人学习

Java 教程
Java 教程

共578课时 | 74.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号