庖丁解牛:NIO核心概念与机制详解 03 _ 缓冲区分配、包装和分片
文章目录
- Pre
- 概述
- 缓冲区分配和包装 (allocate 、 wrap)
- 缓冲区分片 (slice)
- 缓冲区份片和数据共享
- 只读缓冲区 (asReadOnlyBuffer)
- 直接和间接缓冲区 (allocateDirect)
- 内存映射文件 I/O
- 将文件映射到内存(map)

Pre
庖丁解牛:NIO核心概念与机制详解 01
庖丁解牛:NIO核心概念与机制详解 02 _ 缓冲区的细节实现
概述
到目前为止,我们已经使用缓冲区进行日常工作所需要掌握的大部分内容。例子没怎么超出标准的读/写过程种类,在原来的 I/O 中可以像在 NIO 中一样容易地实现这样的标准读写过程。
这里我们将讨论使用缓冲区的一些更复杂的方面,比如缓冲区分配、包装和分片。我们还会讨论 NIO 带给 Java 平台的一些新功能。
这里我们可以看到
- 如何创建不同类型的缓冲区以达到不同的目的,
- 如可保护数据不被修改的 只读 缓冲区,和
- 直接映射到底层操作系统缓冲区的 直接 缓冲区。
- 最后介绍如何在 NIO 中创建内存映射文件。

