本文介紹了不同的IO方式以及他們之間的效率比較
1.一次讀取寫入單個字節(讀取400M的文件浪費了很久,等了很久沒讀取完成,證明其效率很差)
1 public class CopyFileDemo { 2 public static void main(String[] args) throws IOException { 3 FileInputStream fis = new FileInputStream("a.txt"); 4 FileOutputStream fos = new FileOutputStream("b.txt"); 5 6 // 方式一:一次讀取寫入單個字節 7 int i = 0; 8 while ((i = fis.read()) != -1) { 9 fos.write(i); 10 } 11 fos.close(); 12 fis.close(); 13 } 14 }
2.一次讀取寫入多個字節(讀取400M的文件700ms)
1 public class CopyFileDemo { 2 public static void main(String[] args) throws IOException { 3 FileInputStream fis = new FileInputStream("a.txt"); 4 FileOutputStream fos = new FileOutputStream("b.txt"); 5 // 方式二:一次讀取寫入一個字節數組 6 byte[] by = new byte[1024]; 7 int len = 0; 8 while ((len = fis.read(by)) != -1) { 9 fos.write(by, 0, len); 10 } 11 fos.close(); 12 fis.close(); 13 } 14 }
3.文件流輸入輸出(讀取400M的文件5000ms,為什么更慢呢,猜測是readline這里,大神可以指出來)
1 public class CopyFileDemo3 { 2 public static void main(String[] args) throws IOException { 3 BufferedReader br=new BufferedReader(new FileReader("a.txt")); 4 //如果d文件中有數據,true表示繼續往文件中追加數據 5 BufferedWriter bw=new BufferedWriter(new FileWriter("d.txt",true)); 6 7 String line=null; 8 //高效字符輸入流的特有方法readline(),每次讀取一行數據 9 while((line=br.readLine())!=null){ 10 bw.write(line); 11 //高效字符輸出流的特有方法newline() 12 bw.newLine(); 13 //將緩沖區中的數據刷到目的地文件中 14 bw.flush(); 15 } 16 //關閉流,其實關閉的就是java調用的系統底層資源。在關閉前,會先刷新該流。 17 bw.close(); 18 br.close(); 19 } 20 }
BufferedInputStream 會根據情況自動為我們預讀更多的字節數據到它自己維護的一個內部字節數組緩沖區中,這樣我們便可以減少系統調用次數,從而達到其緩沖區的目的。所以要明確的一點是 BufferedInputStream 的作用不是減少 磁盤IO操作次數(這個OS已經幫我們做了),而是通過減少系統調用次數來提高性能的。
4.NIO讀取 (400M的視頻文件,讀取要長達700ms)
1 public class ReadDemo{ 2 public static void main(String[] args) throws IOException { 3 File file = new File("sdtgj.mp4"); 4 FileInputStream in = new FileInputStream(file); 5 FileChannel channel = in.getChannel(); 6 ByteBuffer buff = ByteBuffer.allocate(1024); 7 8 long begin = System.currentTimeMillis(); 9 while (channel.read(buff) != -1) { 10 buff.flip(); 11 buff.clear(); 12 } 13 long end = System.currentTimeMillis(); 14 System.out.println("time is:" + (end - begin)+"毫秒 "+"讀取 "+file.getName()); 15 } 16 }
5.內存映射讀取 (400M的視頻文件,讀取只要100ms)
1 public class ReadDemo{ 2 3 static final int BUFFER_SIZE = 1024; 4 5 public static void main(String[] args) throws Exception { 6 7 File file = new File("sdtgj.mp4"); 8 FileInputStream in = new FileInputStream(file); 9 FileChannel channel = in.getChannel(); 10 MappedByteBuffer buff = channel.map(FileChannel.MapMode.READ_ONLY, 0, 11 channel.size()); 12 13 byte[] b = new byte[1024]; 14 int len = (int) file.length(); 15 16 long begin = System.currentTimeMillis(); 17 18 for (int offset = 0; offset < len; offset += 1024) { 19 20 if (len - offset > BUFFER_SIZE) { 21 buff.get(b); 22 } else { 23 buff.get(new byte[len - offset]); 24 } 25 } 26 27 long end = System.currentTimeMillis(); 28 System.out.println("time is:" + (end - begin)+"毫秒 "+"讀取 "+file.getName()); 29 30 } 31 }
MappedByteBuffer 不受JVM堆大小控制,速度最快。
MappedByteBuffer 的要點:
- java通過java.nio包來支持內存映射IO。
- 內存映射文件主要用於性能敏感的應用,例如高頻電子交易平台。
- 通過使用內存映射IO,你可以將大文件加載到內存。
- 內存映射文件可能導致頁面請求錯誤,如果請求頁面不在內存中的話。
- 映射文件區域的能力取決於於內存尋址的大小。在32位機器中,你不能訪問超過4GB或2 ^ 32(以上的文件)。
- 內存映射IO比起Java中的IO流要快的多。
- 加載文件所使用的內存是Java堆區之外,並駐留共享內存,允許兩個不同進程共享文件。
- 內存映射文件讀寫由操作系統完成,所以即使在將內容寫入內存后java程序崩潰了,它將仍然會將它寫入文件直到操作系統恢復。
- 出於性能考慮,推薦使用直接字節緩沖而不是非直接緩沖。
- 不要頻繁調用MappedByteBuffer.force()方法,這個方法意味着強制操作系統將內存中的內容寫入磁盤,所以如果你每次寫入內存映射文件都調用force()方法,你將不會體會到使用映射字節緩沖的好處,相反,它(的性能)將類似於磁盤IO的性能。
- 萬一發生了電源故障或主機故障,將會有很小的機率發生內存映射文件沒有寫入到磁盤,這意味着你可能會丟失關鍵數據。
