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

Java NIO Buffer概念

针对每一种基本类型的 Buffer ,NIO 又根据 Buffer 背后的数据存储内存不同分为了:HeapBuffer,DirectBuffer,MappedBuffer。

HeapBuffer 顾名思义它背后的存储内存是在 JVM 堆中分配,在堆中分配一个数组用来存放 Buffer 中的数据。

public abstract class ByteBufferextends Bufferimplements Comparable<ByteBuffer>
{// Cached array base offsetprivate static final long ARRAY_BASE_OFFSET = UNSAFE.arrayBaseOffset(byte[].class);// These fields are declared here rather than in Heap-X-Buffer in order to// reduce the number of virtual method invocations needed to access these// values, which is especially costly when coding small buffers.//// 在堆中使用一个数组存放Buffer数据final byte[] hb;                  // Non-null only for heap buffers
}

DirectBuffer 背后的存储内存是在堆外内存中分配,MappedBuffer 是通过内存文件映射将文件中的内容直接映射到堆外内存中,其本质也是一个 DirectBuffer 。

由于 DirectBuffer 和 MappedBuffer 背后的存储内存是在堆外内存中分配,不受 JVM 管理,所以不能用一个 Java 基本类型的数组表示,而是直接记录这段堆外内存的起始地址。

public abstract class Buffer {...// Used by heap byte buffers or direct buffers with Unsafe access// For heap byte buffers this field will be the address relative to the// array base address and offset into that array. The address might// not align on a word boundary for slices, nor align at a long word// (8 byte) boundary for byte[] allocations on 32-bit systems.// For direct buffers it is the start address of the memory region. The// address might not align on a word boundary for slices, nor when created// using JNI, see NewDirectByteBuffer(void*, long).// Should ideally be declared final// NOTE: hoisted here for speed in JNI GetDirectBufferAddress// //堆外内存地址long address;
}

用户空间缓冲区:

以给文件写数据为例:

FileChannel fileChannel = new RandomAccessFile(new File("file-read-write.txt"), "rw").getChannel();
ByteBuffer  heapByteBuffer = ByteBuffer.allocate(4096);
fileChannel.write(heapByteBuffer);

sun.nio.ch.FileChannelImpl#write(java.nio.ByteBuffer)

在 IOUtil 中首先创建一个临时的 DirectByteBuffer,然后将 HeapByteBuffer 中的数据全部拷贝到这个临时的 DirectByteBuffer 中。这个 DirectByteBuffer 就是我们在 IO 系统调用中经常提到的用户空间缓冲区。

随后在 writeFromNativeBuffer 方法中通过  FileDispatcher 触发 JNI 层的 native 方法执行底层系统调用 write 。

而 JDK Buffer 也会根据其背后所依赖的虚拟内存在进程虚拟内存空间中具体所属的虚拟内存区域而演变出 HeapByteBuffer , MappedByteBuffer , DirectByteBuffer 。这三种不同类型 ByteBuffer 的本质区别就是其背后依赖的虚拟内存在 JVM 进程虚拟内存空间中的布局位置不同。

JVM 在操作系统的视角来看其实就是一个普通的进程,内核会根据进程在运行期间所需数据的功能特性不同,而为每一类数据专门开辟出一段虚拟内存区域出来。

位于 JVM 堆之外的内存其实都可以归属到 DirectByteBuffer 的范畴中。比如,位于 OS 堆之内,JVM 堆之外的 MetaSpace,即时编译(JIT) 之后的 codecache,JVM 线程栈,Native 线程栈,JNI 相关的内存,等等。 

JVM  在 OS 堆中划分出的 Direct Memory (上图红色部分)特指受到参数 -XX:MaxDirectMemorySize 限制的直接内存区域,比如通过 ByteBuffer#allocateDirect 申请到的 Direct Memory 容量就会受到该参数的限制。

而通过 Unsafe#allocateMemory 申请到的 Direct Memory 容量则不会受任何 JVM 参数的限制,只会受操作系统本身对进程所使用内存容量的限制。也就是说 Unsafe 类会脱离 JVM 直接向操作系统进行内存申请。

HeapByteBuffer 底层依赖的字节数组背后的内存位于 JVM 堆中:

MappedByteBuffer 背后所占用的内存位于 JVM 进程虚拟内存空间中的文件映射与匿名映射区中,系统调用 mmap 映射出来的内存就是在这个区域中划分的。

