首页 > 编程语言 >JAVA NIO零拷贝实现

JAVA NIO零拷贝实现

时间:2024-12-03 17:59:19浏览次数:6  
标签:文件 JAVA NIO 映射 URL URI 内存 缓冲区 拷贝

文章目录

JAVA NIO零拷贝实现

零拷贝通常通常指网络发送文件时,不需要把文件内容拷贝到用户空间,而直接在内核空间中传输到网络方式。通过减少不同内存区域的拷贝,减低CPU的消耗。

java NIO中**通道相当于操作系统的内核空间的缓冲区**,而**缓冲区对应的相当于操作系统的用户空间的用户缓冲区**。 
  • 通道是全双工的(双向运输),它即可能是读缓冲区,也可能是网络缓冲区。

  • 缓冲区分为堆内存和堆外内存,这是通过malloc()分配出来的用户态内存。

java NIO中的SocketChannel和ServerSocketChannel类在实现网络通信时,通常会使用HeadBuffer来读取和解析数据包头部信息。在网络通信中,数据往往是分成若干个数据包进行传输,每个数据包都包含了头部信息和数据内容。

ByteBuffer headBuffer = ByteBuffer.allocate(4); // 创建一个长度为4的HeadBuffer
socketChannel.read(headBuffer); // 读取HeadBuffer中的数据

headBuffer.flip(); // 将HeadBuffer切换为读模式
int packetLength = headBuffer.getInt(); // 从HeadBuffer中读取数据包长度

ByteBuffer dataBuffer = ByteBuffer.allocate(packetLength); // 创建一个长度为packetLength的数据缓冲区
socketChannel.read(dataBuffer); // 从SocketChannel中读取剩余的数据内容

JDK8及之前使用替身缓冲区,在使用HeadBuffer读写数据时,为了避免缓冲区数据因为GC而丢失,NIO会先把HeadBuffer内部的数据拷贝到一个临时DirectBuffer中的本地内存,通过Unsafe类的copyMemory()调用,类似memcpy()。然后将临时生成的DirectBuffer内部的数据内存地址传给I/O调用函数,避免了再去访问Java对象处理I/O读写。

JDK9及之后的版本,JDK使用共享缓冲区的机制避免HeadBuffer数据丢失问题。JDK会创建一个共享的DirectBuffer,HeadBuffer和DirectBuffer共享一块内存区域。

MappedByteBuffer

**基于内存映射**,继承ByteBuffer。FileChannel定义了一个map()方法,可以把一个文件从position位置开始的size大小的区域映射为内存映像文件。**mmap技术。**

内存映射文件是指将磁盘上的文件映射到进程的虚拟内存空间,使得进程可以像访问文件,无需进行读取或写入操作。

/*
* mode:限定内存映射区域堆内存映像文件的访问模式:只读、可读可写、写时拷贝
* position:文件映射的起始地址,对应内存映射区域的首地址
* size:文件映射的字节长度。对应MappedByteBuffer的大小
*/
public abstract MappedByteBuffer map(MapMode mode, long position,
                                      long size)
        throws IOException;

在使用 MappedByteBuffer 读写文件时,操作系统会将文件的数据读入内核缓冲区,在进程中生成一个内存映射区域,然后将该内存映射区域映射到进程的虚拟内存空间中,从而使进程可以像访问内存一样访问文件。

MappedByteBuffer新增的三个重要的方法:

  • fore():对于可读可写模式下的缓冲区,把对缓冲区内容的修改强制刷新到本地文件。

  • load():将缓冲区的内容载入到物理内存中,并返回这个缓冲区的引用。(内核缓冲区写入到物理内存中)。

  • isLoaded():如果缓冲区的内容在物理内存中,返回true。

map()方法的底层实现原理:

  • 通过本地方法map0为文件分配一块虚拟内存,作为它的内存映射区域,返回这块内存映射区域的起始地址。

  • 通过(起始地址+偏移量)就可以获取指定内存的数据。

  • 通过jni调用c语言的mmap64()对linux底层内核发出内存映射的调用。

