天天看点

JVM 直接内存的分配与释放详解和底层实现

直接内存

定义:

  • 常见于 NIO 操作时,用于数据缓冲区
  • 分配回收成本较高,但读写性能高
  • 不受 JVM 内存回收管理
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class memory_overflow {
    static int _100Mb = 1024 * 1024 * 100;
    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i = 0;
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
                list.add(byteBuffer);
                i++;
            }
        } finally {
            System.out.println(i);
        }
    }
}
           

直接内存分配,每次内存分配100m,一直分配空间,运行代码会抛出异常 java.lang.OutOfMemoryError: Direct buffer memory , 表示直接内存不足。

内存的占用 释放原理

在这里我们运行一段代码,给添加一个g的内存,之后再进行释放内存,在任务管理器当中进行查看内存的占用情况。使用以下代码段进行测试

import java.io.IOException;
import java.nio.ByteBuffer;

public class GC_memory {
    static int _1Gb = 1024 * 1024 * 1024;
    public static void main(String[] args) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
        System.out.println("分配完毕...");
        System.in.read();
        System.out.println("开始释放...");
        byteBuffer = null;
        System.gc(); // 显式的垃圾回收,Full GC
        System.in.read();
    }
}
           

在按压第一次回车后分配内存,再一次按压回车后将内存释放掉。

JVM 直接内存的分配与释放详解和底层实现

释放原理的底层实现

直接内存分配的底层原理:Unsafe ;

使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法

ByteBuffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffer 对象,一旦

ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调用 freeMemory 来释放直接内存

使用以下代码段进行演示

package direct;

import sun.misc.Unsafe;
import java.io.IOException;
import java.lang.reflect.Field;

public class GC_memory_bottom {
    static int _1Gb = 1024 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
        Unsafe unsafe = getUnsafe();
        // 分配内存
        long base = unsafe.allocateMemory(_1Gb);
        unsafe.setMemory(base, _1Gb, (byte) 0);
        System.in.read();
        // 释放内存
        unsafe.freeMemory(base);
        System.in.read();
    }
    
    public static Unsafe getUnsafe() {
        try {
            Field f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            Unsafe unsafe = (Unsafe) f.get(null);
            return unsafe;
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}
           

执行代码,在任务管理器当中查看内存占用,再分配了1g的内存后,会上升,内存释放后,又会下降下来。

JVM 直接内存的分配与释放详解和底层实现

性能调优

禁用显示的垃圾回收机制。使用参数 -XX:+DisableExplicitGC 禁用之后直接内存的回收会受到影响,在 内存的占用 释放原理 这里的代码段进行添加参数。进行测试:

在进行内存释放的时候通常要采用unsafe对象进行释放

JVM 直接内存的分配与释放详解和底层实现