JVM直接內存

概述

直接內存並不是虛擬機運行時數據區的一部分,也不是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");
    }
}

輸出:
在進行10000000次分配操作時,堆內存 分配耗時:12ms
在進行10000000次分配操作時,直接內存 分配耗時:8233ms
在進行1000000000次讀寫操作時,非直接內存讀寫耗時:4055ms
在進行1000000000次讀寫操作時,直接內存讀寫耗時:745ms

可以自己設置不同的time 值進行比較

分析

從數據流的角度,來看

非直接內存作用鏈:
本地IO –>直接內存–>非直接內存–>直接內存–>本地IO
直接內存作用鏈:
本地IO–>直接內存–>本地IO

直接內存使用場景

  • 有很大的數據需要存儲,它的生命週期很長
  • 適合頻繁的IO操作,例如網絡併發場景

參考

《深入理解Java虛擬機》 –周志明

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章