本文介绍了不同的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的性能。
- 万一发生了电源故障或主机故障,将会有很小的机率发生内存映射文件没有写入到磁盘,这意味着你可能会丢失关键数据。