Java IO和Java NIO在文件拷貝上的性能差異分析


1.  在JAVA傳統的IO系統中,讀取磁盤文件數據的過程如下:

以FileInputStream類為例,該類有一個read(byte b[])方法,byte b[]是我們要存儲讀取到用戶空間的緩沖區。參看read(byte b[])方法的源碼,可知,它會在內部再調用readBytes(b, 0, b.length)方法,而且readBytes(b, 0, b.length)方法是一個native方法(即本地方法),最終通過這個本地方法來發起一次系統調用,即調用系統內核的read()方法,內核從磁盤讀取數據到內核緩沖區,這個過程由磁盤控制器通過DMA操作將數據從磁盤讀取取內核緩沖區,此過程不依賴於CPU。然后用戶進程再將數據從內核緩沖區拷貝到用戶空間緩沖區。用戶進程再從用戶空間緩沖區中讀取數據。因為用戶進程是不可以直接訪問硬件的。所以需要通過內核來充當中間人的作用來實現文件的讀取。整個過程如下圖所示:

          Java IO和Java NIO在文件拷貝上的性能差異分析

2.  自從JAVA 1.4以后,JAVA在NIO在引入了文件通道的概念,在API中有提供了一個FileChannel類。該類與傳統的IO流進行關聯。可以由FileInputStream或FileOutputStream獲取該文件通道,我們可以通過通道對文件進行讀寫操作。

 

3.JAVA NIO中還引入了文件內存映射的概念:現代操作系統大都支持虛擬內存映射,這樣,我們可以把內核空間地址與用戶空間的虛擬地址映射到同一個物理地址,這樣,DMA 硬件(只能訪問物理內存地址)就可以填充對內核與用戶空間進程同時可見的緩沖區了。如下圖所示:

 

          Java IO和Java NIO在文件拷貝上的性能差異分析

這樣做的好處是,我們在讀取磁盤文件時,再也不用通過內核緩沖區到用戶進程緩沖區的來回拷貝操作了。操作系統會通過一些頁面調度算法來將磁盤文件載入對分頁區進行高速緩存的物理內存。我們就可以通過映射后物理內存來讀取磁盤文件了。

 

4.  下面我們通過三種不同方式文件拷貝的案例來驗證文件通道及文件內存映射在IO系統中的作用。測試環境為windows 32位系統和JDK1.6。代碼中使用的測試文件movie.avi為一個123MB的視頻文件。代碼如下:

  1 package cn.com.hbust.nio.file; 
  2    
  3 import java.io.File; 
  4    
  5 import java.io.FileInputStream; 
  6    
  7 import java.io.FileOutputStream; 
  8    
  9 import java.nio.MappedByteBuffer; 
 10    
 11 import java.nio.channels.FileChannel; 
 12    
 13     
 14    
 15 public class FileCopyTest { 
 16    
 17     
 18    
 19     public  static  void main(String[] args) throws Exception { 
 20    
 21        String sourcePath = "F:\\mywork\\javademo\\dir1\\movie.avi"; 
 22    
 23        String destPath1 = "F:\\mywork\\javademo\\dir2\\movie1.avi"; 
 24    
 25        String destPath2 = "F:\\mywork\\javademo\\dir2\\movie2.avi"; 
 26    
 27        String destPath3 = "F:\\mywork\\javademo\\dir2\\movie3.avi"; 
 28    
 29        long t1 = System.currentTimeMillis(); 
 30    
 31        traditionalCopy(sourcePath,destPath1); 
 32    
 33        long t2 = System.currentTimeMillis(); 
 34    
 35        System.out.println("傳統IO方法實現文件拷貝耗時:" + (t2-t1) + "ms"); 
 36    
 37          
 38    
 39        nioCopy(sourcePath,destPath2); 
 40    
 41        long t3 = System.currentTimeMillis(); 
 42    
 43        System.out.println("利用NIO文件通道方法實現文件拷貝耗時:" + (t3-t2) + "ms"); 
 44    
 45          
 46    
 47        nioCopy2(sourcePath,destPath3); 
 48    
 49        long t4 = System.currentTimeMillis(); 
 50    
 51        System.out.println("利用NIO文件內存映射及文件通道實現文件拷貝耗時:" + (t4-t3) + "ms"); 
 52    
 53          
 54    
 55     } 
 56    
 57     
 58    
 59     private  static  void nioCopy2(String sourcePath, String destPath) throws Exception { 
 60    
 61        File source = new File(sourcePath); 
 62    
 63        File dest = new File(destPath); 
 64    
 65        if(!dest.exists()) { 
 66    
 67            dest.createNewFile(); 
 68    
 69        } 
 70    
 71        FileInputStream fis = new FileInputStream(source); 
 72    
 73        FileOutputStream fos = new FileOutputStream(dest); 
 74    
 75        FileChannel sourceCh = fis.getChannel(); 
 76    
 77        FileChannel destCh = fos.getChannel(); 
 78    
 79        MappedByteBuffer mbb = sourceCh.map(FileChannel.MapMode.READ_ONLY, 0, sourceCh.size()); 
 80    
 81        destCh.write(mbb); 
 82    
 83        sourceCh.close(); 
 84    
 85        destCh.close(); 
 86    
 87     } 
 88    
 89     
 90    
 91     
 92    
 93     private  static  void traditionalCopy(String sourcePath, String destPath) throws Exception{ 
 94    
 95        File source = new File(sourcePath); 
 96    
 97        File dest = new File(destPath); 
 98    
 99        if(!dest.exists()) { 
100    
101            dest.createNewFile(); 
102    
103        } 
104    
105        FileInputStream fis = new FileInputStream(source); 
106    
107        FileOutputStream fos = new FileOutputStream(dest); 
108    
109        byte [] buf = newbyte [512]; 
110    
111        int len = 0; 
112    
113        while((len = fis.read(buf)) != -1) { 
114    
115            fos.write(buf, 0, len); 
116    
117        } 
118    
119        fis.close(); 
120    
121        fos.close(); 
122    
123     } 
124    
125     
126    
127     private  static  void nioCopy(String sourcePath, String destPath) throws Exception{ 
128    
129        File source = new File(sourcePath); 
130    
131        File dest = new File(destPath); 
132    
133        if(!dest.exists()) { 
134    
135            dest.createNewFile(); 
136    
137        } 
138    
139        FileInputStream fis = new FileInputStream(source); 
140    
141        FileOutputStream fos = new FileOutputStream(dest); 
142    
143        FileChannel sourceCh = fis.getChannel(); 
144    
145        FileChannel destCh = fos.getChannel(); 
146    
147        destCh.transferFrom(sourceCh, 0, sourceCh.size()); 
148    
149        sourceCh.close(); 
150    
151        destCh.close(); 
152    
153     } 
154    
155 } 

 每執行完一次拷貝之后,將F:\mywork\javademo\dir2\目錄中的內容刪除掉,重復執行8次。觀察測試結果如下:時間單位為ms(毫秒)

          Java IO和Java NIO在文件拷貝上的性能差異分析

由上表可知,傳統IO方式平均拷貝完成時間約為1968ms,NIO文件通道方式平均拷貝完成時間約為1672ms,文件內存映射及文件通道方式平均拷貝完成時間約為1418ms。

轉載自http://www.open-open.com/lib/view/open1413518521372.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM