1. 程式人生 > >java nio原理和它的優點

java nio原理和它的優點

    要想講清楚nio的原理和它的優點得先清楚Java應用程式的檔案讀寫原理和虛擬記憶體的原理。Java檔案讀取原理可參見如下圖:


當應用程式需要讀取檔案的時候,核心首先通過DMA技術將檔案內容從磁碟讀入核心中的buffer,然後Java應用程序再從核心的buffer將資料讀取到應用程式的buffer。

為了提升I/O效率和處理能力,作業系統採用虛擬記憶體的機制。虛擬記憶體也就是我們常說的交換記憶體,它實際上是硬碟上的檔案,虛擬記憶體有兩個作用:

1. 不同的虛擬記憶體可以對映到相同的實體記憶體,根據這個原理,可以簡化檔案讀取流程,提升讀取效率,效果如下圖所示:


通過使用虛擬記憶體技術,將應用程式的buffer和核心的buffer都作為虛擬記憶體,並且兩塊不同的虛擬記憶體指向相同的實體記憶體,核心通過DMA將資料讀取到buffer的時候,應用程式就可以直接使用這些資料了。

2. 通過使用虛擬記憶體, 應用程式可以使用比實體記憶體所能容納得大得多的記憶體,並且也能夠提高I/O效率。當實體記憶體中的資料不使用的時候,可以將實體記憶體中的資料放到虛擬記憶體中,作業系統就可以騰出實體記憶體空間儲存新的要處理的資料。當需要使用虛擬記憶體中的資料時,再可以把虛擬記憶體中的資料載入到實體記憶體中。因此實體記憶體可以看做時虛擬記憶體中存放資料的cache。而上面所述的cache虛擬記憶體資料的過程,對應用程式來說時透明的,它可以像處理實體記憶體資料一樣處理虛擬記憶體中的資料。

Java nio通過使用虛擬記憶體技術將檔案系統中的檔案頁和應用程式空間直接對應起來,使用nio後,檔案的讀寫操作都是在虛擬記憶體中實現。這樣在操作檔案的時候,好像檔案已經在記憶體中一樣。採用了虛擬記憶體技術,使用Java nio方式可以很快的讀取很大的檔案。

Java nio開啟檔案可指定對映內容在所要對映的檔案所在位置。使用Java nio開啟檔案原始碼:

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
 
public class MemoryMappedFileExample
{
    static int length = 0x8FFFFFF; // 128 Mb
 
    public static void main(String[] args) throws Exception
    {
        MappedByteBuffer out = new RandomAccessFile("howtodoinjava.dat", "rw")
                                    .getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length);
        for (int i = 0; i < length; i++)
        {
            out.put((byte) 'x');
        }
        System.out.println("Finished writing");
    }
}


使用Java nio讀取檔案內容原始碼:

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
 
public class MemoryMappedFileReadExample
{
    private static String bigExcelFile = "bigFile.xls";
 
    public static void main(String[] args) throws Exception
    {
        //Create file object
        File file = new File(bigExcelFile);
         
        //Get file channel in readonly mode
        FileChannel fileChannel = new RandomAccessFile(file, "r").getChannel();
         
        //Get direct byte buffer access using channel.map() operation
        MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
         
        // the buffer now reads the file as if it were loaded in memory.
        System.out.println(buffer.isLoaded());  //prints false
        System.out.println(buffer.capacity());  //Get the size based on content size of file
         
        //You can read the file from this buffer the way you like.
        for (int i = 0; i < buffer.limit(); i++)
        {
            System.out.print((char) buffer.get()); //Print the content of file
        }
    }
}


使用Java nio寫入檔案新的內容:

import java.io.File;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
 
public class MemoryMappedFileWriteExample {
    private static String bigExcelFile = "test.txt";
 
    public static void main(String[] args) throws Exception {
        // Create file object
        File file = new File(bigExcelFile);
         
        //Delete the file; we will create a new file
        file.delete();
 
        // Get file channel in readonly mode
        FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();
 
        // Get direct byte buffer access using channel.map() operation
        MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 4096 * 8 * 8);
 
        //Write the content using put methods
        buffer.put("howtodoinjava.com".getBytes());
    }
}


可見Java nio的優勢為:

1. 不需要使用read()或者write()操作就可以處理檔案內容了

2. 修改檔案後,修改自動flush到檔案

3. nio方式能很快處理大檔案和處理效率很快

參考文章:

http://howtodoinjava.com/2014/12/10/how-java-io-works-internally-at-lower-level/

http://howtodoinjava.com/2015/01/16/java-nio-2-0-memory-mapped-files-mappedbytebuffer-tutorial/