Copia cero presentación

prefacio

Entendido desde el significado literal es a copiar de nuevo los datos de un lado a otro no es necesario, mejorar considerablemente el rendimiento del sistema; la palabra que a menudo oímos en Java NIO, Netty, kafka, RocketMQ y otros marcos, a menudo como un punto culminante de su mejora de rendimiento; a continuación varios conceptos de E / S comienzan, cero copia posterior análisis.

concepto de E / S

1. Buffer

El buffer es la base de todas las E / S, E / S no es nada más que hablar dentro o fuera del búfer de datos; lleva a cabo procesos de operaciones de E / S, se envía una petición al sistema operativo, o bien es el tampón se drenó los datos (escritura), o para llenar la memoria intermedia (de lectura); véase más adelante iniciados proceso java unos datos de petición de lectura para cargar un diagrama de flujo más o menos:

después de iniciar el proceso después de la solicitud de lectura, la solicitud de lectura es recibida por el núcleo, será primero comprobar si el proceso ya existe en el espacio del núcleo los datos requeridos, si ya existe, a continuación, copiar los datos directamente en el proceso de amortiguación; si no hay un núcleo entonces emitir comandos al controlador de disco, requiere la lectura de datos desde el disco, el controlador de disco para leer los datos directamente en el búfer del núcleo este paso por la finalización de DMA, el siguiente paso es copiar los datos del núcleo buffer de proceso,
si el proceso iniciado solicitudes de escritura, los usuarios también tienen que copiar los datos en una memoria intermedia dentro de la memoria intermedia en el interior del núcleo zócalo, y luego a través de los datos de DMA copiar la tarjeta de red y los envía;
se podría pensar que esto es un desperdicio de espacio, cada dato de tiempo necesario para copiar el espacio del núcleo el espacio de usuario, por lo que la copia cero aparece para resolver este problema;
en la copia cero proporciona dos formas son: el modo de mmap + escritura, forma sendfile;

2. Memoria virtual

Todos los sistemas operativos modernos utilizan la memoria virtual, la dirección virtual para utilizar para reemplazar la dirección física, los beneficios de hacerlo son:
más de 1. Una dirección virtual puede apuntar a la misma dirección de memoria física,
2. espacio de memoria virtual puede ser mayor que la dirección física real disponible ;
el uso de la primera característica se puede asignar espacio de direcciones virtuales del espacio del núcleo y el usuario dirección a la misma dirección física, de modo que pueda ser llenado a la DMA kernel y los procesos de espacio de usuario simultáneamente visibles a un tampón sustancialmente como se muestra a continuación:

omite las relaciones copiar el núcleo y el espacio de usuario, java también tomar ventaja de esta característica del sistema operativo para mejorar el rendimiento, aspecto en el siguiente enfoque java en qué apoyo tiene copia cero.

3.mmap + modo de escritura

El uso de mmap + modo de escritura en lugar de la modalidad de lectura + escritura original, mmap es un método de archivos asignados a la memoria, un archivo u otros objetos procedentes corresponder en la dirección del espacio de direcciones del proceso, archivo y del disco en el espacio de direcciones virtuales proceso para una dirección virtual once pares de relación enantiomérica; así Guardar el núcleo original se puede copiar datos búfer de lectura a la memoria intermedia del usuario, pero aún requiere que el núcleo para leer los datos de copia de tampón en el tampón de socket kernel, sustancialmente como se muestra a continuación:

forma 4.sendfile

llamada al sistema sendfile kernel versión 2.1 se introduce, el propósito es el de simplificar el proceso de transferencia de datos llevado a cabo a través de una red entre los dos canales. la introducción de la llamada al sistema sendfile, no sólo reduce la replicación de datos, también reduce el cambio de contexto, sustancialmente como se muestra a continuación:

la transferencia de datos se produce sólo en el espacio del núcleo, un cambio de contexto se reduce, sin embargo, todavía hay una copia, Can esta copia tiempo también puede ser omitido, Linux2.4 kernel se ha mejorado, el núcleo tampón correspondiente información de descripción de datos (dirección de memoria, offset) que corresponde a la registrada entre el buffer de zócalo, de modo que incluso una CPU espacio del núcleo también guardado una copia;

Java copia cero

1.MappedByteBuffer

