java中零拷贝和深拷贝的原理以及实现探究

2023-09-17 16:19:45

深拷贝和零拷贝是两个在 Java 中广泛使用的概念,它们分别用于对象复制和数据传输优化。下面将详细介绍这两个概念的原理,并给出相应的 Java 代码示例。

深拷贝

  1. 深拷贝(Deep Copy)原理: 深拷贝是创建一个对象的完全独立副本,包括对象本身、引用类型的属性和子对象。可以通过序列化和反序列化来实现深拷贝。

首先,需要确保要拷贝的对象及其内部引用的类实现了 Serializable 接口。接下来,通过将对象写入输出流并从输入流中读取来完成序列化和反序列化操作。这样就可以得到一个全新的对象副本,原始对象和副本对象之间互不影响。

实现深拷贝的一种常见方式是通过序列化和反序列化来实现。具体步骤如下:

  • 首先,需要将原始对象写入一个输出流(例如 ObjectOutputStream),将对象转换为字节序列。
  • 然后,再从输出流中读取字节序列,通过输入流(例如 ObjectInputStream)反序列化成一个新的对象。这个新对象与原始对象相互独立,它们的属性和子对象都是独立复制的。

这种方式可以确保深拷贝对象及其引用的属性和子对象都是全新的,但也可能涉及到对象图中的循环引用等问题,需要特殊处理。另外,被拷贝的对象和其引用的类需要实现 Serializable 接口,以便进行序列化和反序列化操作。

import java.io.*;

class Student implements Serializable {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters and setters here...

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class DeepCopyExample {
    public static void main(String[] args) {
        Student original = new Student("John", 20);

        // 深拷贝
        Student copy = deepCopy(original);

        // 改变原始对象的属性值
        original.setName("Tom");
        original.setAge(25);

        System.out.println("Original: " + original);  // 输出 Original: Student{name='Tom', age=25}
        System.out.println("Copy: " + copy);          // 输出 Copy: Student{name='John', age=20}
    }

    public static <T extends Serializable> T deepCopy(T object) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            oos.close();

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            T copy = (T) ois.readObject();
            ois.close();

            return copy;
        } catch (Exception e) {
            throw new RuntimeException("Deep copy failed", e);
        }
    }
}

在上述示例中,创建了一个 Student 类作为要拷贝的对象。deepCopy() 方法使用了序列化和反序列化的方式进行深拷贝。首先将原始对象写入字节数组输出流 (ByteArrayOutputStream) 中,再通过字节数组输入流 (ByteArrayInputStream) 进行反序列化,从而得到一个全新的对象副本。

零拷贝

  1. 零拷贝(Zero-copy)原理: 零拷贝是一种优化技术,用于减少或避免数据传输过程中的不必要数据拷贝。在 Java 中,常用的零拷贝方法包括使用内存映射文件和 NIO。

  2. 零拷贝(Zero-copy): 零拷贝是一种优化技术,用于在数据传输过程中减少或避免不必要的数据拷贝操作。它通过将数据直接从一个地址空间传输到另一个地址空间,而无需在中间进行复制,提高了数据传输的效率和性能。

在 Java 中,零拷贝通常用于处理 IO 操作,例如文件传输、网络传输等。它的原理是利用操作系统的特性,通过共享内存(Memory-mapped Files)或使用 DMA(Direct Memory Access)技术来直接访问数据所在的内存,从而减少了内核态和用户态之间的数据复制。

具体实现零拷贝的方式取决于场景和使用的 API。以下是两个常见的零拷贝实现方式:

  • 内存映射文件(Memory-mapped Files):使用 FileChannel 和 MappedByteBuffer,将文件直接映射到内存中,达到零拷贝的效果。可以直接在内存中操作文件内容,避免了读写过程中的数据拷贝。
  • 零拷贝网络传输:通过使用 NIO(Non-blocking I/O)库,如 SocketChannel,结合 ByteBuffer,可以实现零拷贝的网络传输。数据可以直接从网络缓冲区读取到应用程序的直接内存缓冲区,或者直接从内存缓冲区写入到网络中,避免了数据在用户态和内核态之间的复制。

使用零拷贝技术可以大幅提高数据传输的效率,减少 CPU 的工作量,特别是在大量数据传输、高并发环境下,对性能的提升非常显著。

  • 内存映射文件:通过将文件直接映射到内存中,可以避免数据在用户态和内核态之间的复制。具体可使用 FileChannel 和 MappedByteBuffer 实现,相关方法包括 map()get()put() 等。以下是一个示例:
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class ZeroCopyMemoryMappedFileExample {
    public static void main(String[] args) throws IOException {
        File file = new File("data.txt");
        String content = "This is the content to be written.";

        // 写入数据
        try (FileChannel channel = new RandomAccessFile(file, "rw").getChannel()) {
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, content.length());
            buffer.put(content.getBytes());
        }

        // 读取数据
        try (FileChannel channel = new FileInputStream(file).getChannel()) {
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
            byte[] data = new byte[(int) channel.size()];
            buffer.get(data);
            System.out.println(new String(data));
        }
    }
}

