前言
直接内存大多时候也被称为堆外内存, 自从 JDK 引入 NIO 后, 直接内存的使用也越来越普遍. 通过 native 方法可以分配堆外内存, 通过 DirectByteBuffer 对象来操作.
直接内存不属于 Java 堆, 所以它不受堆大小限制, 但是它受物理内存大小的限制.
配置
可以通过 -XX:MaxDirectMemorySize 参数来设置最大可用直接内存, 如果启动时未设置则默认为最大堆内存大小, 即与 -Xmx 相同. 即假如最大堆内存为 1G, 则默认直接内存也为 1G, 那么 JVM 最大需要的内存大小为 2G 多一些. 当直接内存达到最大限制时就会触发 GC, 如果回收失败则会引起 OutOfMemoryError.
分配内存耗时
环境为 JDK9, 两种内存分配的耗时如下, 运行两遍让其预热. 可以看到直接内存的分配比较耗时, 而堆内存分配操作耗时少好几倍.
- public static void directMemoryAllocate() {long tsStart = System.currentTimeMillis();
- for (int i = 0; i < 100000; i++) {
- ByteBuffer buffer = ByteBuffer.allocateDirect(400);
- }
- System.out.println("direct memory allocate:" + (System.currentTimeMillis() - tsStart) + "ms");
- tsStart = System.currentTimeMillis();
- for (int i = 0; i < 100000; i++) {
- ByteBuffer buffer = ByteBuffer.allocate(400);
- }
- System.out.println("heap memory allocate:" + (System.currentTimeMillis() - tsStart) + "ms");
- }
direct memory allocate: 149 ms
heap memory allocate: 41 ms
direct memory allocate: 122 ms
heap memory allocate: 31 ms
读写操作耗时
环境为 JDK9, 两种内存的读写操作耗时如下, 同样运行两遍让其预热, 可以看到直接内存读写操作的速度相对快一些.
- public static void memoryRW() {
- ByteBuffer buffer = ByteBuffer.allocateDirect(400);
- ByteBuffer buffer2 = ByteBuffer.allocate(400);
- long tsStart = System.currentTimeMillis();
- for (int i = 0; i < 100000; i++) {
- for (int j = 0; j < 100; j++) {
- buffer.putInt(j);
- }
- buffer.flip();
- for (byte j = 0; j < 100; j++) {
- buffer.getInt();
- }
- buffer.clear();
- }
- System.out.println("direct memory rw:" + (System.currentTimeMillis() - tsStart) + "ms");
- tsStart = System.currentTimeMillis();
- for (int i = 0; i < 100000; i++) {
- for (int j = 0; j < 100; j++) {
- buffer2.putInt(j);
- }
- buffer2.flip();
- for (byte j = 0; j < 100; j++) {
- buffer2.getInt();
- }
- buffer2.clear();
- }
- System.out.println("heap memory rw:" + (System.currentTimeMillis() - tsStart) + "ms");
- }
direct memory rw: 39 ms
heap memory rw: 34 ms
direct memory rw: 23 ms
heap memory rw: 46 ms
总结
理论上直接内存的机制访问速度要快一些, 但也不能武断地直接说直接内存快, 另外, 在内存分配操作上直接内存要慢一些. 直接内存更适合在内存申请次数较少, 但读写操作较频繁的场景.
来源: https://juejin.im/post/5b0def9ef265da0908442207