直接内存(Direct Memory)
直接内存(Direct Memory)是Java中一种特殊的内存分配方式,它不是在Java堆中分配,而是直接在操作系统的本地内存中分配。这部分内存不受Java堆大小限制,但会受到本机总内存的限制。
直接内存的特点
直接内存的应用场景
NIO网络编程:SocketChannel、FileChannel等
 
高性能I/O:大文件读写
 
内存映射文件:MappedByteBuffer
 
JNI调用:与本地代码交互
 
图形处理:OpenGL/DirectX绑定
 
科学计算:大规模数值计算
 
直接内存与堆内存比较
直接内存的核心类
Java中主要通过java.nio.ByteBuffer来操作直接内存:
1 2 3 4
   |  ByteBuffer heapBuffer = ByteBuffer.allocate(1024);
  ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
 
  | 
 
直接内存的分配与释放
释放直接内存,直接内存不会随着ByteBuffer的GC而自动释放,最佳实践是:
显式调用System.gc()(不推荐,不可靠)
 
使用try-with-resources模式(Java 9+)
 
使用Cleaner机制
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | import java.nio.ByteBuffer; public class DirectMemoryExample {     public static void main(String[] args) {                  ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024);
          System.out.println("直接内存分配成功");         System.out.println("是否是直接内存: " + buffer.isDirect());         System.out.println("缓冲区容量: " + buffer.capacity() + " bytes");
                   buffer.putInt(123);         buffer.flip();         System.out.println("读取的值: " + buffer.getInt());     } }
   | 
 
Java 9+的释放方式:
1 2 3 4 5 6 7 8 9 10 11 12 13
   | import java.nio.ByteBuffer; public class DirectMemoryJava9 {     public static void main(String[] args) {                  try (ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024)) {             System.out.println("直接内存分配成功");             buffer.putInt(456);             buffer.flip();             System.out.println("读取的值: " + buffer.getInt());         }         System.out.println("直接内存已释放");     } }
   | 
 
手动管理方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
   | import sun.misc.Cleaner; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.nio.ByteBuffer; public class DirectMemoryManual {     public static void main(String[] args) {         ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024);
                   Cleaner cleaner = Cleaner.create(buffer, () -> {             System.out.println("Cleaner释放直接内存");         });
                   ReferenceQueue<ByteBuffer> queue = new ReferenceQueue<>();         PhantomReference<ByteBuffer> phantomRef =              new PhantomReference<>(buffer, queue);
          buffer = null; 
                   System.gc(); 
          try {                          Thread.sleep(1000);         } catch (InterruptedException e) {             e.printStackTrace();         }     } }
   | 
 
直接内存的性能优势
直接内存主要性能优势在于减少数据拷贝,特别是在I/O操作中。性能对比示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
   | import java.nio.ByteBuffer; import java.util.Arrays; public class DirectMemoryPerformance {     private static final int SIZE = 100_000_000;     private static final int TRIES = 10;     public static void main(String[] args) {                  long heapTime = testHeapBuffer();         System.out.println("堆内存平均时间: " + heapTime + " ns");
                   long directTime = testDirectBuffer();         System.out.println("直接内存平均时间: " + directTime + " ns");
          System.out.println("性能提升: " +              ((double)(heapTime - directTime) / heapTime * 100) + "%");     }
      private static long testHeapBuffer() {         long total = 0;         for (int i = 0; i < TRIES; i++) {             ByteBuffer buffer = ByteBuffer.allocate(SIZE);             long start = System.nanoTime();             for (int j = 0; j < SIZE; j++) {                 buffer.put((byte) (j & 0xFF));             }             long end = System.nanoTime();             total += (end - start);         }         return total / TRIES;     }
      private static long testDirectBuffer() {         long total = 0;         for (int i = 0; i < TRIES; i++) {             ByteBuffer buffer = ByteBuffer.allocateDirect(SIZE);             long start = System.nanoTime();             for (int j = 0; j < SIZE; j++) {                 buffer.put((byte) (j & 0xFF));             }             long end = System.nanoTime();             total += (end - start);                          ((sun.nio.ch.DirectBuffer) buffer).cleaner().clean();         }         return total / TRIES;     } }
   | 
 