MappedByteBuffer的特点和不足:

  • MappedByteBuffer 使用是堆外的虚拟内存,因此分配(map)的内存大小不受 JVM 的 -Xmx 参数限制,但是也是有大小限制的,不能超过Integer.MAX_VALUE.当超过字节限制时可能通过Position重新进行映射。

  • MappedByteBuffer 在处理大文件时性能的确很高,但也存内存占用、文件关闭不确定等问题,被其打开的文件只有在垃圾回收的才会被关闭,而且这个时间点是不确定的.

  • MappedByteBuffer 提供了文件映射内存的 mmap() 方法,也提供了释放映射内存的 unmap() 方法。然而 unmap() 是 FileChannelImpl 中的私有方法,无法直接显示调用。因此,用户程序需要通过 Java 反射的调用 sun.misc.Cleaner 类的 clean() 方法手动释放映射占用的内存区域

class类下的getResource和ClassLoader类下的getResource方法的使用和区别:

  • MyClass.class.getResource(“xxx”)方法中传的参数如果是相对路径,那么传递的路径是相对于MyClass而言

  • MyClass.class.getResource(“/xxx”)方法中传的参数如果是绝对路径,那么传递的路径是相对于classpath而言

  • MyClass.class.getClassLoader().getResource(“xxx”)方法中传的参数如果是相对路径,那么传递的路径是相对于classpath而言

  • MyClass.class.getClassLoader().getResource(“/xxx”)方法中传的参数如果是绝对路径,也就是只要参数开头带/那么返回的都是null

java中URL对象和URI对象的区别:

  • URL对象是URI对象的子集。URL是一个具备特定语法的URI,用于表示互联网上的资源。URI是一个通用的标识符,用于表示任何类型的资源。

  • URL对象可以打开网络并读取数据,而URI对象表示一个URI字符串。

  • URL对象对于网络通信的细节做了更多的封装,而URI对象更加通用。URL对象包含了网络通信的协议、主机名、端口号、路径、查询参数等详细信息,封装了网络通信的细节。URI对象不包含网络通信的细节,只是一个标识符,更加通用。

  • URI对象可以表示相对路径,而URL对象需要一个基础URL。URI对象可以表示相对路径,即相对于当前URI的路径。URL对象表示的是一个完整的URL,如果需要表示相对路径,需要指定一个基础URL。

DirectByteBuffer

DirectByteBuffer是java NIO中的一种ByteBuffer类型,它是在堆外内存中直接分配的,意味着它可以避免在JVM堆内存和操作系统之间进行频繁的数据拷贝。

   //DirectByteBuffer静态方法创建并分配内存,本质通过Unsafe.allocateMemory()底层
   //调用malloc函数
   public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw createCapacityException(capacity);
        return new HeapByteBuffer(capacity, capacity, null);
    }

FileChannel

FileChannel是一个用于文化读写、映射和操作的通道,同时在并发环境它是线程安全的。FileChannel定义了transferFrom()和transferTo()两个抽象方法,**它通过在通道和通道之间建立连接实现数据传输。**

transferTo() 和 transferFrom() 方法的底层实现原理都是基于sendfile实现文件数据传输。

//标记当前系统内核是否支持sendfile
private static volatile boolean transfrSupported = true;
//标记当前系统内核是否支持文件描述符基于管道的sendfile调用
private static volatile boolean pipeSupported = true;
//用于标记当前系统内核是否支持文件描述符基于文件的sendfile调\=-
private static volatile boolean fileSupported = true;

RocketMQ选择mmap+write这种零拷贝方式,适用于业务消息这种小块文件的数据持久化和传输。kafka采用sendfile这种零拷贝方式,使用系统日志消息这种高吞吐量的大块文件的数据持久化和传输。

标签:文件,JAVA,NIO,映射,URL,URI,内存,缓冲区,拷贝
From: https://blog.csdn.net/qqqwx_111116/article/details/144220654