在以上示例中,首先创建了一个文件对象 file 和待写入的内容 content。使用 FileChannel 来打开文件通道,并使用 map() 方法将文件的一部分或全部内容映射到内存中的 MappedByteBuffer 缓冲区。然后,通过 put() 方法将内容写入缓冲区。接着,重新打开文件通道,并使用 map() 方法将整个文件内容映射到内存中的另一个 MappedByteBuffer 缓冲区。最后,通过 get() 方法将内容从缓冲区读取到字节数组中,并输出字符串。

更多推荐

Vue2+Vue3

文章目录Vue快速上手Vue是什么第一个Vue程序插值表达式Vue核心特性:响应式Vue指令v-htmlv-show与v-ifv-else与v-else-ifv-onv-bindv-forv-model指令修饰符计算属性watch侦听器(监视器)watch——简写watch——完整写法Vue生命周期和生命周期的四个阶段

【Linux学习笔记】权限

1.普通用户和root用户权限之间的切换2.权限的三个w2.1.什么是权限(what)2.1.1.用户角色2.1.2.文件属性2.2.怎么操作权限呢?(how)2.2.1.ugo+-rwx方案2.2.2.八进制方案2.2.3.文件权限的初始模样2.2.4.进入一个目录,需要什么权限呢?2.3.为什么要有权限呢?(why

计算机毕业设计 基于SSM+Vue的校园短期闲置资源置换平台的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌🍅文末获取源码联系🍅👇🏻精彩专栏推荐订阅👇🏻不然下次找不到哟————————————————计算机毕业设计题目《10

强化学习从基础到进阶--案例与实践[7.1]:深度确定性策略梯度DDPG算法、双延迟深度确定性策略梯度TD3算法详解项目实战

【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现专栏详细介绍:【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现对于深度强化学习这块规划为:基础单智能算法教学(g

01-初识HTML

01-初识HTML学习目标:理解HTML的基本语法掌握排版标签实现标题等效果相对路径和绝对路径媒体标签(图片、音频、视频)超链接一、基础认知了解网页组成和五大浏览器明确Web标准的构成1.1认识网页以下网页有哪些部分组成文字、图片、音频、视频、超链接…那么这个网页背后本质是什么?前端的代码是通过什么软件转换成用户眼中的

高精度地图定位在高速公路自动驾驶系统中的应用

【摘要】自动驾驶已经成为全球汽车产业的战略发展方向,其中L3级高速公路自动驾驶是最有可能率先落地的自动驾驶系统,高精度地图和定位系统是自动驾驶系统的关键一部分,近年来发展迅速,已经达到可量产状态。文章首先分析了自动驾驶和高精度地图定位的发展现状,然后,对高精度地图和定位系统在自动驾驶系统的地理围栏判定和感知冗余方面的应

Linux MQTT智能家居(MQTT框架)

文章目录前言一、MQTT通信框架二、心跳包三、项目中使用到的软件四、MQTT中服务器和客户端建立连接的步骤总结前言本篇文章将会讲解MQTT的框架,我们这个项目使用到的MQTT源码库来自于一位大佬编写。大佬博客主页:主页一、MQTT通信框架MQTT(MessageQueuingTelemetryTransport)是一种

OSI七层网络参考模型与数据流通过程

OSI七层网络参考模型文章目录OSI七层网络参考模型1.OSI参考模型初步了解2.OSI参考模型理解3.数据流通的过程1.OSI参考模型初步了解OSI,英文为OpenSystemInterconnect,意为开放式系统互连,国际化标准组织(ISO)指定了OSI模型,这个模型把网络通信的工作定义成7个框架,分别是物理层,

《java并发编程的艺术》读书笔记 1~2章

1.java并发基本概念1.1上下文切换实现原理:通过CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,时间片非常短,CPU通过不停的切换线程执行,让我们感觉多个线程是同时执行的。CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片会切换到下一个任务,并保存上一个任务的状态,下次切换到这个任务时

day44 数据库查询命令

--isnull和isnotnull#1.查询没有上级领导的员工编号,姓名,工资selectempno,ename,salfromempwheremgrisnull;#2.查询emp表中没有奖金(comm)的员工姓名,工资,奖金selectename,sal,commfromempwherecommisnull;#3.

《Clean Code》

整洁代码文章目录一、命名1.1变量1.2函数Rule11.【推荐】先整体后细节1.3类二、格式三、条件语句四、对象和数据结构一、命名以业务为导向命名[operateMaxSaleQtyLogs]>以技术命名[operateMaxSaleQtyLogList]>随意命名[logList]1.1变量Rule1.【推荐】变量

热文推荐