JDK 仅仅只是对 mmap 文件映射方式进行了封装,所以 MappedByteBuffer 的本质其实是对文件映射与匿名映射区中某一段虚拟映射区域在 JVM 层面上的描述。这段虚拟映射区的起始内存地址 addr 以及映射长度 length 被封装在 MappedByteBuffer 中的 address , capacity 属性中:

mmap:

mmap 有两种映射方式,一种是匿名映射,常用于进程动态的向 OS 申请内存,比如,glibc 库里提供的用于动态申请内存的 malloc 函数,当申请的内存大于 128K 的时候,malloc 就会调用 mmap 采用匿名映射的方式来申请。

另一种就是文件映射,用于将磁盘文件中的某段区域与进程虚拟内存空间中文件映射与匿名映射区里的某段虚拟内存区域进行关联映射。后续我们针对这段映射内存的读写就相当于是对磁盘文件的读写了,整个读写过程没有数据的拷贝,也没有切态的发生(这里特指在完成缺页处理之后)。

当我们调用 mmap 之后,OS 内核只是会为我们分配一段虚拟内存,然后将虚拟内存与磁盘文件进行映射,整个过程都只是在和虚拟内存打交道,并未出现任何物理内存的身影。而这段虚拟内存在 Java 层面就是 MappedByteBuffer。

由于现在我们只是刚刚完成了文件映射,仅仅只是在 JVM 层面得到了一个 MappedByteBuffer,这个 MappedByteBuffer 背后所依赖的虚拟内存就是我们通过 mmap 映射出来的。

此时我们还未对文件进行读写操作,所以该映射文件对应的 page cache 里还是空,没有任何文件页(用于存储文件数据的物理内存页)。而虚拟内存(MappedByteBuffer)与物理内存之间的关联是通过进程页表来完成的,由于此时内核还未对 MappedByteBuffer 分配物理内存,所以 MappedByteBuffer 在 JVM 进程页表中对应的页表项 PTE 还是空的。

当我们开始访问这段 MappedByteBuffer 的时候, CPU 会将 MappedByteBuffer 背后的虚拟内存地址送到 MMU 地址翻译单元中进行地址翻译查找其背后的物理内存地址。

如果 MMU 发现 MappedByteBuffer 在 JVM 进程页表中对应的页表项 PTE 还是空的,这说明 MappedByteBuffer 是刚刚被 mmap 系统调用映射出来的,还没有分配物理内存。

于是 MMU 就会产生缺页中断,随后 JVM  进程切入到内核态,进行缺页处理,为 MappedByteBuffer 分配物理内存。

内核在 do_fault 函数中处理 MappedByteBuffer 缺页的时候,首先会调用 find_get_page 从映射文件的 page cache 中尝试获取文件页,前面已经说了,当 MappedByteBuffer 刚刚被映射出来的时候,映射文件的 page cache 还是空的,没有缓存任何文件页,需要映射到内存的文件内容此时还静静地躺在磁盘上。

当文件页不在 page cache 中,内核则会调用 do_sync_mmap_readahead 来同步预读,这里首先会分配一个物理内存页出来,然后将新分配的内存页加入到 page cache 中,并增加页引用计数。

如果文件页已经缓存在 page cache 中了,则调用 do_async_mmap_readahead 启动异步预读机制,将相邻的若干文件页一起预读进 page cache 中。

随后会通过 address_space_operations (page cache 相关的操作函数集合)中定义的 readpage 激活块设备驱动从磁盘中读取映射的文件内容并填充到 page cache 里的文件页中。

经过 filemap_fault 函数的处理,此时 MappedByteBuffer 背后所映射的文件内容已经加载到 page cache 中了。

后续 JVM 进程在访问这段 MappedByteBuffer 的时候就相当于是直接访问映射文件的 page cache。整个过程是在用户态进行,不需要切态。

不需要切态说明:

后面 JVM  进程对 MappedByteBuffer 的读写就相当于是直接读写 page cache 了,关于这一点,很多读者朋友会有这样的疑问:page cache 是内核态的部分,为什么我们通过用户态的 MappedByteBuffer 就可以直接访问内核态的东西了?

这里大家不要被内核态这三个字给唬住了,虽然 page cache 是属于内核部分的,但其本质上还是一块普通的物理内存,想想我们是怎么访问内存的 ? 不就是先有一段虚拟内存,然后在申请一段物理内存,最后通过进程页表将虚拟内存和物理内存映射起来么,进程在访问虚拟内存的时候,通过页表找到其映射的物理内存地址,然后直接通过物理内存地址访问物理内存。