相关文章

  • java定时任务cron表达式
    .常用cron表达式例子  (1)0/2****?表示每2秒执行任务  (1)00/2***?表示每2分钟执行任务  (1)0021*?表示在每月的1日的凌晨2点调整任务  (2)01510?*MON-FRI表示周一到周五每天上午10:15执行作业  (3)01510?6L2002-2006表示2002-2......
  • 【JavaEE初阶】落霞与孤鹜齐飞,秋水共长天一色 - (重点)线程
    本篇博客给大家带来的是线程的知识点,由于时间有限,分三天来写,本篇为线程第二篇.......
  • 怎么自己创建一个网站? 开发语言首选 java,使用CMS网站内容管理系统是不错的选择
    怎么自己创建一个网站推荐使用JavaCMS网站内容管理系统,根据网站规划的功能模块,创建不同的页面风格;文章目录怎么自己创建一个网站一、规划网站1.1确定网站主题和目的1.2规划网站结构和内容二、注册域名2.1选择域名注册商2.2查找并注册合适的域名三、选择网站托管......
  • 大厂Java面试经验套路总结
    前几天,跟个老朋友吃饭,他最近想跳槽去大厂,觉得压力很大,问我能不能分享些所谓的经验套路。每次有这类请求,都觉得有些有趣,不知道你发现没有大家身边真的有很多人不知道怎么面试,也不知道怎么准备面试,哪怕是一些工龄比较长的“老开发”:有的人明知道有些问题肯定会被问,面试前还不......
  • [Javascript] Dealing with Number in Javascript
    Writebignumber//NOT100000//Better100_0001e5 Shorthandssyntaxforfloatingnumber//Normal0.123//Thesame.123//eXalsoapplytofloatingnumber3.14e10//31400000000console.log(0.123e10===.123e10)//true 8进制Startwith0⚠️ ......
  • jfinal JFinal使用技巧-Record转JavaBean以及List转JavaBea
    本文原作者jfinal社区大牛杜福忠,我因为感觉有用搬一下:功能需求:这是一个泛型方法,接受一个 Record 对象和一个目标Bean的 Class 对象作为参数,返回一个转换后的Bean对象。方法内部通过反射创建Bean的实例,然后遍历Bean的所有属性,从 Record 对象中获取相应的值并设......
  • Java基于Vue+springboot超市管理收银/超市进销存管理系统
    所需该项目可以在最下面查看联系方式,为防止迷路可以收藏文章,以防后期找不到项目介绍项目的初步范围:1、系统数据:商品信息、顾客信息、员工信息、供应商信息、订单信息、业务信息、统计数据2、业务过程:商品管理、人事管理(顾客信息管理、员工信息管理、供应商信息管理)、订......
  • Java基于Vue+Spring Boot框架的网上商城系统的设计与实现
    所需该项目可以在最下面查看联系方式,为防止迷路可以收藏文章,以防后期找不到项目介绍当今的社会正在高速发展,要提高人们的生活水平和质量,就必须重视科技的发展,科技兴国是硬道理,其中互联网科技就是一个特别重要的领域,而网上购物就是互联网科技发展给人们生活带来便利的很好......
  • java web毕业设计开发常用的一些开源库!
    以下是一个关于JavaWeb网页开发者常用开发工具库的介绍表格,包括库的名称、介绍以及官方链接。库/工具名称介绍官方链接JDK(JavaDevelopmentKit)JDK是JavaWeb开发的基础,包含了Java编程语言的开发工具,如编译器、调试器等。它是进行JavaWeb开发所必需的。http://java.sun.com......
  • 01.Java简介
    Java历史​ Java最早是由SUN公司(已被Oracle收购)的詹姆斯·高斯林(高司令,Java之父)在上个世纪90年代初开发的一种编程语言,最初被命名为Oak(橡树),目标是针对小型家电设备的嵌入式应用,在1995年以Java的名称正式发布​ Java介于编译型语言和解释型语言之间编译型语言如C、C++,代码是直接......