jvm对外内存-direct buffer

news/2026/1/19 11:37:44/文章来源:https://www.cnblogs.com/LQBlog/p/19500825

现象

image

分析

步骤一:jvm堆内存健康

image

步骤二 

通过了解这种一般是代码里面触发,一般自己写业务代码不会有手动调用,那么就只有框架,发现直接内存处于高水位,直接内存高水位,但是没有达到限制

image

image

步骤三

了解到框架代码使用直接内存,如果申请不足会尝试调用system.GC,可能是框架导致

try {buffer = ByteBuffer.allocateDirect(size);
} catch (OutOfMemoryError e) {// JVM 会尝试一次 Full GC 来回收被 Cleaner 管理的 DirectByteBufferSystem.gc();  // 由 Java 内部触发,不是您的代码!try {Thread.sleep(100);} catch (InterruptedException ignored) {}buffer = ByteBuffer.allocateDirect(size);
}

以下内容引入自:https://learn.lianglianglee.com/专栏/Netty%20核心原理剖析与%20RPC%20实践-完/10%20%20双刃剑:合理管理%20Netty%20堆外内存.md

为什么需要对堆外内存

在 Java 中对象都是在堆内分配的,通常我们说的JVM 内存也就指的堆内内存,堆内内存完全被JVM 虚拟机所管理,JVM 有自己的垃圾回收算法,对于使用者来说不必关心对象的内存如何回收。

堆外内存与堆内内存相对应,对于整个机器内存而言,除堆内内存以外部分即为堆外内存,堆外内存不受 JVM 虚拟机管理,直接由操作系统管理。

堆外内存优缺点

堆外内存和堆内内存各有利弊,这里我针对其中重要的几点进行说明。

  1. 堆内内存由 JVM GC 自动回收内存,降低了 Java 用户的使用心智,但是 GC 是需要时间开销成本的,堆外内存由于不受 JVM 管理,所以在一定程度上可以降低 GC 对应用运行时带来的影响。
  2. 堆外内存需要手动释放,这一点跟 C/C++ 很像,稍有不慎就会造成应用程序内存泄漏,当出现内存泄漏问题时排查起来会相对困难。
  3. 当进行网络 I/O 操作、文件读写时,堆内内存都需要转换为堆外内存,然后再与底层设备进行交互,直接使用堆外内存可以减少一次内存拷贝。
  4. 堆外内存可以实现进程之间、JVM 多实例之间的数据共享。

由此可以看出,如果你想实现高效的 I/O 操作、缓存常用的对象、降低 JVM GC 压力,堆外内存是一个非常不错的选择。

堆外内存的分配方式

Java 中堆外内存的分配方式有两种:ByteBuffer#allocateDirect和Unsafe#allocateMemory。

首先我们介绍下 Java NIO 包中的 ByteBuffer 类的分配方式,使用方式如下:

// 分配 10M 堆外内存

ByteBuffer buffer = ByteBuffer.allocateDirect(10 * 1024 * 1024); 

跟进 ByteBuffer.allocateDirect 源码,发现其中直接调用的 DirectByteBuffer 构造函数:

DirectByteBuffer(int cap) {super(-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)) {address = base + ps - (base & (ps - 1));} else {address = base;}cleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}

image

从 DirectByteBuffer 的构造函数中可以看出,真正分配堆外内存的逻辑还是通过 unsafe.allocateMemory(size),接下来我们一起认识下 Unsafe 这个神秘的工具类。

Unsafe 是一个非常不安全的类,它用于执行内存访问、分配、修改等敏感操作,可以越过 JVM 限制的枷锁。Unsafe 最初并不是为开发者设计的,使用它时虽然可以获取对底层资源的控制权,但也失去了安全性的保证,所以使用 Unsafe 一定要慎重。Netty 中依赖了 Unsafe 工具类,是因为 Netty 需要与底层 Socket 进行交互,Unsafe 在提升 Netty 的性能方面起到了一定的帮助。

