Direct memory combat

Posted by new_programmer on Sun, 16 Jan 2022 22:09:59 +0100

A finishing touch

It is not part of the virtual machine runtime data area, nor is it a memory area defined in the Java virtual machine specification.
Direct memory is a memory interval that is outside the Java heap and directly applied to the system.
It comes from NIO (introduced by JDK1.4) and operates the Native memory through the DirectByteBuffer in the heap.
Generally, the speed of accessing direct memory is better than Java heap, that is, high read and write performance.
  • Therefore, for performance reasons, direct memory may be considered for frequent reads and writes.
  • Java's NIO library allows Java programs to use direct memory for data buffers.
Use the following code to allocate local memory space directly
int BUFFER = 1024*1024*1024; // 1GB
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);

(II) indirect cache and direct cache

In the original BIO architecture, we need to switch from user mode to kernel mode.

NIO uses the concept of direct cache.

III. problems with direct memory

  • It may also cause an OutOfMemoryError exception.
  • Because there is a Java heap outside the Java heap, its size will not be directly limited by the maximum heap size specified by - Xmx, but the system memory is limited, and the sum of Java heap and direct memory is still limited by the maximum memory given by the operating system.
  • shortcoming
- higher allocated recovery costs
- not managed by JVM memory reclamation
  • The direct memory size can be set through MaxDirectMemorySize.
  • If not specified, the default value is consistent with the maximum value of the heap - Xmx parameter.

IV. actual combat - view the occupation and release of direct memory

1. Code

/**
*  IO                  |   NIO (New IO / Non-Blocking IO)
*  byte[] / char[]     |   Buffer
*  Stream              |   Channel
*
* View the occupation and release of direct memory
*/
public class BufferTest {
    private static final int BUFFER = 1024 * 1024 * 1024; // 1GB


    public static void main(String[] args){
        // Direct allocation of local memory space
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
        System.out.println("Direct memory allocation completed, request instruction!");


        Scanner scanner = new Scanner(System.in);
        scanner.next();


        System.out.println("Direct memory begins to release!");
        byteBuffer = null;
        System.gc();
        scanner.next();
    }
}

2. Test

a occupation

b) release

V. actual combat - Performance Test of IO and NIO

1. Code

package com.atguigu.java;

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

/**
* IO And NIO performance test
* IO: 54606
* NIO: 50244
*/
public class BufferTest1 {
    private static final String TO = "F:\\test\\Alien world BD Chinese characters.mp4";
    private static final int _100Mb = 1024 * 1024 * 100;

    public static void main(String[] args) {
        long sum = 0;
        String src = "F:\\test\\test.mp4";
        for (int i = 0; i < 3; i++) {
            String dest = "F:\\test\\test_" + i + ".mp4";
//            sum += io(src,dest); // 54606
            sum += directBuffer(src, dest); // 50244
        }
        System.out.println("The total time spent is:" + sum);
    }

    private static long directBuffer(String src, String dest) {
        long start = System.currentTimeMillis();
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            inChannel = new FileInputStream(src).getChannel();
            outChannel = new FileOutputStream(dest).getChannel();

            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
            while (inChannel.read(byteBuffer) != -1) {
                byteBuffer.flip(); // Change to read data mode
                outChannel.write(byteBuffer);
                byteBuffer.clear(); // empty
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inChannel != null) {
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outChannel != null) {
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        long end = System.currentTimeMillis();
        return end - start;
    }

    private static long io(String src, String dest) {
        long start = System.currentTimeMillis();
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(src);
            fos = new FileOutputStream(dest);
            byte[] buffer = new byte[_100Mb];
            while (true) {
                int len = fis.read(buffer);
                if (len == -1) {
                    break;
                }
                fos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
        long end = System.currentTimeMillis();
        return end - start;
    }
}

2. Test description

The IO test took 54606 ms.
NIO test took 50244 ms.

Vi. actual combat - local memory OOM test

1. Code

/**
* OOM of local memory: OutOfMemoryError: Direct buffer memory
*/
public class BufferTest2 {
    private static final int BUFFER = 1024 * 1024 * 10; // 10MB

    public static void main(String[] args) {
        ArrayList<ByteBuffer> list = new ArrayList<>();
        int count = 0;
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
                list.add(byteBuffer);
                count++;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            System.out.println(count);
        }
    }
}

2. Test

Note: traditional Java} memory monitoring tools cannot monitor local memory. You can use the task manager to view it.

After OOM, the console prints as follows.

339
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:694)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at com.atguigu.java.BufferTest2.main(BufferTest2.java:17)

VII. Actual combat - MaxDirectMemorySize setting test

1. Code

/**
* -Xmx20m -XX:MaxDirectMemorySize=10m
*/
public class MaxDirectMemorySizeTest {
    private static final long _1MB = 1024 * 1024;

    public static void main(String[] args) throws IllegalAccessException {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe)unsafeField.get(null);
        while(true){
            unsafe.allocateMemory(_1MB);
        }
    }
}

2. Test

Set to -Xmx20m -XX:MaxDirectMemorySize=10m, the console prints as follows
Exception in thread "main" java.lang.OutOfMemoryError
    at sun.misc.Unsafe.allocateMemory(Native Method)
    at com.atguigu.java.MaxDirectMemorySizeTest.main(MaxDirectMemorySizeTest.java:20)

Topics: jvm