回到我们讨论的内容中,这段虚拟内存不就是 MappedByteBuffer 吗,物理内存就是 page cache 啊,在通过页表映射起来之后,进程在通过 MappedByteBuffer 访问 page cache 的过程就和访问普通内存的过程是一模一样的。

也正因为 MappedByteBuffer 背后映射的物理内存是内核空间的 page cache,所以它不会消耗任何用户空间的物理内存(JVM  的堆外内存),因此也不会受到 -XX:MaxDirectMemorySize 参数的限制

参考:从 Linux 内核角度探秘 JDK NIO 文件读写本质

从 Linux 内核角度探秘 JDK MappedByteBuffer

相关文章:

Java NIO Buffer概念

针对每一种基本类型的 Buffer &#xff0c;NIO 又根据 Buffer 背后的数据存储内存不同分为了&#xff1a;HeapBuffer&#xff0c;DirectBuffer&#xff0c;MappedBuffer。 HeapBuffer 顾名思义它背后的存储内存是在 JVM 堆中分配&#xff0c;在堆中分配一个数组用来存放 Buffe…...

Kubernetes在Java应用部署中的最佳实践

Kubernetes在Java应用部署中的最佳实践 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何在Java应用程序中使用Kubernetes进行最佳部署实践。K…...

IOS Swift 从入门到精通:@escaping 和PreferenceKey

@escaping 在Swift中,@escaping是一个属性关键字,用于标记闭包参数。当一个闭包在函数返回之后才被调用时,这个闭包被称为逃逸闭包(Escaping Closure)。使用@escaping关键字可以告诉Swift编译器,传递给函数的闭包可能会在函数执行完毕后被调用,因此它需要“逃逸”函数的…...

基于PHP技术的校园论坛设计的设计与实现-计算机毕业设计源码08586

摘 要 本项目旨在基于PHP技术设计与实现一个校园论坛系统&#xff0c;以提供一个功能丰富、用户友好的交流平台。该论坛系统将包括用户注册与登录、帖子发布与回复、个人信息管理等基本功能&#xff0c;并结合社交化特点&#xff0c;增强用户之间的互动性。通过利用PHP语言及其…...

开机弹窗缺失OpenCL.dll如何解决?分享5种靠谱的解决方法

在电脑使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“开机提示找不到OpenCL.dll”。那么&#xff0c;这个错误提示到底是怎么回事呢&#xff1f;它又对电脑有什么影响&#xff1f;我们又该如何解决这个问题并预防OpenCL.dll再次丢失呢&#xff1…...

IIS 服务器安装SSL证书

IIS 服务器安装SSL证书 步骤一&#xff1a;准备好 SSL 证书 准备好.pfx 格式的证书文件。 步骤二&#xff1a;安装 SSL 证书 1、打开【开始】菜单&#xff0c;找到【管理工具】&#xff0c;打开【Internet 信息服务&#xff08;IIS&#xff09;管理器】。 2、单击服务器名…...

二叉树第二期:堆的实现与应用

若对树与二叉树的相关概念&#xff0c;不太熟悉的同学&#xff0c;可移置上一期博客 链接&#xff1a;二叉树第一期&#xff1a;树与二叉树的概念-CSDN博客 本博客目标&#xff1a;对二叉树的顺序结构&#xff0c;进行深入且具体的讲解&#xff0c;同时学习二叉树顺序结构的应用…...

python-求出 e 的值

[题目描述] 利用公式 e11/1!1/2!1/3!⋯1/&#x1d45b;!&#xff0c;求 e 的值&#xff0c;要求保留小数点后 10 位。输入&#xff1a; 输入只有一行&#xff0c;该行包含一个整数 n&#xff0c;表示计算 e 时累加到1/n!。输出&#xff1a; 输出只有一行&#xff0c;该行包含计…...

模型微调方法

文章目录 LoRADoRAMoRA 以下部分参考自: https://mp.weixin.qq.com/s/OxYNpXcyHF57OShQC26n4g LoRA LoRA是微软于2021年推出的一种经济型微调模型参数的方法。 它在冻结大部分的模型参数的情况下&#xff0c;仅仅更新额外的部分参数。其性能与全参数微调相似。 LoRA假设微调期间…...

cesium使用cesium-navigation-es6插件创建指南针比例尺