运行结果:
1 2 3
   | 堆内存平均时间: 62147600 ns 直接内存平均时间: 51595070 ns 性能提升: 16.979786830062626%
   | 
 
可以看出在同样的IO操作上,直接内存的性能比堆内存更高
直接内存的监控与管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | import java.lang.management.ManagementFactory; import java.lang.management.BufferPoolMXBean; import java.util.List; public class DirectMemoryMonitor {     public static void main(String[] args) {         List<BufferPoolMXBean> pools = ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class);         for (BufferPoolMXBean pool : pools) {             System.out.println("缓冲区池名称: " + pool.getName());             System.out.println("总容量: " + pool.getTotalCapacity() + " bytes");             System.out.println("使用计数: " + pool.getCount());             System.out.println("内存使用量: " + pool.getMemoryUsed() + " bytes");             System.out.println();         }     } }
   | 
 
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | 缓冲区池名称: mapped 总容量: 0 bytes 使用计数: 0 内存使用量: 0 bytes
  缓冲区池名称: direct 总容量: 8192 bytes 使用计数: 1 内存使用量: 8192 bytes
  缓冲区池名称: mapped - 'non-volatile memory' 总容量: 0 bytes 使用计数: 0 内存使用量: 0 bytes
   | 
 
设置直接内存大小:
直接内存的大小可以通过JVM参数限制,如果不指定,默认与-Xmx最大值相同:
1
   | -XX:MaxDirectMemorySize=256m
   | 
 
直接内存的常见问题与解决方案
问题1:直接内存溢出
1
   | java.lang.OutOfMemoryError: Direct buffer memory
   | 
 
解决方案:
增加-XX:MaxDirectMemorySize参数
 
检查代码确保及时释放直接内存
 
减少直接内存的使用量
 
问题2:内存泄漏现象: 直接内存持续增长不释放解决方案:
确保所有ByteBuffer都正确关闭
 
使用工具监控直接内存使用情况
 
考虑使用内存池技术
 
问题3:性能问题
现象: 直接内存分配/释放频繁导致性能下降解决方案:
使用对象池重用ByteBuffer
 
预分配大块内存,自行管理分配
 
减少不必要的直接内存分配
 
直接内存池实现
对于需要频繁使用直接内存的场景,可以实现一个简单的内存池:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
   | import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentLinkedQueue; public class DirectMemoryPool {     private final ConcurrentLinkedQueue<ByteBuffer> pool = new ConcurrentLinkedQueue<>();     private final int bufferSize;     private final int maxPoolSize;     public DirectMemoryPool(int bufferSize, int maxPoolSize) {         this.bufferSize = bufferSize;         this.maxPoolSize = maxPoolSize;     }     public ByteBuffer allocate() {         ByteBuffer buffer = pool.poll();         if (buffer != null) {             buffer.clear();              return buffer;         }         return ByteBuffer.allocateDirect(bufferSize);     }     public void release(ByteBuffer buffer) {         if (pool.size() < maxPoolSize) {             pool.offer(buffer);         } else {                          ((sun.nio.ch.DirectBuffer) buffer).cleaner().clean();         }     }     public void destroy() {         for (ByteBuffer buffer : pool) {             ((sun.nio.ch.DirectBuffer) buffer).cleaner().clean();         }         pool.clear();     }     public static void main(String[] args) {         DirectMemoryPool pool = new DirectMemoryPool(1024 * 1024, 10);
          ByteBuffer buffer1 = pool.allocate();         ByteBuffer buffer2 = pool.allocate();
                   buffer1.putInt(123);         buffer1.flip();         System.out.println(buffer1.getInt());
                   pool.release(buffer1);         pool.release(buffer2);
                   pool.destroy();     } }
   | 
 
直接内存是Java中一种重要的内存管理机制,它提供了高性能的内存访问方式,特别适合大规模数据操作和I/O密集型应用。然而,它也需要开发者更加谨慎地管理内存,避免内存泄漏和溢出问题。
在实际应用中,应当:
根据需求合理选择使用堆内存还是直接内存
 
确保直接内存的正确释放
 
监控直接内存的使用情况
 
对于频繁使用的场景,考虑使用内存池技术