Java常用的几种IO性能对比
最近最常做的工作就是各种文件读写操作,所以来真实的看看,各种Io方式之间的性能对比
FileOutputStream与BufferedWriter
两种区别不大,且与使用NIO来写文件速度区别也不大
1
2
3
4
5
6
7
8
9
10
11
| private void writeBuffer(File file) throws IOException {
FileOutputStream fos = new FileOutputStream(file);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos));
int i=1000000;
while(i>0){
writer.write(word2048);
i--;
}
writer.close();
fos.close();
}
|
ByteBuffer与直接内存
采不采用直接内存几乎没有区别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| private void byteBuffer(File file) throws IOException {
FileOutputStream fos = new FileOutputStream(file);
FileChannel fc = fos.getChannel();
byte[] datas = word2048.getBytes();
ByteBuffer bbuf = ByteBuffer.allocate(4800 * 100);
int i=10000;
while(i>0){
for(int j=0;j<100;j++){
bbuf.put(datas);
}
bbuf.flip();
fc.write(bbuf);
bbuf.clear();
i--;
}
}
|
FileChannel与文件空洞
NIO中,FileChannel可以决定文件写入的位置,通常用这样来产生文件空洞
MappedByteBuffer
使用直接内存映射,来提升IO性能
这个有一个重大bug,关闭FileChannel后,MappedByteBuffer不会释放所持有的文件。到之后需要如果再对写入的文件进行操作时,会导致没有权限。MappedByteBuffer所持有的内存需要手动来释放。释放方式有几种:
- 直接调用System.gc();
- 利用反射来释放
1
2
3
4
| Method m = FileChannelImpl.class.getDeclaredMethod("unmap",
MappedByteBuffer.class);
m.setAccessible(true);
m.invoke(FileChannelImpl.class, buffer);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
try {
Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
getCleanerMethod.setAccessible(true);
sun.misc.Cleaner cleaner = (sun.misc.Cleaner)
getCleanerMethod.invoke(byteBuffer, new Object[0]);
cleaner.clean();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
|
2和3两种释放方式,可能会根据jdk的版本而有所不同。(实测1.8是无法通过反射释放内存的)

内存文件映射的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| private void mappedByteBuffer(File file) throws IOException {
RandomAccessFile acf = new RandomAccessFile(file, "rw");
FileChannel fc = acf.getChannel();
byte[] bs = word2048.getBytes();
int len = bs.length * 1000;
long offset=0;
int i=2000000;
while(i>0){
MappedByteBuffer mbuf = fc.map(FileChannel.MapMode.READ_WRITE, offset, len);
for(int j=0;j<1000;j++){
mbuf.put(bs);
}
offset=offset+len;
i=i-1000;
}
fc.close();
}
|
利用apache-common向tar中压缩文件
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
| // 打一个tar压缩
private void tar(File srcDir,String targetFile) throws IOException {
try(TarArchiveOutputStream tos=new TarArchiveOutputStream(Files.newOutputStream(Paths.get(targetFile)))){
tarRecursive(tos,srcDir,"");
}
}
private void tarRecursive(TarArchiveOutputStream tos,File srcFile,String BasePath) throws IOException {
// 递归打包文件夹
if(srcFile.isDirectory()){
File[] files=srcFile.listFiles();
String nextBasePath = BasePath + srcFile.getName() + "/";
if(files==null) {
TarArchiveEntry entry = new TarArchiveEntry(srcFile, nextBasePath);
tos.putArchiveEntry(entry);
}else{
for (File file : files) {
tarRecursive(tos,file,nextBasePath);
}
}
int[][] nums = new int[2][2];
Arrays.sort(nums, (a, b) -> a[0]-b[0]);
}else{
TarArchiveEntry entry=new TarArchiveEntry(srcFile,srcFile.getName());
tos.putArchiveEntry(entry);
FileUtils.copyFile(srcFile,tos);
tos.closeArchiveEntry();
}
}
|