Investigación sobre los principios y la implementación de copia cero y copia profunda en Java.

La copia profunda y la copia cero son dos conceptos ampliamente utilizados en Java para la copia de objetos y la optimización de la transferencia de datos, respectivamente. Los principios de estos dos conceptos se presentarán en detalle a continuación y se darán los ejemplos de código Java correspondientes.

copia profunda

  1. Principio de copia profunda (copia profunda): la copia profunda consiste en crear una copia completamente independiente de un objeto, incluido el objeto en sí, los atributos de tipo de referencia y los subobjetos. La copia profunda se puede lograr mediante la serialización y deserialización.

Primero, debe asegurarse de que el objeto que se va a copiar y las clases a las que se hace referencia en él implementen la interfaz serializable. A continuación, las operaciones de serialización y deserialización se completan escribiendo objetos en el flujo de salida y leyendo del flujo de entrada. De esta manera, puede obtener una copia nueva del objeto, y el objeto original y el objeto de copia no se influyen entre sí.

Una forma común de implementar una copia profunda es mediante la serialización y deserialización. Los pasos específicos son los siguientes:

  • Primero, debe escribir el objeto original en una secuencia de salida (como ObjectOutputStream), convirtiendo el objeto en una secuencia de bytes.
  • Luego, la secuencia de bytes se lee del flujo de salida y se deserializa en un nuevo objeto a través del flujo de entrada (como ObjectInputStream). Este nuevo objeto es independiente del objeto original y sus propiedades y subobjetos se copian de forma independiente.

Este método puede garantizar que el objeto de copia profunda y sus propiedades y subobjetos referenciados sean todos nuevos, pero también puede implicar problemas como referencias circulares en el gráfico de objetos, que requieren un manejo especial. Además, el objeto copiado y la clase a la que hace referencia deben implementar la interfaz Serializable para poder realizar operaciones de serialización y deserialización.

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);
        }
    }
}

En el ejemplo anterior, Studentse crea una clase como objeto a copiar. deepCopy()El método utiliza serialización y deserialización para realizar una copia profunda. Primero, el objeto original se escribe en el flujo de salida de la matriz de bytes ( ) y luego se deserializa ByteArrayOutputStreama través del flujo de entrada de la matriz de bytes ( ) para obtener una copia completamente nueva del objeto.ByteArrayInputStream

copia cero

  1. Principio de copia cero: la copia cero es una tecnología de optimización que se utiliza para reducir o evitar copias de datos innecesarias durante la transmisión de datos. En Java, los métodos comunes de copia cero incluyen el uso de archivos asignados en memoria y NIO.

  2. Copia cero: La copia cero es una tecnología de optimización que se utiliza para reducir o evitar operaciones de copia de datos innecesarias durante la transmisión de datos. Mejora la eficiencia y el rendimiento de la transferencia de datos al transferir datos directamente de un espacio de direcciones a otro sin copiarlos en el medio.

En Java, la copia cero se usa generalmente para manejar operaciones de IO, como transferencia de archivos, transferencia de red, etc. Su principio es utilizar las características del sistema operativo para acceder directamente a la memoria donde se encuentran los datos a través de la memoria compartida (Archivos mapeados en memoria) o utilizar la tecnología DMA (Acceso directo a la memoria), reduciendo así la copia de datos entre el modo kernel y el modo de usuario.

La forma específica de implementar la copia cero depende del escenario y de la API utilizada. Las siguientes son dos implementaciones comunes de copia cero:

  • Archivos asignados en memoria: use FileChannel y MappedByteBuffer para asignar archivos directamente a la memoria para lograr un efecto de copia cero. El contenido del archivo se puede manipular directamente en la memoria, evitando la copia de datos durante la lectura y escritura.
  • Transmisión de red de copia cero: mediante el uso de bibliotecas NIO (E/S sin bloqueo), como SocketChannel, combinadas con ByteBuffer, se puede lograr una transmisión de red de copia cero. Los datos se pueden leer directamente desde el búfer de red al búfer de memoria directa de la aplicación, o escribirse directamente desde el búfer de memoria a la red, evitando la copia de datos entre el modo de usuario y el modo kernel.

El uso de tecnología de copia cero puede mejorar en gran medida la eficiencia de la transmisión de datos y reducir la carga de trabajo de la CPU. Especialmente en la transmisión de datos a gran escala y entornos de alta concurrencia, la mejora del rendimiento es muy significativa.

  • Archivos asignados en memoria: al asignar archivos directamente a la memoria, puede evitar copiar datos entre el modo de usuario y el modo kernel. Específicamente, se puede usar  FileChannel e  MappedByteBuffer implementar, y los métodos relacionados   incluyen map(),, get()etc. put()Aquí hay un ejemplo:
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));
        }
    }
}

En el ejemplo anterior, primero se crean un objeto de archivo filey el contenido que se escribirá content. Úselo FileChannelpara abrir un canal de archivo y use map()el método para asignar parte o todo el contenido del archivo a un búfer en la memoria MappedByteBuffer. Luego, put()el contenido se escribe en el búfer mediante el método. A continuación, vuelva a abrir el canal del archivo y utilice map()el método para asignar todo el contenido del archivo a otro búfer en la memoria MappedByteBuffer. Finalmente, get()el contenido se lee del búfer en una matriz de bytes a través del método y se genera la cadena.

Supongo que te gusta

Origin blog.csdn.net/weixin_44427181/article/details/132945656
Recomendado
Clasificación