【JVM 08-直接内存】

发布于:2025-06-20 ⋅ 阅读:(16) ⋅ 点赞:(0)

1. 定义

不属于 JVM 运行时数据区的一部分,通过 NIO 类引入,是一种堆外内存,可以显著提高 I/O 性能。直接内存的使用受到本机总内存的限制,若分配不当,可能导致 OutOfMemoryError 异常。

  • 常见于NIO操作时,用于数据缓冲区。
  • 分配回收成本较高,但读写性能高。
  • 不受JVM内存回收管理。

1.1 传统阻塞IO和对比

传统io读写时间:3s
这里是引用
bytebuffer读写时间:不到1s这里使用了直接内存在这里插入图片描述
看下面的图,直接内存少了系统缓存区复制到java缓存区的步骤。
在这里插入图片描述

2. 内存溢出情况演示

因为直接内存不受JVM内存回收管理,所以是有可能内存溢出的。
在这里插入图片描述
在这里插入图片描述

3. 直接内存分配和释放的原理

这里因为是直接内存所以要通过任务管理器来查看内存的变化。
在这里插入图片描述
通过反射来看Unsafe类是如何分配和释放直接内存的。这里用Unsafe类是因为bytebuffer底层就是通过Unsafe类来释放和分配内存的。具体可以看源码。
在这里插入图片描述
源码如下

 DirectByteBuffer(int cap) {                   // package-private
        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);

        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }

总结:

  • 使用了Unsafe对象完成直接内存的分配回收,并且回收需要主动调用freeMemory方法。
  • ByteBuffer的实现类内部,使用了Cleaner(虚引用)来监测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存。

4. 禁用显式回收对直接内存的影响

如同使用了**-XX:+DisableExplicitGC** 禁止显示式回收,可能导致直接拿内存没有回收。这里是引用
如果使用了禁止显式GC ,解决办法:
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到