程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> MappedByteBuffer高速緩存文件、RandomAccessFile隨機訪問

MappedByteBuffer高速緩存文件、RandomAccessFile隨機訪問

編輯:C++入門知識

MappedByteBuffer高速緩存文件、RandomAccessFile隨機訪問


說到高速緩存存儲,處理讀寫文件,那就不得不說MappedByteBuffer。

看了好多文章以後寫一下自己的總結。


在這裡先介紹一下相關的類與方法。

先說一下Buffer、ByteBuffer、MappedByteBuffer這幾個類之間的關系。


public abstract class Buffer {

    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;
    long address;
    ......
}

public abstract class ByteBuffer extends Buffer implements Comparable {

    // These fields are declared here rather than in Heap-X-Buffer in order to
    // reduce the number of virtual method invocations needed to access these
    // values, which is especially costly when coding small buffers.
    //
    final byte[] hb;   // Non-null only for heap buffers
    final int offset;
    boolean isReadOnly;   // Valid only for heap buffers
    boolean bigEndian;
    boolean nativeByteOrder;
    ......
}
//字節數組final byte[] hb就是所指的那塊內存緩沖區

public abstract class MappedByteBuffer extends ByteBuffer{
	private final FileDescriptor fd;
	......
}


public abstract class MappedByteBuffer extends ByteBuffer 直接字節緩沖區,其內容是文件的內存映射區域。

映射的字節緩沖區是通過 FileChannel.map 方法創建的。此類用特定於內存映射文件區域的操作擴展 ByteBuffer 類。
映射的字節緩沖區和它所表示的文件映射關系在該緩沖區本身成為垃圾回收緩沖區之前一直保持有效。
映射的字節緩沖區的內容可以隨時更改,例如,在此程序或另一個程序更改了對應的映射文件區域的內容的情況下。這些更改是否發生(以及何時發生)與操作系統無關,因此是未指定的。
全部或部分映射的字節緩沖區可能隨時成為不可訪問的,例如,如果我們截取映射的文件。試圖訪問映射的字節緩沖區的不可訪問區域將不會更改緩沖區的內容,並導致在訪問時或訪問後的某個時刻拋出未指定的異常。因此強烈推薦采取適當的預防措施,以避免此程序或另一個同時運行的程序對映射的文件執行操作(讀寫文件內容除外)。

除此之外,映射的字節緩沖區的功能與普通的直接字節緩沖區完全相同。


public RandomAccessFile(File file, String mode)throws FileNotFoundException
創建從中讀取和向其中寫入(可選)的隨機訪問文件流,該文件由 File 參數指定。將創建一個新的 FileDescriptor 對象來表示此文件的連接。
r" 以只讀方式打開。調用結果對象的任何 write 方法都將導致拋出 IOException。
"rw" 打開以便讀取和寫入。如果該文件尚不存在,則嘗試創建該文件。
"rws" 打開以便讀取和寫入,對於 "rw",還要求對文件的內容或元數據的每個更新都同步寫入到底層存儲設備。
"rwd" 打開以便讀取和寫入,對於 "rw",還要求對文件內容的每個更新都同步寫入到底層存儲設備。


public final FileChannel getChannel()返回與此文件關聯的唯一 FileChannel 對象。
返回通道的 java.nio.channels.FileChannel#position()position 將始終等於 getFilePointer 方法返回的此對象的文件指針偏移量。顯式或者通過讀取或寫入字節來更改此對象的文件指針偏移量將更改通道的位置,反之亦然。通過此對象更改此文件的長度將更改通過文件通道看到的長度,反之亦然。

public abstract MappedByteBuffer map(FileChannel.MapMode mode,long position,long size)throws IOException將此通道的文件區域直接映射到內存中。
mode - 根據是按只讀、讀取/寫入或專用(寫入時拷貝)來映射文件,分別為 FileChannel.MapMode類中所定義的 READ_ONLY、READ_WRITE 或 PRIVATE 之一
position - 文件中的位置,映射區域從此位置開始;必須為非負數
size - 要映射的區域大小;必須為非負數且不大於 Integer.MAX_VALUE


