JVM12——直接内存的回收原理

直接内存direct memory并不由jvm进行垃圾回收,可能导致内存泄漏问题。运行如下代码。

/**
 * 演示直接内存溢出
 */
public class Demo1_10 {
    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);
        }
        // 方法区是jvm规范, jdk6 中对方法区的实现称为永久代
        //                  jdk8 对方法区的实现称为元空间
    }
}

输出结果。

72
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:695)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at cn.itcast.jvm.t1.direct.Demo1_10.main(Demo1_10.java:19)

直接内存的底层回收机制是怎样的呢?运行以下代码。

/**
 * 禁用显式回收对直接内存的影响
 */
public class Demo1_26 {
    static int _1Gb = 1024 * 1024 * 1024;

    /*
     * -XX:+DisableExplicitGC 显式的
     */
    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();
    }
}

在控制台输出分配完毕后,从后台的任务管理器可以看到其内存占用情况。
图片说明

当在控制台输入回车,输出开始释放,再输入回车,这个占用1个G的内存的进程就被清理了。是不是意味着java的gc操作发生了作用呢?

下面我们来解析上面直接内存回收的过程。Unsafe是jdk底层的一个类,用于内存分配,内存回收等,一般普通程序员无需使用,这里我们通过反射获取Unsafe对象,演示直接内存分配的底层原理。

/**
 * 直接内存分配的底层原理:Unsafe
 */
public class Demo1_27 {
    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);
        }
    }
}

运行代码,在任务管理器观察jdk进程内存占用发现,内存占用会在allocateMemory()后增加1G,在freeMemory()后恢复。因此,直接内存的回收其实不是由jvm虚拟机完成,而是通过Unsafe对象调用freeMemory()完成。

java全栈日日学 文章被收录于专栏

java全栈每日必学,不要高估自己一年能做的事,不要低估自己十年能做的事

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务