Java stack memory, off heap memory, zero copy analysis and code implementation

Posted by fredroines on Fri, 11 Feb 2022 04:49:30 +0100

The official account of WeChat: Java essays
Follow to learn more about Java related technology sharing. Questions or suggestions, welcome to the official account message!

Zero copy, which is a familiar term, is often mentioned in the interview of development post. Recently, after reviewing the basic principles of Netty, I went through the knowledge points about out of heap memory in NIO. Here, I will record the concepts and related knowledge of stack memory, out of heap memory and zero copy.

Java stack memory and off heap memory

1. Stack memory

Stack memory, as the name suggests, refers to heap memory and stack memory. Heap memory is a memory area managed by Java GC, while stack memory is thread memory. About stack memory, I won't go into detail here. Taking Hotspot as an example, the brief structure of heap memory is shown in the following figure:

The stack relationship can be understood through a simple line of code:

public static void main(String[] args) {
    Object o = new Object();
}

The above code mainly completes two things. new Object() opens up a piece of memory on the heap, that is, new Object() is allocated on the heap; The variable o is on the stack of thread main, which points to the heap memory address opened by new Object(). In short, the objects created in the program are stored in heap memory, and stack memory contains references to it.

2. Out of heap memory

In short, except stack memory, the rest is off heap memory (of course, from the perspective of Java runtime memory). Off heap memory is directly managed by the operating system rather than virtual machines. The main reasons for using off heap memory are as follows:

  • GC is reduced to a certain extent. The out of heap memory is directly managed by the operating system rather than the JVM. Therefore, if the out of heap memory is used, a relatively small in heap memory can be maintained to reduce the impact of garbage collection on program performance. This one is well applied in Kafka. Interested students can learn about it;

  • Another greater advantage is to improve the efficiency of IO operation! This involves the user state and kernel state, as well as the concept of kernel buffer. For details, see the author's previous article Java notes - kernel buffer and process buffer . The in heap memory is actually the process buffer of the user process, which belongs to the user state, while the out of heap memory is managed by the operating system, which belongs to the kernel state. If you write data from the heap to the disk, the data will be copied to the out of heap memory, that is, the kernel buffer, and then written to the disk by the OS. However, if you use the out of heap memory, you can avoid this copy operation.

Zero-copy

Summarizing the description of stack memory and off heap memory in the above content, we mainly solve two questions: "where is zero copy copied from to?"? How does "zero copy" optimize this copy operation?

  • When the user process needs to write data like the disk, it needs to copy the contents of the user buffer (in heap memory) to the kernel buffer (out of heap memory), and then the operating system writes the contents of the kernel buffer into the disk;

  • If the user needs to copy the data out of the disk, it can be directly stored in the disk.

In Java, some methods of using out of heap memory and DMA are provided, which can optimize the IO efficiency of user processes to a great extent. Here, a copy of the file code is given, which uses BIO, NIO and NIO using off heap memory to copy the file respectively, and the time consumption is simply compared.

Here I use a 200MB pdf file to copy. You can specify a larger file. The larger the file, the more obvious the contrast. Here, the average time-consuming of BIO is about 1500ms, that of NIO is about 120ms, and that of NIO using off heap memory is about 100ms.

package top.jiangnanmax.nio;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * @author jiangnanmax
 * @email jiangnanmax@gmail.com
 * @description CopyCompare
 * @date 2021/5/7
 **/

public class CopyCompare {

    public static void main(String[] args) throws Exception {
        String inputFile = "/tmp/nio/input/HyperLedger.pdf";
        String outputFile = "/tmp/nio/output/HyperLedger.pdf";

        long start = System.currentTimeMillis();

        nioCopyByDirectMem(inputFile, outputFile);

        long end = System.currentTimeMillis();

        System.out.println("cost time: " + (end - start) + " ms");

        deleteFile(outputFile);
    }

    /**
     * File replication using traditional IO
     *
     * Average time: 15** ms
     *
     * @param sourcePath
     * @param destPath
     */
    private static void bioCopy(String sourcePath, String destPath) throws Exception {
        File sourceFile = new File(sourcePath);
        File destFile = new File(destPath);
        if (!destFile.exists()) {
            destFile.createNewFile();
        }

        FileInputStream inputStream = new FileInputStream(sourceFile);
        FileOutputStream outputStream = new FileOutputStream(destFile);

        byte[] buffer = new byte[512];
        int lenRead;

        while ((lenRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, lenRead);
        }

        inputStream.close();
        outputStream.close();
    }

    /**
     * Use NIO for file replication, but not off heap memory
     *
     * The average time consumption is 1** ms, which is one order of magnitude faster than BIO direct???
     *
     * @param sourcePath
     * @param destPath
     */
    private static void nioCopy(String sourcePath, String destPath) throws Exception {
        File sourceFile = new File(sourcePath);
        File destFile = new File(destPath);
        if (!destFile.exists()) {
            destFile.createNewFile();
        }

        FileInputStream inputStream = new FileInputStream(sourceFile);
        FileOutputStream outputStream = new FileOutputStream(destFile);

        FileChannel inputChannel = inputStream.getChannel();
        FileChannel outputChannel = outputStream.getChannel();

        // The underlying call of transferFrom should be sendfile
        // Data is transferred directly between two file descriptors
        // DMA

        outputChannel.transferFrom(inputChannel, 0, inputChannel.size());

        inputChannel.close();
        outputChannel.close();
        inputStream.close();
        outputStream.close();

    }

    /**
     * Use NIO to copy files and use off heap memory
     *
     * The average time is about 100ms, which is a little faster than NIO without off heap memory
     *
     * @param sourcePath
     * @param destPath
     */
    private static void nioCopyByDirectMem(String sourcePath, String destPath) throws Exception {
        File sourceFile = new File(sourcePath);
        File destFile = new File(destPath);
        if (!destFile.exists()) {
            destFile.createNewFile();
        }

        FileInputStream inputStream = new FileInputStream(sourceFile);
        FileOutputStream outputStream = new FileOutputStream(destFile);

        FileChannel inputChannel = inputStream.getChannel();
        FileChannel outputChannel = outputStream.getChannel();

        MappedByteBuffer buffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0, inputChannel.size());

        outputChannel.write(buffer);

        inputChannel.close();
        outputChannel.close();
        inputStream.close();
        outputStream.close();

    }

    /**
     * Delete target file
     *
     * @param target
     */
    private static void deleteFile(String target) {
        File file = new File(target);
        file.delete();
    }

}

official account

  • Pay attention to the official account and receive the technology sharing on Java instantly.

Topics: Java