当前位置: 首页 > news >正文

Java 直接内存ByteBuffer.allocateDirect原理与源码解析

Java直接内存通过ByteBuffer.allocateDirect()分配,绕开Java堆直接操作本地内存,适用于高频IO场景。以下结合源码详解其全流程:


一、Java层入口:DirectByteBuffer初始化

代码入口ByteBuffer.allocateDirect(int capacity)

public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}

创建DirectByteBuffer对象,构造函数核心逻辑如下:

  1. 内存对齐与容量计算

    boolean pa = VM.isDirectMemoryPageAligned(); // 是否页对齐
    int ps = Bits.pageSize(); // 系统页面大小
    long size = Math.max(1L, (long)cap + (pa ? ps : 0)); // 总分配大小

    若开启页对齐,分配大小=容量+页面大小,确保后续调整后用户内存对齐。

  2. 配额预留与分配检查

    Bits.reserveMemory(size, cap); // 检查直接内存配额

    Bits.reserveMemory内部通过-XX:MaxDirectMemorySize判断是否超限,可能触发GC或抛出OOM。

  3. 本地内存分配

    long base = UNSAFE.allocateMemory(size); // 调用native方法

    调用Unsafe.allocateMemory,通过JNI进入JVM层。


二、JVM层:本地内存分配

JNI映射allocateMemory0对应Unsafe_AllocateMemory0

UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory0(...)) {size_t sz = (size_t)size;void* x = os::malloc(sz, mtOther); // 调用os::mallocreturn addr_to_java(x);
} UNSAFE_END
  1. 操作系统内存分配

    void* os::malloc(size_t size, MEMFLAGS flags) {if (size == 0) size = 1; // 保证至少分配1字节return ::malloc(alloc_size); // 调用系统malloc
    }

    通过系统调用(如glibc的malloc())分配内存,支持NMT(Native Memory Tracking)追踪。

  2. 内存追踪与保护

    ptr = (u_char*)::malloc(alloc_size); // 实际分配内存
    MemTracker::record_malloc(...); // 记录内存分配

    NMT添加头信息跟踪内存使用,调试模式下通过GuardedMemory检测越界访问。


三、Java层后续处理
  1. 内存初始化

    UNSAFE.setMemory(base, size, (byte) 0); // 清零内存

    确保分配的内存初始为0,避免脏数据。

  2. 地址对齐调整

    if (pa && (base % ps != 0)) {address = base + ps - (base & (ps - 1)); // 页面对齐调整
    }

    若开启页对齐,调整返回地址为页面起始位置,提升IO效率。

  3. 注册Cleaner释放资源

    cleaner = Cleaner.create(this, new Deallocator(base, size, cap));

    Cleaner通过虚引用(PhantomReference)关联DirectByteBuffer,当对象GC回收时触发Deallocator.run()释放内存:

    UNSAFE.freeMemory(base); // 释放本地内存
    Bits.unreserveMemory(size, cap); // 释放配额

四、异常处理
  • 分配失败:若os::malloc返回NULL,抛出OutOfMemoryError,并回滚配额。

  • 页对齐失败:地址计算错误可能导致内存访问异常,但JVM通过保护页机制拦截非法访问。


五、关键设计
  1. 配额管理Bits类全局记录已分配内存,避免超出MaxDirectMemorySize限制。

  2. 页对齐优化:对齐的内存提升NIO操作性能(如FileChannel读写)。

  3. 安全释放:Cleaner机制确保即使开发者未手动释放,GC时也能自动回收内存,防止泄漏。


总结:Java直接内存通过JNI桥接,由JVM调用系统API分配本地内存,结合配额管理、页对齐优化及Cleaner自动释放机制,实现了高效、安全的内存管理。

##源码