FileChannel oferta java nio proporciona un método map (), que puede crear un mapeo de memoria virtual entre un archivo abierto y MappedByteBuffer, MappedByteBuffer heredado de ByteBuffer, similar a un tampón basado en memoria, pero los datos del objeto elementos almacenados en un archivo en el disco, llamada a método get () obtiene los datos desde el disco, los datos reflejan el contenido actual del archivo, llame al método put () actualiza el archivo en el disco, y las modificaciones de archivos hizo a otra lector también es visible, véase el siguiente ejemplo una lectura simple, y después se analizó para MappedByteBuffer:

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18
public class MappedByteBufferTest { 
 
    void main (String [] args) public static lanza la excepción { 
        Archivo = new File ( "D: //db.txt"); 
        largo len = file.length (); 
        byte [] ds = new byte [(int) len]; 
        MappedByteBuffer mappedByteBuffer = new FileInputStream (archivo) .getChannel () Mapa (FileChannel.MapMode.READ_ONLY, 0,. 
                LEN); 
        for (int offset = 0; compensado <len; compensar ++) { 
            byte b = mappedByteBuffer.get (); 
            ds [desplazamiento] = b; 
        } 
        Exploración explorador = nuevo escáner (nuevo ByteArrayInputStream (ds)) useDelimiter (". "); 
        while (scan.hasNext ()) { 
            System.out.print (scan.next () + " "); 
        } 
    } 
}

Mapa FileChannel proporcionado principalmente por () para lograr mapa, mapa () método es el siguiente:

1 
2 
3
    Mapa MappedByteBuffer abstracta pública (modo MapMode, 
                                         posición larga, tamaño de largo) 
        throws IOException;

Se proporcionan tres parámetros, MapMode, posición y tamaño; representan respectivamente:
mapmode: modos de asignación, opciones incluyen: READ_ONLY, READ_WRITE, privado;
la Posición: posición inicial asignada desde la que el número de posición de bytes;
Tamaño: desde la posición emprender el regreso cuántos bytes;

Enfoque mirada MapMode, indique dos de sólo lectura y de lectura y escritura, y por supuesto el modo de asignación solicitada por las restricciones de acceso FileChannel objeto, si está habilitado READ_ONLY en un archivo no tiene permiso de lectura, lanzará NonReadableChannelException; modo privado Representa copia en escritura medios de mapeo a través método put () dará lugar a los cambios realizados para producir una copia privada de los datos y los datos del único ejemplo MappedByteBuffer copia puede ser visto; este proceso no se realiza ningún cambio en el archivo subyacente , y una vez que el tampón se somete a la acción de la recolección de basura (basura recogida), se perderán esos cambios; un rápido vistazo a la de mapa de origen (método):

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53
    mapa público MappedByteBuffer (modo MapMode, posición larga, tamaño de largo) 
        throws IOException 
    { 
            ...省略... 
            int pagePosition = (int) (posición allocationGranularity%); 
            largo mapPosition = posición - pagePosition; 
            largo mapSize = tamaño + pagePosition; 
            try { 
                // Si no es una excepción fue arrojado de map0, la dirección es válida 
                dir = map0 (i-mode, mapPosition, mapSize); 
            } Catch (OutOfMemoryError x) { 
                // Un OutOfMemoryError puede indicar que hemos agotado la memoria 
                // por lo gc fuerza y volver a intentar mapa 
                System.gc ();
                try { 
                    Thread.sleep (100); 
                } Catch (InterruptedException y) { 
                    Thread.currentThread () de interrupción (.); 
                } 
                Try { 
                    addr = map0 (imode, mapPosition, mapSize); 
                } Catch (OutOfMemoryError y) { 
                    // Después de un segundo OOME, fallará 
                    arrojar nueva IOException ( "Mapa falló", y); 
                } 
            } 
 
            // En Windows, y potencialmente otras plataformas, que necesitan un proceso abierto 
            // descriptor de archivo para algunas operaciones de mapeo. 
            FileDescriptor MFD; 
            tratar {
                MFD = nd.duplicateForMapping (fd); 
            } Catch (IOException OIE) { 
                unmap0 (dir, mapSize); 
                tirar de la OIE; 
            } 
 
            Assert (IOStatus.checkAll (addr)); 
            assert (addr% allocationGranularity == 0); 
            int ISIZE = (int) tamaño; 
            Unmapper um = new Unmapper (addr, mapSize, ISIZE, MFD); 
            if ((grabable) || (i-mode == MAP_RO)) { 
                retorno Util.newMappedByteBufferR (ISIZE, 
                                                 addr + pagePosition, 
                                                 fabricado, 
                                                 um);
            } Else { 
                volver Util.newMappedByteBuffer (ISIZE, 
                                                addr + pagePosition, 
                                                MFD, 
                                                um); 
            } 
     }

Que significa aproximadamente adquirido por la dirección de método nativo mapeado en memoria, y si eso no funciona, a re-mapeo manualmente gc; la última dirección de la memoria mapeada por instancias de un MappedByteBuffer, sí MappedByteBuffer es una clase abstracta, de hecho, no es un verdadero ejemplo de palabras que salieron de DirectByteBuffer;

2.DirectByteBuffer

DirectByteBuffer heredada MappedByteBuffer, se puede adivinar por el nombre se abre espacio en la memoria directa, no ocupa espacio en la memoria JVM; el trazado por FileChannel MappedByteBuffer DirectByteBuffer real también, por supuesto, además de esta manera, se puede abrir manualmente algo de espacio:

1
ByteBuffer directByteBuffer = ByteBuffer.allocateDirect (100);

Dado que el espacio de memoria directa abierta de 100 bytes;

transporte 3.Channel-a-Channel

A menudo tienen que transferir archivos de un lugar a otro lugar, siempre FileChannel TransferTo () método se utiliza para aumentar la eficiencia de la transmisión, primer vistazo a un ejemplo sencillo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ChannelTransfer {
    public static void main(String[] argv) throws Exception {
        String files[]=new String[1];
        files[0]="D://db.txt";
        catFiles(Channels.newChannel(System.out), files);
    }
 
    private static void catFiles(WritableByteChannel target, String[] files)
            throws Exception {
        for (int i = 0; i < files.length; i++) {
            FileInputStream fis = new FileInputStream(files[i]);
            FileChannel channel = fis.getChannel();
            channel.transferTo(0, channel.size(), target);
            channel.close();
            fis.close();
        }
    }
}

通过FileChannel的transferTo()方法将文件数据传输到System.out通道,接口定义如下:

1
2
3
    public abstract long transferTo(long position, long count,
                                    WritableByteChannel target)
        throws IOException;

几个参数也比较好理解,分别是开始传输的位置,传输的字节数,以及目标通道;transferTo()允许将一个通道交叉连接到另一个通道,而不需要一个中间缓冲区来传递数据;
注:这里不需要中间缓冲区有两层意思:第一层不需要用户空间缓冲区来拷贝内核缓冲区,另外一层两个通道都有自己的内核缓冲区,两个内核缓冲区也可以做到无需拷贝数据;

Netty零拷贝

netty提供了零拷贝的buffer,在传输数据时,最终处理的数据会需要对单个传输的报文,进行组合和拆分,Nio原生的ByteBuffer无法做到,netty通过提供的Composite(组合)和Slice(拆分)两种buffer来实现零拷贝;看下面一张图会比较清晰:

TCP层HTTP报文被分成了两个ChannelBuffer,这两个Buffer对我们上层的逻辑(HTTP处理)是没有意义的。 但是两个ChannelBuffer被组合起来,就成为了一个有意义的HTTP报文,这个报文对应的ChannelBuffer,才是能称之为”Message”的东西,这里用到了一个词”Virtual Buffer”。
可以看一下netty提供的CompositeChannelBuffer源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class CompositeChannelBuffer extends AbstractChannelBuffer {
 
    private final ByteOrder order;
    private ChannelBuffer[] components;
    private int[] indices;
    private int lastAccessedComponentId;
    private final boolean gathering;
 
    public byte getByte(int index) {
        int componentId = componentId(index);
        return components[componentId].getByte(index - indices[componentId]);
    }
    ...省略...

components用来保存的就是所有接收到的buffer,indices记录每个buffer的起始位置,lastAccessedComponentId记录上一次访问的ComponentId;CompositeChannelBuffer并不会开辟新的内存并直接复制所有ChannelBuffer内容,而是直接保存了所有ChannelBuffer的引用,并在子ChannelBuffer里进行读写,实现了零拷贝。

其他零拷贝

RocketMQ的消息采用顺序写到commitlog文件,然后利用consume queue文件作为索引;RocketMQ采用零拷贝mmap+write的方式来回应Consumer的请求;
同样kafka中存在大量的网络数据持久化到磁盘和磁盘文件通过网络发送的过程,kafka使用了sendfile零拷贝方式;

总结

零拷贝如果简单用java里面对象的概率来理解的话,其实就是使用的都是对象的引用,每个引用对象的地方对其改变就都能改变此对象,永远只存在一份对象。高质量编程视频shangyepingtai.xin

发布了122 篇原创文章 · 获赞 47 · 访问量 3万+

Supongo que te gusta

Origin blog.csdn.net/fengzongfu/article/details/105341878
Recomendado
Clasificación