缓冲区分配和包装 (allocate 、 wrap)
在能够读和写之前,必须有一个缓冲区。要创建缓冲区,您必须 分配 它。我们使用静态方法 allocate() 来分配缓冲区:
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
allocate() 方法分配一个具有指定大小的底层数组,并将它包装到一个缓冲区对象中 , 在本例中是一个 ByteBuffer。
还可以将一个现有的数组转换为缓冲区,如下所示:
byte array[] = new byte[1024];
ByteBuffer buffer = ByteBuffer.wrap( array );
]
本例使用了 wrap() 方法将一个数组包装为缓冲区。必须非常小心地进行这类操作。一旦完成包装,底层数据就可以通过缓冲区或者直接访问。
完整Demo
import java.io.*;
import java.nio.*;
import java.nio.channels.*;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/
public class CreateBuffer
{static public void main( String args[] ) throws Exception {ByteBuffer buffer = ByteBuffer.allocate( 1024 );buffer.put( (byte)'a' );buffer.put( (byte)'b' );buffer.put( (byte)'c' );buffer.flip();System.out.println( (char)buffer.get() );System.out.println( (char)buffer.get() );System.out.println( (char)buffer.get() );}
}
import java.io.*;
import java.nio.*;
import java.nio.channels.*;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/
public class CreateArrayBuffer
{static public void main( String args[] ) throws Exception {byte array[] = new byte[1024];ByteBuffer buffer = ByteBuffer.wrap( array );buffer.put( (byte)'a' );buffer.put( (byte)'b' );buffer.put( (byte)'c' );buffer.flip();System.out.println( (char)buffer.get() );System.out.println( (char)buffer.get() );System.out.println( (char)buffer.get() );}
}
缓冲区分片 (slice)
slice() 方法根据现有的缓冲区创建一种 子缓冲区 。也就是说,它创建一个新的缓冲区,新缓冲区与原来的缓冲区的一部分共享数据。
使用例子可以最好地说明这点。让我们首先创建一个长度为 10 的 ByteBuffer:
ByteBuffer buffer = ByteBuffer.allocate( 10 );
然后使用数据来填充这个缓冲区,在第 n 个槽中放入数字 n:
for (int i=0; i<buffer.capacity(); ++i) {buffer.put( (byte)i );
}
现在我们对这个缓冲区 分片 ,以创建一个包含槽 3 到槽 6 的子缓冲区。在某种意义上,子缓冲区就像原来的缓冲区中的一个 窗口 。
窗口的起始和结束位置通过设置 position 和 limit 值来指定,然后调用 Buffer 的 slice() 方法:
buffer.position( 3 );
buffer.limit( 7 );
ByteBuffer slice = buffer.slice();
片 是缓冲区的 子缓冲区 。不过, 片段 和 缓冲区 共享同一个底层数据数组 。
缓冲区份片和数据共享
我们已经创建了原缓冲区的子缓冲区,并且我们知道缓冲区和子缓冲区共享同一个底层数据数组。让我们看看这意味着什么。
我们遍历子缓冲区,将每一个元素乘以 11 来改变它。例如,5 会变成 55。
for (int i=0; i<slice.capacity(); ++i) {byte b = slice.get( i );b *= 11;slice.put( i, b );
}
最后,再看一下原缓冲区中的内容:
buffer.position( 0 );
buffer.limit( buffer.capacity() );while (buffer.remaining()>0) {System.out.println( buffer.get() );
}
结果表明只有在子缓冲区窗口中的元素被改变了:
$ java SliceBuffer
0
1
2
33
44
55
66
7
8
9
缓冲区片对于促进抽象非常有帮助。可以编写自己的函数处理整个缓冲区,而且如果想要将这个过程应用于子缓冲区上,只需取主缓冲区的一个片,并将它传递给你的函数。这比编写自己的函数来取额外的参数以指定要对缓冲区的哪一部分进行操作更容易。
完整Demo
import java.io.*;
import java.nio.*;
import java.nio.channels.*;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/
public class SliceBuffer
{static public void main( String args[] ) throws Exception {ByteBuffer buffer = ByteBuffer.allocate( 10 );for (int i=0; i<buffer.capacity(); ++i) {buffer.put( (byte)i );}buffer.position( 3 );buffer.limit( 7 );ByteBuffer slice = buffer.slice();for (int i=0; i<slice.capacity(); ++i) {byte b = slice.get( i );b *= 11;slice.put( i, b );}buffer.position( 0 );buffer.limit( buffer.capacity() );while (buffer.remaining()>0) {System.out.println( buffer.get() );}}
}
只读缓冲区 (asReadOnlyBuffer)
只读缓冲区非常简单 ― 可以读取它们,但是不能向它们写入。
可以通过调用缓冲区的 asReadOnlyBuffer() 方法,将任何常规缓冲区转换为只读缓冲区,这个方法返回一个与原缓冲区完全相同的缓冲区(并与其共享数据),只不过它是只读的。
只读缓冲区对于保护数据很有用。在将缓冲区传递给某个对象的方法时,无法知道这个方法是否会修改缓冲区中的数据。创建一个只读的缓冲区可以 保证 该缓冲区不会被修改。
不能将只读的缓冲区转换为可写的缓冲区。
直接和间接缓冲区 (allocateDirect)
另一种有用的 ByteBuffer 是直接缓冲区。 直接缓冲区 是为加快 I/O 速度,而以一种特殊的方式分配其内存的缓冲区。
实际上,直接缓冲区的准确定义是与实现相关的。
Oracle 的文档是这样描述直接缓冲区的:
给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接对它执行本机 I/O 操作。也就是说,它会在每一次调用底层操作系统的本机 I/O 操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中(或者从一个中间缓冲区中拷贝数据)。
import java.io.*;
import java.nio.*;
import java.nio.channels.*;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/
public class FastCopyFile
{static public void main( String args[] ) throws Exception {if (args.length<2) {System.err.println( "Usage: java FastCopyFile infile outfile" );System.exit( 1 );}String infile = args[0];String outfile = args[1];FileInputStream fin = new FileInputStream( infile );FileOutputStream fout = new FileOutputStream( outfile );FileChannel fcin = fin.getChannel();FileChannel fcout = fout.getChannel();ByteBuffer buffer = ByteBuffer.allocateDirect( 1024 );while (true) {buffer.clear();int r = fcin.read( buffer );if (r==-1) {break;}buffer.flip();fcout.write( buffer );}}
}
直接缓冲区的实际应用,这个程序是 CopyFile.java 的另一个版本,它使用了直接缓冲区以提高速度。
还可以用内存映射文件创建直接缓冲区。
内存映射文件 I/O
内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。
内存映射文件 I/O 是通过使文件中的数据神奇般地出现为内存数组的内容来完成的。这其初听起来似乎不过就是将整个文件读到内存中,但是事实上并不是这样。一般来说,只有文件中实际读取或者写入的部分才会送入(或者 映射 )到内存中。
内存映射并不真的神奇或者多么不寻常。现代操作系统一般根据需要将文件的部分映射为内存的部分,从而实现文件系统。Java 内存映射机制不过是在底层操作系统中可以采用这种机制时,提供了对该机制的访问。
尽管创建内存映射文件相当简单,但是向它写入可能是危险的。仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。
将文件映射到内存(map)
了解内存映射的最好方法是使用例子。在下面的例子中,我们要将一个 FileChannel (它的全部或者部分)映射到内存中。为此我们将使用 FileChannel.map() 方法。
下面代码行将文件的前 1024 个字节映射到内存中:
MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE, 0, 1024 );
map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行行映射。
完整Demo
import java.io.*;
import java.nio.*;
import java.nio.channels.*;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world*/
public class UseMappedFile
{static private final int start = 0;static private final int size = 1024;static public void main( String args[] ) throws Exception {RandomAccessFile raf = new RandomAccessFile( "usemappedfile.txt", "rw" );FileChannel fc = raf.getChannel();MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE, start, size );mbb.put( 0, (byte)97 );mbb.put( 1023, (byte)122 );raf.close();}
}