java代码
public long allocateMemory(long bytes) {allocateMemoryChecks(bytes);if (bytes == 0) {return 0;}long p = allocateMemory0(bytes);if (p == 0) {throw new OutOfMemoryError();}return p;}private native long allocateMemory0(long bytes);public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);}DirectByteBuffer(int cap) {                   // package-privatesuper(-1, 0, cap, cap);boolean pa = VM.isDirectMemoryPageAligned();int ps = Bits.pageSize();long size = Math.max(1L, (long)cap + (pa ? ps : 0));Bits.reserveMemory(size, cap);long base = 0;try {base = UNSAFE.allocateMemory(size);} catch (OutOfMemoryError x) {Bits.unreserveMemory(size, cap);throw x;}UNSAFE.setMemory(base, size, (byte) 0);if (pa && (base % ps != 0)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}cleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}openjdk jvm C++代码{CC "allocateMemory0",    CC "(J)" ADR,              FN_PTR(Unsafe_AllocateMemory0)},UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory0(JNIEnv *env, jobject unsafe, jlong size)) {size_t sz = (size_t)size;assert(is_aligned(sz, HeapWordSize), "sz not aligned");void* x = os::malloc(sz, mtOther);return addr_to_java(x);
} UNSAFE_ENDvoid* os::malloc(size_t size, MEMFLAGS flags) {return os::malloc(size, flags, CALLER_PC);
}void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));// Since os::malloc can be called when the libjvm.{dll,so} is// first loaded and we don't have a thread yet we must accept NULL also here.assert(!os::ThreadCrashProtection::is_crash_protected(Thread::current_or_null()),"malloc() not allowed when crash protection is set");if (size == 0) {// return a valid pointer if size is zero// if NULL is returned the calling functions assume out of memory.size = 1;}// NMT supportNMT_TrackingLevel level = MemTracker::tracking_level();size_t            nmt_header_size = MemTracker::malloc_header_size(level);#ifndef ASSERTconst size_t alloc_size = size + nmt_header_size;
#elseconst size_t alloc_size = GuardedMemory::get_total_size(size + nmt_header_size);if (size + nmt_header_size > alloc_size) { // Check for rollover.return NULL;}
#endif// For the test flag -XX:MallocMaxTestWordsif (has_reached_max_malloc_test_peak(size)) {return NULL;}u_char* ptr;ptr = (u_char*)::malloc(alloc_size);#ifdef ASSERTif (ptr == NULL) {return NULL;}// Wrap memory with guardGuardedMemory guarded(ptr, size + nmt_header_size);ptr = guarded.get_user_ptr();if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) {log_warning(malloc, free)("os::malloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, p2i(ptr));breakpoint();}if (paranoid) {verify_memory(ptr);}
#endif// we do not track guard memoryreturn MemTracker::record_malloc((address)ptr, size, memflags, stack, level);
}

http://www.xdnf.cn/news/440533.html

相关文章:

  • git切换分支后需要pull吗
  • Spark缓存---cache方法
  • 在Ubuntu24.04中配置开源直线特征提取软件DeepLSD
  • Java 与 Go 语言对比
  • Milvus 视角看主流嵌入式模型(Embeddings)
  • 推荐一个Winform开源的UI工具包
  • 《AI大模型应知应会100篇》第64篇:构建你的第一个大模型 Chatbot
  • 嵌入式C语言中指针的不同类型及其特点分析
  • iOS 阅后即焚功能的实现
  • 如何利用大模型对文章进行分段,提高向量搜索的准确性?
  • 关于 Golang GC 机制的一些细节:什么是根对象?GC 机制的触发时机?
  • 【SSL证书系列】操作系统如何保障根证书的有效性和安全
  • 【sql】按照数据的日期/天 ,对入库数据做数量分类
  • java加强 -File
  • MobiPDF:安卓设备上的专业PDF阅读与编辑工具
  • 【CustomPagination:基于Vue 3与Element Plus的高效二次封装分页器】
  • Spark的基础介绍
  • 性能比拼: Nginx vs. Envoy
  • AcroForm JavaScript Promise 对象应用示例: 异步加载PDF文件
  • YOLO v1:目标检测领域的革命性突破
  • 笔记本电脑打开网页很慢,一查ip地址网段不对怎么处理
  • DAX权威指南2:CALCULATE 与 CALCULATETABLE
  • Windows 环境下安装 Node 和 npm
  • 智能化双语LaTeX系统,分阶段系统性开发技术实现路径:目标是实现语义级编译和认知增强写作,推动跨文明知识表达
  • 【C++ / STL】封装红黑树实现map和set
  • 【LeetCode 热题 100】反转链表 / 回文链表 / 有序链表转换二叉搜索树 / LRU 缓存
  • 腾讯云-人脸核身+人脸识别教程
  • 榕壹云打车系统:基于Spring Boot+MySQL+UniApp的开源网约车解决方案
  • PCB设计实践(十七)PCB设计时11个维度分析双层板和四层板该如何抉择
  • python打卡day25