下面一個運行的例子:

package com.tzx.ne;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MappedByteBufferDemo {
	public static void main(String[] args) throws Exception{
		/**
		 * output: 0.001s(讀)
		 * input: 0.11s(寫)
		 * */
		MappedByteBufferTest();
		/**
		 * size=1024*8
		 * out: 0.0s
		 * input: 0.014s
		 * */
		/**
		 * size=1024*1024*8
		 * output: 0.01s
		 * input: 0.014s
		 * */
		/**
		 * size=80
		 * output: 0.0s
		 * input: 0.546s
		 * */
		//BufferTest();
		/**
		 * time: 0.585s
		 * */
		//BufferedInputStreamTest();
	}
	
	
	
	/*
	 * 測試結果與Buffer size有關
	 */
	// 1、使用MappedByteBuffer: 0.7s
	public static void MappedByteBufferTest() throws Exception{
		String srcFile = "F:\\Ebook\\偷天.txt";
		String destFile = "F:\\Ebook\\toutian.txt";
		RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
		RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");
		FileChannel fci = rafi.getChannel();
		FileChannel fco = rafo.getChannel();
		long size = fci.size();
		byte b;
		long start = System.currentTimeMillis();
		MappedByteBuffer mbbi = fci.map(FileChannel.MapMode.READ_ONLY, 0, size);
		System.out.println("output: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
		MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, 0, size);
		start = System.currentTimeMillis();
		for (int i = 0; i < size; i++) {
			b = mbbi.get(i);
			mbbo.put(i, b);
		}
		fci.close();
		fco.close();
		rafi.close();
		rafo.close();
		System.out.println("input: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
	}


	// 2、自己處理Buffer(RandomAccessFile): 0.13s	
	public static void BufferTest() throws Exception{
		String srcFile = "F:\\Ebook\\偷天.txt";
		String destFile = "F:\\Ebook\\toutian.txt";
		RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
		RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");

		byte[] buf = new byte[80];
		long start = System.currentTimeMillis();
		int c = rafi.read(buf);
		System.out.println("output: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
		start = System.currentTimeMillis();
		while (c > 0) {
			if (c == buf.length) {
				rafo.write(buf);
			} else {
				rafo.write(buf, 0, c);
			}

			c = rafi.read(buf);
		}
		System.out.println("input: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
		rafi.close();
		rafo.close();

	}

	// 3、BufferedInputStream&BufferedOutputStream: 3.02s
	public static void BufferedInputStreamTest() throws Exception{
		String srcFile = "F:\\Ebook\\偷天.txt";
		String destFile = "F:\\Ebook\\toutian.txt";
		FileInputStream rafi = new FileInputStream(srcFile);
		FileOutputStream rafo = new FileOutputStream(destFile);

		BufferedInputStream bis = new BufferedInputStream(rafi, 8192);
		BufferedOutputStream bos = new BufferedOutputStream(rafo, 8192);
		long size = rafi.available();

		long start = System.currentTimeMillis();

		for (int i = 0; i < size; i++) {
			byte b = (byte) bis.read();
			bos.write(b);
		}
		rafi.close();
		rafo.close();
		System.out.println("time: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");

	}
}


總結:

1、RandomAccessFile是Java輸入輸出流體系中功能最豐富的文件內容訪問類,他提供 了眾多的方法來訪問文件,它既可以讀取文件的內容,也可以說向文件輸出數據,本身不帶緩沖讀寫,和FileInputStream、FileOutputStream等一樣,直接按字節讀寫時,性能不可接受;


2、使用MappedByteBuffer讀寫,固然性能會得到極大提升;其實只要自己處理緩沖,性能都會有非常大的提升,比如以下兩種方式中第一種使用了MappedByteBuffer,第二種自己進行緩沖處理後,對於幾兆的文件,後者的效率甚至高於前者,可以從幾個size大小看出運行速度,當size較大的時候一次性的讀取速度是慢些,但是整體的效率非常之高。


3、BufferedXXXX之類的緩沖流,如果僅使用默認的buffer size,性能不一定最優,要權衡不同情況各種因素設置大小。



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved