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

“零拷贝”(Zero-Copy)技术详解以及使用场景

“零拷贝”(Zero-Copy)是一种优化数据传输效率的技术,通过减少或消除数据在内存中的复制次数,显著提高I/O操作性能。以下是使用Java代码实现的零拷贝技术示例。

Java NIO 中的零拷贝实现

1. 内存映射文件(Memory Mapped File)

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

public class MemoryMappedFileExample {
   
    public static void main(String[] args) {
   
        try {
   
            // 传统方式读取文件
            System.out.println("传统方式读取大文件:");
            long start = System.currentTimeMillis();
            traditionalReadFile("bigfile.data");
            System.out.println("传统方式耗时: " + (System.currentTimeMillis() - start) + "ms");
            
            // 使用内存映射方式读取文件
            System.out.println("\n内存映射方式读取大文件:");
            start = System.currentTimeMillis();
            memoryMappedReadFile("bigfile.data");
            System.out.println("内存映射方式耗时: " + (System.currentTimeMillis() - start) + "ms");
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }
    
    // 传统方式读取文件
    private static void traditionalReadFile(String fileName) throws IOException {
   
        RandomAccessFile file = new RandomAccessFile(fileName, "r");
        byte[] buffer = new byte[1024];
        int bytesRead;
        long totalBytes = 0;
        
        while ((bytesRead = file.read(buffer)) != -1) {
   
            // 处理数据...
            totalBytes += bytesRead;
        }
        
        file.close();
        System.out.println("读取了 " + totalBytes + " 字节的数据");
    }
    
    // 使用内存映射方式读取文件
    private static void memoryMappedReadFile(String fileName) throws IOException {
   
        RandomAccessFile file = new RandomAccessFile(fileName, "r");
        FileChannel channel = file.getChannel();
        
        // 将文件映射到内存中
        MappedByteBuffer buffer = channel.map(MapMode.READ_ONLY, 0, channel.size());
        
        long totalBytes = 0;
        while (buffer.hasRemaining()) {
   
            // 直接从映射内存中读取数据
            byte data = buffer.get();
            // 处理数据...
            totalBytes++;
        }
        
        channel.close();
        file.close();
        System.out.println("读取了 " + totalBytes + " 字节的数据");
    }
}

2. 直接内存传输(transferTo/transferFrom)

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

public class TransferToExample {
   
    public static void main(String[] args) {
   
        try {
   
            // 传统方式复制文件
            System.out.println("传统方式复制文件:");
            long start = System.currentTimeMillis();
            traditionalCopy("source.data", "destination1.data");
            System.out.println("传统方式耗时: " + (System.currentTimeMillis() - start) + "ms");
            
            // 使用transferTo方式复制文件
            System.out.println("\n零拷贝方式复制文件:");
            start = System.currentTimeMillis();
            zeroCopy("source.data", "destination2.data");
            System.out.println("零拷贝方式耗时: " + (System.currentTimeMillis() - start) + "ms");
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }
    
    // 传统方式复制文件
    private static void traditionalCopy(String source, String destination) throws IOException {
   
        FileInputStream fis = new FileInputStream(source);
        FileOutputStream fos = new FileOutputStream(destination);
        byte[] buffer = new byte[4096];
        int bytesRead;
        
        while ((bytesRead = fis.read(buffer)) != -1) {
   
            fos.write(buffer, 0, bytesRead);
        }
        
        fos.close();
        fis.close();
    }
    
    // 使用零拷贝方式复制文件
    private static void zeroCopy(String source, String destination) throws IOException {
   
        FileChannel sourceChannel = new FileInputStream(source).getChannel();
        FileChannel destinationChannel = new FileOutputStream(destination).getChannel();
        
        // 使用transferTo方法直接传输数据
        // 这里的实现依赖于操作系统的支持,在Linux和UNIX系统上通常使用sendfile系统调用
        sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);
        
        sourceChannel.close();
        destinationChannel.close();
    }
}

3. 直接缓冲区(Direct Buffer)

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class DirectBufferExample {
   
    public static void main(String[] args) {
   
        try {
   
            // 使用堆内缓冲区
            System.out.println("使用堆内缓冲区(HeapByteBuffer):");
            long start = System.currentTimeMillis();
            copyWithHeapBuffer("source.data", "destination1.data");
            System.out.println("堆内缓冲区耗时: " + (System.currentTimeMillis() - start) + "ms");
            
            // 使用直接缓冲区
            System.out.println("\n使用直接缓冲区(DirectByteBuffer):");
            start = System.currentTimeMillis();
            copyWithDirectBuffer("source.data", "destination2.data");
            System.out.println("直接缓冲区耗时: " + (System.currentTimeMillis() - start) + "ms");
        } catch (IOException e) {
   
            e.printStackTrace();
        }
    }
    
    // 使用堆内缓冲区复制文件
    private static void copyWithHeapBuffer(String source, String destination) throws IOException {
   
        FileChannel sourceChannel = new FileInputStream(source).getChannel();
        FileChannel destinationChannel = new FileOutputStream(destination).getChannel();
        
        // 分配堆内缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); // 1MB buffer
        
        while (sourceChannel.read(buffer) != -1) {
   
            buffer.flip();
            destinationChannel.write(buffer);
            buffer.clear();
        }
        
        sourceChannel.close();
        destinationChannel.close();
    }
    
    // 使用直接缓冲区复制文件
    private static void copyWithDirectBuffer(String source, String destination) throws IOException {
   
        FileChannel sourceChannel = new FileInputStream(source).getChannel();
        FileChannel destinationChannel = new FileOutputStream(destination).getChannel();
        
        // 分配直接缓冲区(堆外内存)
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024); // 1MB direct buffer
        
        while (sourceChannel.read(buffer) != -1) {
   
            buffer.flip();
            destinationChannel.write(buffer);
            buffer.clear();
        }
        
        sourceChannel.close();
        destinationChannel.close();
    }
}

Netty 中的零拷贝实现

1. 使用 DirectBuffer

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class NettyDirectBufferServer {
   
    private final int port;

    public NettyDirectBufferServer(int port) {
   
        this.port = port;
    }

    public void run() throws Exception {
   
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try {
   
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) // 使用池化的DirectBuffer
             .childHandler(new ChannelInitializer<SocketChannel>() {
   
                 @Override
                 public void initChannel(SocketChannel ch) {
   
                     ch.pipeline().addLast(new SimpleChannelInboundHandler<ByteBuf>() {
   
                         @Override
                         protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
   
                             // 处理接收到的数据
                             System.out.println("收到数据: " + msg.readableBytes() + " 字节");
                             
                             // 分配一个直接缓冲区用于响应
                             ByteBuf response = ctx.alloc().directBuffer(256);
                             response.writeBytes("已收到数据".getBytes());
                             ctx.writeAndFlush(response);
                         }
                     });
                 }
             });

            // 绑定端口并启动服务器
            ChannelFuture f = b.bind(port).sync();
            System.out.println("服务器启动,监听端口: " + port);

            // 等待服务器关闭
            f.channel().closeFuture().sync();
        } finally {
   
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public stati

相关文章:

  • 内网渗透-端口转发
  • 【NLP 40、文本生成任务】
  • 【AI大模型】搭建本地大模型GPT-NeoX:详细步骤及常见问题处理
  • 通往自主智能之路:探索自我成长的AI
  • 图像处理全流程指南(OpenCV 实现)
  • 2025-03-23 学习记录--C/C++-C语言 sprintf()实现将多个值按指定格式拼接成字符串
  • 国产达梦(DM)数据库的安装(Linux系统)
  • 设计模式:创建型、结构型、行为型
  • Linux 系统运行 Android 应用的几种方案
  • 为什么EasyExcel能处理大数据量而不内存溢出,EasyExcel原理
  • Linux__之__基于UDP的Socket编程网络通信
  • LLaMA-Factory微调大模型
  • 网络爬虫-4:jsonpath+实战
  • 一个轻量级的 SQLite ORM 工具包
  • c++测试题
  • C# 反射(Reflection)详解
  • Java EE(16)——网络原理——TCP协议解析二
  • 深度解析历年蓝桥杯算法题,助力提升编程技能
  • ngx_http_core_server_name
  • 文献分享: ColXTR——将ColBERTv2的优化引入ColXTR
  • 上海首发经济“卷”到会展业,浦东签约三个年度“首展”
  • 伊美第四轮核问题谈判开始
  • 贵州省总工会正厅级副主席梁伟被查,曾任贵州省纪委副书记
  • 河北邯郸一酒店婚宴发生火灾:众人惊险逃生,酒店未买保险
  • 印称一名高级官员在巴基斯坦发动的袭击中死亡
  • 越怕出错越会出错,“墨菲定律”的魔咒该怎么破?