在 Java 中是不能直接使用 Unsafe 的,但是我们可以通过反射获取 Unsafe 实例,使用方式如下所示

private static Unsafe unsafe = null;static {try {Field getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");getUnsafe.setAccessible(true);unsafe = (Unsafe) getUnsafe.get(null);} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}}

获得 Unsafe 实例后,我们可以通过 allocateMemory 方法分配堆外内存,allocateMemory 方法返回的是内存地址,使用方法如下所示:

 

// 分配 10M 堆外内存long address = unsafe.allocateMemory(10 * 1024 * 1024);

与 DirectByteBuffer 不同的是,Unsafe#allocateMemory 所分配的内存必须自己手动释放,否则会造成内存泄漏,这也是 Unsafe 不安全的体现。Unsafe 同样提供了内存释放的操作:

unsafe.freeMemory(address);

到目前为止,我们了解了堆外内存分配的两种方式,对于 Java 开发者而言,常用的是 ByteBuffer.allocateDirect 分配方式,我们平时常说的堆外内存泄漏都与该分配方式有关,接下来我们一起看看使用 ByteBuffer 分配的堆外内存如何被 JVM 回收,这对我们排查堆外内存泄漏问题有较大的帮助。

堆外内存的回收

我们试想这么一种场景,因为 DirectByteBuffer 对象有可能长时间存在于堆内内存,所以它很可能晋升到 JVM 的老年代,所以这时候 DirectByteBuffer 对象的回收需要依赖 Old GC 或者 Full GC 才能触发清理。如果长时间没有 Old GC 或者 Full GC 执行,那么堆外内存即使不再使用,也会一直在占用内存不释放,很容易将机器的物理内存耗尽,这是相当危险的。

那么在使用 DirectByteBuffer 时我们如何避免物理内存被耗尽呢?因为 JVM 并不知道堆外内存是不是已经不足了,所以我们最好通过 JVM 参数 -XX:MaxDirectMemorySize 指定堆外内存的上限大小,当堆外内存的大小超过该阈值时,就会触发一次 Full GC 进行清理回收,如果在 Full GC 之后还是无法满足堆外内存的分配,那么程序将会抛出 OOM 异常。

此外在 ByteBuffer.allocateDirect 分配的过程中,如果没有足够的空间分配堆外内存,在 Bits.reserveMemory 方法中也会主动调用 System.gc() 强制执行 Full GC,但是在生产环境一般都是设置了 -XX:+DisableExplicitGC,System.gc() 是不起作用的,所以依赖 System.gc() 并不是一个好办法。

通过前面堆外内存分配方式的介绍,我们知道 DirectByteBuffer 在初始化时会创建一个 Cleaner 对象,它会负责堆外内存的回收工作,那么 Cleaner 是如何与 GC 关联起来的呢?

Java 对象有四种引用方式:强引用 StrongReference、软引用 SoftReference、弱引用 WeakReference 和虚引用 PhantomReference。其中 PhantomReference 是最不常用的一种引用方式,Cleaner 就属于 PhantomReference 的子类,如以下源码所示,PhantomReference 不能被单独使用,需要与引用队列 ReferenceQueue 联合使用。

public class Cleaner extends java.lang.ref.PhantomReference<java.lang.Object> {private static final java.lang.ref.ReferenceQueue<java.lang.Object> dummyQueue;private static sun.misc.Cleaner first;private sun.misc.Cleaner next;private sun.misc.Cleaner prev;private final java.lang.Runnable thunk;public void clean() {}}

首先我们看下,当初始化堆外内存时,内存中的对象引用情况如下图所示,first 是 Cleaner 类中的静态变量,Cleaner 对象在初始化时会加入 Cleaner 链表中。DirectByteBuffer 对象包含堆外内存的地址、大小以及 Cleaner 对象的引用,ReferenceQueue 用于保存需要回收的 Cleaner 对象。

image

 当发生 GC 时,DirectByteBuffer 对象被回收,内存中的对象引用情况发生了如下变化:

 

image

 

此时 Cleaner 对象不再有任何引用关系,在下一次 GC 时,该 Cleaner 对象将被添加到 ReferenceQueue 中,并执行 clean() 方法。clean() 方法主要做两件事情:

  1. 将 Cleaner 对象从 Cleaner 链表中移除;
  2. 调用 unsafe.freeMemory 方法清理堆外内存。

至此,堆外内存的回收已经介绍完了,下次再排查内存泄漏问题的时候先回顾下这些最基本的知识,做到心中有数。

总结

堆外内存是一把双刃剑,在网络 I/O、文件读写、分布式缓存等领域使用堆外内存都更加简单、高效,此外使用堆外内存不受 JVM 约束,可以避免 JVM GC 的压力,降低对业务应用的影响。当然天下没有免费的午餐,堆外内存也不能滥用,使用堆外内存你就需要关注内存回收问题,虽然 JVM 在一定程度上帮助我们实现了堆外内存的自动回收,但我们仍然需要培养类似 C/C++ 的分配/回收的意识,出现内存泄漏问题能够知道如何分析和处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1182790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2026充氮烘箱厂家推荐:技术与品质之选 - 品牌排行榜

充氮烘箱作为工业生产及科研领域中实现干燥、防氧化处理的关键设备,广泛应用于电子半导体、集成电路、生物医疗、新能源等精密制造场景。其通过向箱内充入氮气营造惰性环境,有效避免物料在高温处理过程中发生氧化反应…

AI驱动的牲畜寄生虫病自动诊断系统

基于人工智能的牲畜蠕虫寄生虫感染识别诊断系统 本研究提出了一种新颖的深度学习方法&#xff0c;旨在通过自动化识别牲畜寄生虫感染&#xff0c;解决印度东北丘陵地区兽医专业知识严重短缺的问题。开发了一种能够分析标准图像和显微图像的卷积神经网络架构&#xff0c;用于识别…

金山平台打造极致用户体验 - 博客万

在竞争激烈的金融服务市场,用户体验是平台核心竞争力的重要体现。金山 —— 黄金资产增值综合服务平台始终将用户体验放在首位,通过优化产品设计、提升服务质量、创新服务模式,为境内外客户提供极致的投资体验。平台…

艾体宝产品 | Redis 企业版 8.0.6 发布:迄今最快、最安全的版本

Redis 企业版 8.0.6 延续了致力于将 Redis 打造为企业级的最快、最可靠、最安全数据平台的承诺。此版本引入了一系列增强功能&#xff0c;在性能、安全性和可观测性方面为 Redis 运维者和开发者带来了显著提升。 现在&#xff0c;让我们深入了解一下 Redis 企业版 8.0.6 的新特…

如何让其他人协作开发的时候直接拉取项目就能运行? - link

用这个插件可以不上传.idea和.gradle,这些配置文件。

Gitee项目管理软件:中国开发者生态的数字化基石

Gitee项目管理软件&#xff1a;中国开发者生态的数字化基石 数字化转型浪潮席卷全球之际&#xff0c;项目管理软件作为企业效率提升的核心工具&#xff0c;正迎来前所未有的发展机遇。2025年最新市场评测数据显示&#xff0c;Gitee&#xff08;码云&#xff09;凭借其本土化优势…

2026年值得关注的5家工单系统厂商推荐:技术原理与应用价值解析 - 品牌2026

在数字化转型加速推进的当下,企业服务场景日益复杂,客户咨询与业务需求的处理效率直接影响核心竞争力。工单系统作为整合多渠道需求、规范服务流程、提升协作效率的关键工具,已成为企业客服与运营体系的核心基础设施…

转行网络安全,这4个关键要点你必须掌握!

转行网络安全&#xff0c;这4个关键要点你必须掌握&#xff01; 我将以“3个转行成功vs失败的真实对比”为新视角&#xff0c;重构段落逻辑&#xff0c;用更生活化的表述拆解转行要点&#xff0c;同时保留核心信息与情感基调&#xff0c;大幅提升原创性。 想转行网络安全不踩…

LoRA微调秩大小优化实战

&#x1f493; 博客主页&#xff1a;借口的CSDN主页 ⏩ 文章专栏&#xff1a;《热点资讯》 LoRA微调秩大小优化&#xff1a;实战指南与前沿洞察目录LoRA微调秩大小优化&#xff1a;实战指南与前沿洞察 引言&#xff1a;为何秩大小是LoRA微调的“隐形关键” 一、秩大小&#xff…

浏览器资源嗅探神器:三步精准捕获全网视频资源

浏览器资源嗅探神器&#xff1a;三步精准捕获全网视频资源 【免费下载链接】cat-catch 猫抓 chrome资源嗅探扩展 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为网页视频无法保存而困扰吗&#xff1f;猫抓扩展作为一款高效的浏览器资源嗅探工具&am…

2026真空干燥箱厂家哪家靠谱?行业实力企业推荐 - 品牌排行榜

真空干燥箱作为一种利用真空环境进行干燥处理的设备,广泛应用于电子半导体、生物医疗、新能源、科研院校等领域,其性能直接影响实验结果与生产质量。选择技术可靠、产品稳定的厂家,是保障工艺效果的关键。一、推荐榜…

OpCore Simplify:黑苹果EFI自动化工具完全指南

OpCore Simplify&#xff1a;黑苹果EFI自动化工具完全指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为复杂的OpenCore配置而烦恼吗&#xf…

当读文件时,另一个进程把文件长度置0,会发生......

读进程: cat r.c #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <string.h>int main() {int fd = open("test.txt", O_RDONLY);if (fd == -1) { perror(&q…

Python字节码逆向解密:pycdc工具从入门到实战完整指南

Python字节码逆向解密&#xff1a;pycdc工具从入门到实战完整指南 【免费下载链接】pycdc C python bytecode disassembler and decompiler 项目地址: https://gitcode.com/GitHub_Trending/py/pycdc 你是否曾经面对一个.pyc文件&#xff0c;却无法看到其中的源代码&…

基于python和vue的企业门户网站的设计与实现

目录企业门户网站的设计与实现摘要开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;企业门户网站的设计与实现摘要 基于Python和Vue的企业门户网站设计旨在构建一个高效、响应式且功能完善的…

信息技术 数字孪生 第2部分:数字实体

信息技术 数字孪生 第2部分:数字实体000011 范围 本文件描述了数字孪生系统中数字实体的构成要素,规定了数字实体的构建过程和数字实体管理等相关要求。 本文件适用于数字孪生技术或相关产品的研发和应用。 000012 …

Soundflower虚拟音频路由终极指南:从零到精通

Soundflower虚拟音频路由终极指南&#xff1a;从零到精通 【免费下载链接】Soundflower MacOS system extension that allows applications to pass audio to other applications. 项目地址: https://gitcode.com/gh_mirrors/sou/Soundflower 虚拟音频路由技术正在彻底改…

Python+django的招聘求职人才信息管理系统设计与实现可视化 vue

目录系统架构设计核心功能模块可视化实现技术创新点开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;系统架构设计 采用前后端分离架构&#xff0c;后端使用PythonDjango框架提供RESTful API…

智能时代,如何选择一家卓越的呼叫中心合作伙伴? - 品牌2026

在数字化浪潮席卷各行各业的今天,客户联络已成为企业运营的核心环节。无论是产品咨询、售后服务,还是市场拓展与客户维系,一个高效、稳定、智能的客户联络体系都是企业提升竞争力、优化客户体验的关键。然而,面对市…

基于python和vue的在线考试管理系统的设计与实现前台329fgzk

目录设计与实现概述技术架构核心功能模块创新与优化应用价值开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;设计与实现概述 基于Python和Vue的在线考试管理系统旨在提供高效、安全的考试管…