JVM 的直接内存(Direct Memory)是什么?

JVM 的直接内存(Direct Memory)是什么? 先打个比方想象你住在小区 A朋友住在小区 B你要给他送一份文件。普通方式堆内存你把文件交给小区快递站 → 快递站发快递 → 文件送到朋友手上你要给他送东西得先交给快递站快递站再转给朋友。快递站就是中转站多了一步。直接内存方式你俩后来住进了同一个小区文件直接递过去就行东西直接递过去就行不用走快递站中间省了一步直接内存就是 JVM绕过堆内存直接向操作系统申请的一块内存区域让 JVM 和操作系统共用同一块地盘。具体发生了什么没有直接内存时磁盘/网卡 → 操作系统内核缓冲区 → 【复制一份】→ JVM 堆内存 byte[] → Java 程序读取 ↑ ↑ 系统管的区域 Java 管的区域每次做 I/O 操作读文件、收网络数据数据从磁盘出来后要先存在系统内核缓冲区然后再复制一份到 JVM 堆内存的 byte[] 里你的 Java 程序才能用。同一份数据存了两份复制这一步既浪费时间又浪费内存。有了直接内存后磁盘/网卡 → 直接内存 → Java 程序读取 ↑ ↑ 系统能访问 JVM也能访问 同一块内存不用复制数据从磁盘出来后直接进入一块两边都能访问的内存区域。Java 堆里的 byte[] 缓冲区不再需要中间那次复制被彻底干掉了。代码怎么用Java NIO 提供了ByteBuffer就一个方法的区别// 普通的堆内存缓冲区 ByteBuffer heapBuffer ByteBuffer.allocate(1024); ​ // 直接内存缓冲区 —— 关键就在这里 ByteBuffer directBuffer ByteBuffer.allocateDirect(1024);allocateDirect()就是向操作系统申请直接内存。用起来 API 一模一样区别全在底层。一个实际例子——文件零拷贝FileChannel in FileChannel.open(Paths.get(大文件.dat)); FileChannel out FileChannel.open(Paths.get(副本.dat)); ​ // 底层就是直接内存没有经过 Java 堆 in.transferTo(0, in.size(), out);你没手动分配 ByteBuffer但transferTo内部就是靠直接内存实现的。很多你天天用的 API 底层都是这套机制。堆内存 vs 直接内存一张表看清对比项堆内存直接内存分配位置JVM 堆内堆外操作系统管理分配速度快相对慢要跟操作系统打交道读写速度慢需要额外复制到系统缓冲区快直接访问不需要复制GC 管理自动回收不归 GC 管需要自己管理适合场景普通 Java 对象大量 I/O 操作网络、文件核心优势少了一次内存复制频繁 I/O 场景下性能提升明显。三个坑用之前得知道坑一分配慢堆上 new 一个数组很快但allocateDirect每次都要跟操作系统申请开销不小。不是 I/O 密集的场景没必要用。坑二不归 GC 管GC 不会主动回收直接内存。短时间内分配了大量直接内存又不用可能会报java.lang.OutOfMemoryError: Direct buffer memory建议启动时显式设置上限-XX:MaxDirectMemorySize256m坑三出了问题难查直接内存不在堆里jmap、jstat这些工具看不到它的全貌排查成本比堆内存高不少。谁在用Netty网络框架—— ByteBuf 默认用直接内存这是它高性能的核心原因之一NIO FileChannel——FileChannel.map()返回的就是直接内存Hadoop / Spark—— 大块缓冲区放堆外减轻 GC 压力各种 RPC 框架—— 涉及大量网络 I/O 的基本都在用不是偏门知识是 Java 高性能 I/O 的基础设施。一句话总结直接内存 JVM 向操作系统借一块内存JVM 和操作系统共用同一块地盘跳过中间的复制环节让 I/O 更快。代价是分配慢、不归 GC 管、排查困难。高频 I/O 场景值得用