cesium-navigation-es6 是一个为 Cesium.js 提供导航控件的库&#xff0c;它提供了一些常见的用户界面组件&#xff0c;用于在 Cesium 场景中实现用户导航和交互。下面将介绍如何在项目中使用 cesium-navigation-es6。 使用步骤 1. 安装 cesium-navigation-es6 首先&#xf…...

go sync包(七)Sync.Map

Sync.Map 原理 通过 read 和 dirty 两个字段实现数据的读写分离&#xff0c;读的数据存在只读字段 read 上&#xff0c;将最新写入的数据存在 dirty 字段上。读取时会先查询 read&#xff0c;不存在再查询 dirty&#xff0c;写入时则只写入 dirty。读取 read 并不需要加锁&am…...

Batch文件中的goto命令:控制流程的艺术

Batch文件&#xff0c;也称为批处理脚本&#xff0c;是Windows操作系统中用于自动化任务的一种脚本文件。在Batch脚本中&#xff0c;goto命令是一个至关重要的控制结构&#xff0c;它允许脚本跳转到指定的标签位置&#xff0c;从而实现循环、条件分支等复杂的控制流程。本文将详…...

【chatgpt】两层gcn提取最后一层节点输出特征,如何自定义简单数据集

文章目录 两层gcn&#xff0c;提取最后一层节点输出特征&#xff0c;10个节点&#xff0c;每个节点8个特征&#xff0c;连接关系随机生成&#xff08;无全连接层&#xff09;如何计算MSE 100个样本&#xff0c;并且使用批量大小为32进行训练第一个版本定义数据集出错&#xff0…...

Java面试题:讨论你如何保持对Java生态系统中新技术的了解

保持对Java生态系统中新技术的了解可以通过以下几种方法&#xff1a; 官方资源&#xff1a; Oracle的官方博客和新闻&#xff1a;Oracle是Java的主要维护者&#xff0c;其官方网站和博客会定期发布Java的新版本、功能更新和最佳实践。Java SE Documentation&#xff1a;Java官方…...

深度学习之Transformer模型的Vision Transformer(ViT)和Swin Transformer

Transformer 模型最初由 Vaswani 等人在 2017 年提出,是一种基于自注意力机制的深度学习模型。它在自然语言处理(NLP)领域取得了巨大成功,并且也逐渐被应用到计算机视觉任务中。以下是两种在计算机视觉领域中非常重要的 Transformer 模型:Vision Transformer(ViT)和 Swi…...

玩个游戏 找以下2个wordpress外贸主题的不同 你几找到几处

Aitken艾特肯wordpress外贸主题 适合中国产品出海的蓝色风格wordpress外贸主题&#xff0c;产品多图展示、可自定义显示产品详细参数。 https://www.jianzhanpress.com/?p7060 Ultra奥创工业装备公司wordpress主题 蓝色风格wordpress主题&#xff0c;适合装备制造、工业设备…...

React Native优质开源项目推荐与解析

目录 2. React Native的优势 2.1. 跨平台开发 2.2. 热更新 2.3. 丰富的社区资源 2.4. 优秀的性能 3. 优质开源项目推荐 3.1. React Navigation 3.1.1 项目简介 3.1.2 特点和优势 3.1.3 应用场景 3.2. Redux 3.2.1 项目简介 3.2.2 特点和优势 3.2.3 应用场景 3.3…...

树莓派安装windows系统

第1步&#xff1a; https://uupdump.net/下载对应的系统文件&#xff0c;所有选择项都默认选择。 第2步&#xff1a; 解压下载后的文件&#xff0c;双击运行下面文件。等待下载完成&#xff0c;等待过程很漫长&#xff0c;很考验耐心。 第3步&#xff1a; 提示已经finish了&…...

CSS-position/transform

1 需求 2 语法 在CSS中&#xff0c;positioning 和 transform 是两个非常重要的概念&#xff0c;它们分别用于控制元素在页面上的布局和变换。 Positioning CSS中的position属性用于设置元素的定位类型。它有几个值&#xff0c;包括&#xff1a; static&#xff1a;这是默认…...

面试题之一

路由的两种模式&#xff1a;hash模式和 history模式。 两种的区别、如何实现。 hash模式中#的作用 vue性能优化。具体如何实现&#xff08;回答了一个可以函数引入的方法引入路由。问&#xff09; keep-alive 说一下EventBus CSS&#xff1a; flex布局 css新特性 盒子模型 J…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...