千家信息网

什么是JVM直接内存

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇内容介绍了"什么是JVM直接内存"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!JDK7和JDK8
千家信息网最后更新 2026年11月03日什么是JVM直接内存

本篇内容介绍了"什么是JVM直接内存"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

JDK7和JDK8的内存结构对比

从上面的图中可以看到Java8相比Java7来讲将方法区的实现,从非堆空间(其实逻辑与堆相连,所属于运行时数据区内部)迁移到了本地内存中,不会造成FullGC过多的压力以及与老年代的耦合度过高的问题,减少FullGC的扫描范围,从而改为手动去回收机制(也可以自动回收需要配置调整)。 之前的文章里面介绍了JVM的运行时数据区的相关介绍,一直对直接内存的研究和学习较少,现在我们就开始介绍一下直接内存的分配方式以及回收方式。

直接内存的定义

1. 常见于NIO操作时,用于数据缓冲区(ByteBuffer.allocate),当然也可以采用Unsafe类进行native方法运作进行申请直接内存。

2. 分配回收成本较高(属于操作系统内存),但读写性能高。(因为直接内存不需要经过JVM解释器进行地址映射转换到系统真正内存,故此读写速度会比堆内存在快很多,但是申请和回收机制角度而言复杂,因为属于直接由操作系统进行管理,而非JVM直接进行管理。

3. 不受JVM内存回收管理(依旧存在内存溢出的问题),此外受限制与操作系统物理内存,以及我们可以手动设置它的阈值(MaxDirectMemory),默认情况下来讲直接内存几乎没有限制(当没有超出物理内存的控制而言)

4. jvm的内存分配与回收是自动的,不需要手动调用任何的方法,但是直接内存需要我们手动调用方法。

直接内存基本使用(IO操作举例)

  1. Java堆内存:

  1. 使用直接内存后,可以减少步骤:

可以看出来,直接内存无需进行与java堆内存进行映射和转换,可以直接去操作系统内存。

直接内存导致的内存溢出问题

书写程序:每次都分配直接内存,直到内存溢出

public class Test1 {    static int _100Mb=1024*1024*100;    public static void main(String[] args) {        List list=new ArrayList<>();        int i=0;        try {           while (true){               ByteBuffer byteBuffer=ByteBuffer.allocateDirect(_100Mb);               list.add(byteBuffer);               i++;           }        }finally {            System.out.println(i);        }    }}

测试结果:

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory    at java.nio.Bits.reserveMemory(Bits.java:694)    at java.nio.DirectByteBuffer.(DirectByteBuffer.java:123)    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)    at pers.zhb.test.Test1.main(Test1.java:15)

直接内存的分配与回收(底层通过Unsafe对象管理)

直接内存的分配与回收

运行程序前:

直接内存的分配与释放程序:

public class Test1 {    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();    }}

分配直接内存后:

可以看出来相关的多出一个java的进程,并且内存占用1037M,所以也证实了我们的猜测和预想。

在IDEA的控制台点击回车对内存进行释放:

可以看到恢复到了原始的状态。

控制台打印出分配与回收的提示:

分配完毕开始释放Process finished with exit code 0

其中System.gc() ,强制执行FullGC,回收掉byteBuffer对象

至此,我们的推测全部验证通过,直接内存会采用另外一个进程去存储相关的系统内存区域,并且不属于jvm内存之内的管理。

Unsafe实现对直接内存的分配与回收:
public class Test1 {    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(){        Field field= null;        try {            field = Unsafe.class.getDeclaredField("theUnsafe");        } catch (NoSuchFieldException e) {            e.printStackTrace();        }        field.setAccessible(true);        Unsafe unsafe= null;        try {            unsafe = (Unsafe)field.get(null);        } catch (IllegalAccessException e) {            e.printStackTrace();        }        return unsafe;    }}

jvm的内存分配与回收是自动的,不需要手动调用任何的方法,但是直接内存需要我们手动调用方法。

ByteBuffer源码

ByteBuffer (属于工具方法、工厂方法)

ByteBuffer byteBuffer= ByteBuffer.allocateDirect(_1Gb);

allocateDirect(分配内存方法)

public static ByteBuffer allocateDirect(int capacity) {    return new DirectByteBuffer(capacity);}

DirectByteBuffer(直接内存的对象)

DirectByteBuffer(int cap) {        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对象

优缺点

优点

  • 减少了垃圾回收

堆外内存是直接受操作系统管理(不是JVM)。这样做能保持一个较小的堆内内存,以减少垃圾收集对应用的影响。

  • 提升IO速度

堆内内存由JVM管理,属于"用户态";而堆外内存由OS管理,属于"内核态"。如果从堆内向磁盘写数据时,数据会被先复制到堆外内存,即内核缓冲区,然后再由OS写入磁盘,使用堆外内存避免了这个操作。

缺点

  • 没有JVM协助管理内存,需要我们自己手动来管理堆外内存,防止内存溢出同时也为了避免一直有FULL GC,最终导致物理内存被耗完。

我们会指定直接内存的最大值,通过-XX:MaxDirectMemorySize来指定,当达到阈值的时候,调用system.gc来进行一次full gc,把那些没有被使用的直接内存回收掉。

  • 此外,分配直接内存和回收内存的性能和速度也比较复杂,所以成本也会很高,特别是回收需要FullGC、分配内存的时候,需要切换一次用户态到系统态的操作,分配直接内存,从而用用户态的句柄来引用系统态的内存空间。

"什么是JVM直接内存"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

内存 分配 管理 方法 系统 手动 操作系统 数据 对象 物理 用户 程序 速度 问题 控制 运行 复杂 内容 内核 垃圾 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 花魁咖啡 北京路客互联网科技有限公司 中国互联网科技股 工业互联网科技新基建 互联网科技有限公司图片 腾讯阿里互联网科技 互联网前沿科技 开启互联网科技时代小说 剑昆互联网络科技有限公司临桂 阿里互联网科技公司 互联网科技大奖都有哪些公司 民营互联网公司国防科技 工业互联网等科技实现智能化赋能 科技互联网海报 互联网科技数字货币 互联网科技接地气 江苏互联网智能科技产品出厂价 长沙易易互联网科技有限公司 互联网领先科技成果宣传视频 重庆科技产业互联网有哪些 互联网科技工作委员会副主任毛军 互联网科技的发展 互联网科技集团 重庆威客帮互联网科技 互联网科技如何创业 互联网科技公司营业执照怎么办理 恒生互联网科技业指数调整周期 雨阳互联网卡科技有限公司 广州百诚互联网科技有限公司官网 互联网领先科技宣传片
0