相关文章:
庖丁解牛:NIO核心概念与机制详解 03 _ 缓冲区分配、包装和分片
文章目录 Pre概述缓冲区分配和包装 (allocate 、 wrap)缓冲区分片 (slice)缓冲区份片和数据共享只读缓冲区 (asReadOnlyBuffer)直接和间接缓冲区 (allocateDirect)内存映射文件 I/O将文件映射到内存(map) Pre 庖丁解牛࿱…...
002 OpenCV dft 傅里叶变换
目录 一、傅里叶变换 1.1 傅里叶变换概念 1.2 opencv中傅里叶变换 二、实验代码 一、环境 本文使用环境为: Windows10Python 3.9.17opencv-python 4.8.0.74 二、傅里叶变换 2.1 傅里叶变换概念 傅里叶变换(Fourier Transform)是一种…...
力扣:171. Excel 表列序号(Python3)
题目: 给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如: A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... 来源:力扣(LeetCode) …...
C++中结构体的初始化
C中结构体的初始化 结构体是一个由程序员定义的数据类型,可以容纳许多不同的数据值。在过去,面向对象编程的应用尚未普及之前,程序员通常使用这些从逻辑上连接在一起的数据组合到一个单元中。一旦结构体类型被声明并且其数据成员被标识&…...
vue3+vite+ts 发布自定义组件到npm
vue3vite 发布自定义组件到npm 初始化项目编写组件配置打包组件上传到npm测试组件库 初始化项目 // 创建项目 pnpm create vite vue-test-app --template vue-ts// 运行项目 cd vite vue-test-app pnpm install pnpm run dev编写组件 1、根目录下创建packages目录作为组件的开…...
mybatis使用xml形式配置
以这个注解形式的查询代码为例 Select("select * from emp where name like concat(%,#{name},%) and gender #{gender} and entrydate between #{begin} and #{end} order by update_time desc ")public List<Emp> list(String name, Short gender, LocalDat…...
开源简历生成器OpenResume
什么是 OpenResume ? OpenResume 是一个功能强大的开源简历生成器和简历解析器。OpenResume 的目标是为每个人提供免费的现代专业简历设计,让任何人都能充满信心地申请工作。 OpenResume 有 5 个核心特点: 特征描述1. 实时UI更新当您输入简历…...
AI变现之Gpts搞流量+赚钱
文章目录 Gpts | 搞流量 + 赚钱1.项目介绍2.项目分析3.项目实操4.变现路径Gpts | 搞流量 + 赚钱 1.项目介绍 这两天 AI 圈最火的莫过于 OpenAI 开发者大会公布的一个爆炸产品 Gpts 了,大家都知道这个肯定是一个划时代的产品,也绝对是一个风口,虽然官方还没有公布到底怎么通…...
音视频项目—基于FFmpeg和SDL的音视频播放器解析(十六)
介绍 在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器 如果您不理解本…...
Elasticsearch文档操作
一、Elasticsearch的CURD 1、CURD之Create PUT lqz/doc/1 {"name":"顾老二","age":30,"from": "gu","desc": "皮肤黑、武器长、性格直","tags": ["黑", "长", "直…...
聊一聊go的单元测试(goconvey、gomonkey、gomock)
文章目录 概要一、测试框架1.1、testing1.2、stretchr/testify1.3、smartystreets/goconvey1.4、cweill/gotests 二、打桩和mock2.1、打桩2.2、mock2.2.1、mockgen2.2.1、示例 三、基准测试和模糊测试3.1、基准测试3.2、模糊测试 四、总结4.1、小结4.2、其他4.3、参考资料 概要…...
Positive Technologies 利用 PT Cloud Application Firewall 保护中小型企业的网络资源
云产品按月订购,无需购买硬件资源 PT Cloud Application Firewall 是 Positive Technologies 推出的首个用于保护网络应用程序的商用云产品。Web 应用层防火墙 (web application firewall, WAF) 现在可以通过 技术合作伙伴——授权服务商和云提供商以订购方式提供1…...
深入解析序列模型:全面阐释 RNN、LSTM 与 Seq2Seq 的秘密
探索序列建模的基础知识和应用。 简介 序列建模是许多领域的一个重要问题,包括自然语言处理 (NLP)、语音识别和语音合成、时间序列预测、音乐生成和「生物信息学」。所有这些任务的共同点是它们需要坚持。接下来的事情的预测是基于历史的。例如,在“哈桑…...
vue项目本地开发构建速度优化 hard-source-webpack-plugin
1、为啥要优化本地构建速度 有些项目因为项目需求点多、功能复杂、管理混乱、引入第三方插件/样式库过多、本身项目页面较多、文件较多等等原因,会导致项目体积变大、本地构建速度明显变慢,这时就需要对项目webpack进行一些设置来提高打包效率、加快打包…...
燕之屋通过港交所聆讯:苦战IPO十余年,黄健等人提前精准套现
撰稿|行星 来源|贝多财经 11月19日,厦门燕之屋生物工程股份有限公司(下称“燕之屋”)通过港交所聆讯,并披露了聆讯后资料集(即招股书),中金公司和广发证券为其联席保荐人。 据贝多财经了解&a…...
【51单片机系列】C51基础
本文内容是关于C51语言的基础内容的,包括C51的数据类型、变量、运算符、函数以及reg52.h文件中的内容,有些与C中相同的内容没有记录在此,比如常量、某些变量、表达式、程序结构、数组等没有涉及。 文章目录 C51的数据类型1. C51中的基本数据类…...
openssl1.0.2版本Windows安装问题
之前安装过1.1版本,Windows环境下C 安装OpenSSL库 源码编译及使用(VS2019)_vs2019安装openssl_肥宝Fable的博客-CSDN博客 后来发现linux编译不过,以为是版本问题,相差太大,所以降一下版本,以免…...
【Java 进阶篇】Ajax 实现——原生JS方式
大家好,欢迎来到这篇关于原生 JavaScript 中使用 Ajax 实现的博客!在前端开发中,我们经常需要与服务器进行数据交互,而 Ajax(Asynchronous JavaScript and XML)是一种用于创建异步请求的技术,它…...
Spring Cloud Stream实践
概述 不同中间件,有各自的使用方法,代码也不一样。 可以使用Spring Cloud Stream解耦,切换中间件时,不需要修改代码。实现方式为使用绑定层,绑定层对生产者和消费者提供统一的编码方式,需要连接不同的中间…...
高精度算法【Java】(待更新中~)
高进度加法 在Java中可以使用BigInteger进行高精度计算,除此也可以仿照竖式相加的计算原理进行计算。 BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算࿱…...
3个核心模块揭秘:Python量化投资如何免费获取通达信专业数据
3个核心模块揭秘:Python量化投资如何免费获取通达信专业数据 【免费下载链接】mootdx 通达信数据读取的一个简便使用封装 项目地址: https://gitcode.com/GitHub_Trending/mo/mootdx 你是否在量化投资中为数据获取而烦恼?商业接口太贵,…...
从零搭建PointRCNN:Linux环境配置与3D检测可视化实战
1. 环境准备:从零搭建Linux深度学习工作站 第一次在Linux系统上配置深度学习环境时,我盯着命令行界面手足无措的样子还历历在目。现在回想起来,其实只要掌握几个关键步骤,就能快速搭建好PointRCNN所需的运行环境。我们以配备NVIDI…...
Python 3.14 JIT编译器性能调优,深度解析_pyltopt.c中6处可调优位点与GCC/Clang后端适配策略
第一章:Python 3.14 JIT编译器性能调优概览Python 3.14 引入了实验性内置 JIT(Just-In-Time)编译器,基于 LLVM 后端实现,旨在对热点函数进行动态编译优化,显著提升数值计算、循环密集型及递归场景的执行效率…...
基于 LlamaFactory 与 LoRA 微调开源大模型:构建高效文本分类系统的实践指南
1. 为什么选择LlamaFactoryLoRA做文本分类? 最近在做一个政务工单分类项目时,我发现传统BERT模型遇到三个头疼问题:标注成本高(需要上万条数据)、领域迁移难(换个场景就失效)、小样本表现差&…...
万物识别镜像高级功能探索:除了基础识别,还能做什么?
万物识别镜像高级功能探索:除了基础识别,还能做什么? 1. 万物识别镜像的隐藏潜力 大多数人使用万物识别镜像时,只停留在基础识别功能上——上传图片,获取识别结果。但这款基于cv_resnest101_general_recognition算法…...
2026年AI Agent将迎来爆发!这五大趋势将重塑企业未来,你准备好了吗?
2026年AI Agent将进入规模化部署阶段,应用渗透率将大幅提升。文章分析了五大核心趋势:多智能体协同、企业级部署规模化、行业垂直化、可信性与透明度提升,以及人机协作模式重构。同时,文章也提醒企业需警惕项目失败风险࿰…...
Go Module 依赖冲突调试方法
Go Module 依赖冲突调试方法 在Go语言开发中,依赖管理是一个关键环节。随着项目规模的扩大,依赖的第三方库越来越多,版本冲突问题也愈发常见。Go Module作为官方推荐的依赖管理工具,虽然简化了依赖管理流程,但在多级依…...
Ubuntu 22.04 改IP重启失效?别急,可能是OVS的ovsdb-server在捣鬼
Ubuntu 22.04网络配置失效:当OVS与netplan的隐秘博弈 在虚拟化技术大行其道的今天,Open vSwitch(OVS)作为开源虚拟交换机的标杆,已经成为众多云计算平台和容器网络的核心组件。然而,当它遇上Ubuntu 22.04默…...
南北阁模型新玩法:一键部署极简WebUI,体验手机短信般AI对话
南北阁模型新玩法:一键部署极简WebUI,体验手机短信般AI对话 还在用那些界面老旧、反应迟钝的AI对话工具吗?每次发送问题后,只能盯着屏幕上的加载图标干等,几秒甚至十几秒后才能看到一大段文字“啪”地一下弹出来&…...
UV固化三防漆好用吗?光固化速度与设备要求
UV固化三防漆好用吗?光固化速度与设备要求高效快速的固化优势 UV固化三防漆(也称紫外光固化保形涂层)是一种专为印刷电路板(PCB)设计的保护材料,通过紫外光照射触发光引发剂瞬间聚合,实现快速固…...
