JVM直接内存和堆内存

时间:2023-01-02 16:28:06

概述

直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中农定义的内存区域。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通脱一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

  • 本机直接内存的分配不会受到Java 堆大小的限制,受到本机总内存大小限制

  • 配置虚拟机参数时,不要忽略直接内存 防止出现OutOfMemoryError异常

直接内存(堆外内存)与堆内存比较

  1. 直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显
  2. 直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显

代码验证:

package com.xnccs.cn.share;

import java.nio.ByteBuffer;


/** * 直接内存 与 堆内存的比较 */
public class ByteBufferCompare {


    public static void main(String[] args) {
        allocateCompare();   //分配比较
        operateCompare();    //读写比较
    }

    /** * 直接内存 和 堆内存的 分配空间比较 * * 结论: 在数据量提升时,直接内存相比非直接内的申请,有很严重的性能问题 * */
    public static void allocateCompare(){
        int time = 10000000;    //操作次数 


        long st = System.currentTimeMillis();
        for (int i = 0; i < time; i++) {

            //ByteBuffer.allocate(int capacity) 分配一个新的字节缓冲区。
            ByteBuffer buffer = ByteBuffer.allocate(2);      //非直接内存分配申请 
        }
        long et = System.currentTimeMillis();

        System.out.println("在进行"+time+"次分配操作时,堆内存 分配耗时:" + (et-st) +"ms" );

        long st_heap = System.currentTimeMillis();
        for (int i = 0; i < time; i++) {
            //ByteBuffer.allocateDirect(int capacity) 分配新的直接字节缓冲区。
            ByteBuffer buffer = ByteBuffer.allocateDirect(2); //直接内存分配申请
        }
        long et_direct = System.currentTimeMillis();

        System.out.println("在进行"+time+"次分配操作时,直接内存 分配耗时:" + (et_direct-st_heap) +"ms" );

    }

    /** * 直接内存 和 堆内存的 读写性能比较 * * 结论:直接内存在直接的IO 操作上,在频繁的读写时 会有显著的性能提升 * */
    public static void operateCompare(){
        int time = 1000000000;

        ByteBuffer buffer = ByteBuffer.allocate(2*time);  
        long st = System.currentTimeMillis();
        for (int i = 0; i < time; i++) {

            // putChar(char value) 用来写入 char 值的相对 put 方法
            buffer.putChar('a');
        }
        buffer.flip();
        for (int i = 0; i < time; i++) {
            buffer.getChar();
        }
        long et = System.currentTimeMillis();

        System.out.println("在进行"+time+"次读写操作时,非直接内存读写耗时:" + (et-st) +"ms");

        ByteBuffer buffer_d = ByteBuffer.allocateDirect(2*time);
        long st_direct = System.currentTimeMillis();
        for (int i = 0; i < time; i++) {

            // putChar(char value) 用来写入 char 值的相对 put 方法
            buffer_d.putChar('a');
        }
        buffer_d.flip();
        for (int i = 0; i < time; i++) {
            buffer_d.getChar();
        }
        long et_direct = System.currentTimeMillis();

        System.out.println("在进行"+time+"次读写操作时,直接内存读写耗时:" + (et_direct - st_direct) +"ms");
    }
}
  • 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
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

输出: 
在进行10000000次分配操作时,堆内存 分配耗时:12ms 
在进行10000000次分配操作时,直接内存 分配耗时:8233ms 
在进行1000000000次读写操作时,非直接内存读写耗时:4055ms 
在进行1000000000次读写操作时,直接内存读写耗时:745ms

可以自己设置不同的time 值进行比较

分析

从数据流的角度,来看

非直接内存作用链: 
本地IO –>直接内存–>非直接内存–>直接内存–>本地IO 
直接内存作用链: 

本地IO–>直接内存–>本地IO

对于直接内存来说,JVM将会在IO操作上具有更高的性能,因为它直接作用于本地系统的IO操作。而非直接内存,也就是堆内存中的数据,如果要作IO操作,会先复制到直接内存,再利用本地IO处理。

直接内存使用场景

  • 有很大的数据需要存储,它的生命周期很长
  • 适合频繁的IO操作,例如网络并发场景

直接内存(堆外内存)的优点和缺点

直接内存(堆外内存),其实就是不受JVM控制的内存。相比于堆内内存有几个优势: 
  1 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到) 
  2 加快了复制的速度。因为堆内在flush到远程时,会先复制到直接内存(堆外内存),然后在发送;而直接内存(堆外内存)相当于省略掉了这个工作。 
  而福之祸所依,自然也有不好的一面: 
  1 直接内存难以控制,如果内存泄漏,那么很难排查 
  2 直接内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。


参考

《深入理解Java虚拟机》 –周志明

博文:https://www.cnblogs.com/xing901022/p/5243657.html