java四種文件讀寫方式及性能比較


測試代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package  com.boot.demo.test.io;
 
import  java.io.*;
import  java.lang.reflect.Method;
import  java.nio.MappedByteBuffer;
import  java.nio.channels.FileChannel;
import  java.nio.file.Files;
import  java.nio.file.Paths;
import  java.nio.file.StandardOpenOption;
import  java.security.AccessController;
import  java.security.PrivilegedAction;
 
/**
  * @author braska
  * @date 2020/3/19
  **/
public  class  FileTest {
 
     public  static  void  fileStream(String sourceFile, String targetFile) {
         File file =  new  File(targetFile);
         try  (FileInputStream fis =  new  FileInputStream(sourceFile);
              FileOutputStream fos =  new  FileOutputStream(file)) {
             byte [] bytes =  new  byte [ 1024  1024 ];
             int  len;
             while  ((len = fis.read(bytes)) >  0 ) {
                 fos.write(bytes,  0 , len);
             }
         catch  (Exception e) {
 
         }
     }
 
     public  static  void  bufferStream(String sourceFile, String targetFile) {
         try  (BufferedInputStream bis =  new  BufferedInputStream(Files.newInputStream(Paths.get(sourceFile)));
              BufferedOutputStream bos =
                      new  BufferedOutputStream(Files.newOutputStream(Paths.get(targetFile),
                              StandardOpenOption.CREATE,
                              StandardOpenOption.TRUNCATE_EXISTING,
                              StandardOpenOption.WRITE))) {
             byte [] bytes =  new  byte [ 1024  1024 ];
             int  len;
             while  ((len = bis.read(bytes)) >  0 ) {
                 bos.write(bytes,  0 , len);
             }
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
 
     public  static  void  randomFile(String sourceFile, String targetFile) {
         try  (RandomAccessFile read =  new  RandomAccessFile(sourceFile,  "r" );
              RandomAccessFile write =  new  RandomAccessFile(targetFile,  "rw" )) {
             byte [] bytes =  new  byte [ 1024  1024 ];
             int  len;
             while  ((len = read.read(bytes)) >  0 ) {
                 write.write(bytes,  0 , len);
             }
         catch  (Exception e) {
 
         }
     }
 
 
     public  static  void  memoryMap(String sourceFile, String targetFile) {
         try  (FileChannel rc = FileChannel.open(Paths.get(sourceFile));
              FileChannel wc = FileChannel.open(Paths.get(targetFile),
                      StandardOpenOption.CREATE,
                      StandardOpenOption.READ,
                      StandardOpenOption.TRUNCATE_EXISTING,
                      StandardOpenOption.WRITE)) {
             long  copy = 1L <<  30 ;
             long  cur =  0 ;
             long  fileLength = rc.size();
             while  (cur < fileLength) {
                 copy = cur + copy > fileLength ? (fileLength - cur) : copy;
                 MappedByteBuffer rMap = rc.map(FileChannel.MapMode.READ_ONLY, cur, copy);
                 MappedByteBuffer wMap = wc.map(FileChannel.MapMode.READ_WRITE, cur, copy);
                 for  ( int  i =  0 ; i < copy; i++) {
                     byte  b = rMap.get(i);            //從源文件讀取字節
                     wMap.put(i, b);                  //把字節寫到目標文件中
                 }
                 System.gc();                 //手動調用 GC       <必須的,否則出現異常>
                 System.runFinalization();
                 cur += copy;
             }
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
 
     private  static  String buildFilePath(String path, String fileName, String extension) {
         return  String.format( "%s%s.%s" , path, fileName, extension);
     }
 
     public  static  void  main(String[] args) {
/*        String path = "F:\\workspace\\demo\\";
         String extension = "hprof";
         // 30M文件
         String sourceFile = buildFilePath(path, "01", extension);*/
 
/*        String path = "E:\\software\\";
         String extension = "exe";
         // 460M文件
         String sourceFile = buildFilePath(path, "Anaconda3-2019.10-Windows-x86_64", extension);*/
 
         String path =  "E:\\software\\" ;
         String extension =  "zip" ;
         // 1.47G文件
         String sourceFile = buildFilePath(path,  "software" , extension);
         String targetFile;
         long  start;
 
/*        targetFile = buildFilePath(path, "target_file_stream", extension);
         start = System.currentTimeMillis();
         FileTest.fileStream(sourceFile, targetFile);
         System.out.println("file stream used time:" + (System.currentTimeMillis() - start));*/
 
/*        targetFile = buildFilePath(path, "target_buffer_stream", extension);
         start = System.currentTimeMillis();
         FileTest.bufferStream(sourceFile, targetFile);
         System.out.println("buffer stream used time:" + (System.currentTimeMillis() - start));*/
 
/*        targetFile = buildFilePath(path, "target_random_file", extension);
         start = System.currentTimeMillis();
         FileTest.randomFile(sourceFile, targetFile);
         System.out.println("random file used time:" + (System.currentTimeMillis() - start));*/
 
 
         targetFile = buildFilePath(path,  "target_memory_map" , extension);
         start = System.currentTimeMillis();
         FileTest.memoryMap(sourceFile, targetFile);
         System.out.println( "memory map used time:"  + (System.currentTimeMillis() - start));
     }
}

  

測試結果

文件大小 讀寫方式 耗時
30M 普通文件流 50-60 ms
緩存流 32-35 ms
隨機文件方式 40-50 ms
內存映射文件 50-60 ms
461M 普通文件流 1300-2300 ms
緩存流 1700-2000 ms
隨機文件方式 1300-3000 ms
內存映射文件 890-1000 ms
1.47G 普通文件流 11s
緩存流 9s
隨機文件方式 10s
內存映射文件 3s(首次較慢)

 

 

 

 

 

 

 

 

 

 

 

結尾:測試1.47G大文件時,內存映射文件中copy大小做了調整,當copy為1G時(copy=1L<<30)性能最佳,整過過程1-3秒左右。調至128M、512M。大約耗時都在15秒左右。為了公平起見,把其他方法中的byte緩沖區大小也做了同樣調整,耗時變化不大。有些甚至更慢。

 

封裝MappedBuffer工具類,代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package  com.boot.demo.test.io;
 
import  java.io.Closeable;
import  java.io.IOException;
import  java.nio.MappedByteBuffer;
import  java.nio.channels.FileChannel;
import  java.nio.file.Paths;
import  java.nio.file.StandardOpenOption;
import  java.util.concurrent.ArrayBlockingQueue;
import  java.util.concurrent.BlockingQueue;
 
/**
  * @author braska
  * @date 2020/3/20
  **/
public  class  MappedByteBufferReader  implements  Closeable {
 
     private  static  final  int  BUFFERED_SIZE =  1  <<  27 ;
     private  static  final  FileChannel.MapMode MAP_MODE = FileChannel.MapMode.READ_ONLY;
 
     private  FileChannel fileChannel;
     private  final  long  fileSize;
     private  final  int  bufferedSize;
 
     BlockingQueue<MappedByteBuffer> queue;
 
     public  MappedByteBufferReader(String file)  throws  Exception {
         this (file, BUFFERED_SIZE, MAP_MODE);
     }
 
     public  MappedByteBufferReader(String file,  int  bufferedSize)  throws  Exception {
         this (file, bufferedSize, MAP_MODE);
     }
 
     public  MappedByteBufferReader(String file, FileChannel.MapMode mapMode)  throws  Exception {
         this (file, BUFFERED_SIZE, mapMode);
     }
 
     public  MappedByteBufferReader(String file,  int  bufferedSize, FileChannel.MapMode mapMode)  throws  Exception {
         this .fileChannel = FileChannel.open(Paths.get(file));
         this .fileSize = fileChannel.size();
         this .bufferedSize = bufferedSize;
         int  capacity = ( int ) Math.ceil(( double ) fileSize / ( double ) bufferedSize);
         this .queue =  new  ArrayBlockingQueue(capacity);
         long  readSize = bufferedSize;
         long  cursor = 0l;
         while  (cursor < fileSize) {
             readSize = cursor + readSize > fileSize ? fileSize - cursor : readSize;
             queue.add(
                     fileChannel.map(mapMode, cursor, readSize)
             );
             cursor += readSize;
         }
     }
 
     public  byte [] read() {
         byte [] bytes;
         MappedByteBuffer byteBuffer = queue.poll();
         if  (byteBuffer !=  null ) {
             int  limit = byteBuffer.limit();
             int  position = byteBuffer.position();
 
             int  realSize =  this .bufferedSize;
             if  (limit - position <  this .bufferedSize) {
                 realSize = limit - position;
             }
             bytes =  new  byte [realSize];
             byteBuffer.get(bytes);
             byteBuffer.clear();
             return  bytes;
         }
 
         return  null ;
     }
 
     public  long  size() {
         return  this .fileSize;
     }
 
     @Override
     public  void  close()  throws  IOException {
         if  ( this .fileChannel !=  null ) {
             this .fileChannel.close();
         }
     }
 
     public  static  void  main(String[] args) {
         long  start = System.currentTimeMillis();
         try  (MappedByteBufferReader reader =  new  MappedByteBufferReader( "E:\\software\\software.zip" 1  <<  30 );
              FileChannel wc = FileChannel.open(Paths.get( "E:\\software\\software_reader.zip" ),
                      StandardOpenOption.CREATE,
                      StandardOpenOption.READ,
                      StandardOpenOption.TRUNCATE_EXISTING,
                      StandardOpenOption.WRITE)) {
             byte [] data;
             MappedByteBuffer writer = wc.map(FileChannel.MapMode.READ_WRITE,  0 , reader.size());
             while  ((data = reader.read()) !=  null ) {
                 writer.put(data);
             }
             writer.clear();
             System.out.println( "used times: "  + (System.currentTimeMillis() - start));
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
}

 


免責聲明!

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



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