我使用NIO完成一个大文件复制函数,但发现该函数的执行很不稳定。对此有以下困惑:
- 效率差异严重:每次复制文件耗时差异很大,且无规律,对一个2G的文件赋值,最快3秒,最慢达到20s;
- 内存泄露:JVM占用的内存并没有异常,但OS使用的内存迅速飙升,我猜测应该是“直接内存”的使用,但不清楚如何释放这部分内存;
- 系统负载:我观察到代码执行后,系统 CPU,内存,磁盘都迅速飙高,但代码停止后CPU,磁盘负载不能及时恢复;
环境信息:Windows10,JDK 8,具体代码如下:
public class CopyTest {
private static final String PATH = ""; // TODO
public static void main(String[] args) throws IOException {
for (int i = 0; i < 10; i++) {
fileChannelCopy();
}
System.in.read();
}
private static void fileChannelCopy() throws IOException {
long start = System.currentTimeMillis();
FileChannel read = new FileInputStream(PATH).getChannel();
FileChannel writer = new RandomAccessFile(PATH + "-"
+ new Random(System.currentTimeMillis()).nextInt(), "rw").getChannel();
long readSize = 0;
long size = read.size() / 30;
ByteBuffer toRead, toWrite = null;
while (readSize < read.size() // 未读完
&& (read.size() - readSize) > size) { // 剩余未读大于size
toRead = read.map(FileChannel.MapMode.READ_ONLY, readSize, size);
toWrite = writer.map(FileChannel.MapMode.READ_WRITE, readSize, size);
toWrite.put(toRead);
readSize += size;
toRead.clear();
toWrite.clear();
}
toRead = read.map(FileChannel.MapMode.READ_ONLY, readSize, read.size() - readSize);
assert toWrite != null;
toWrite.put(toRead);
toRead.clear();
toWrite.clear();
read.close();
writer.close();
long end = System.currentTimeMillis();
System.out.println("FileChannel copy file using " + (end - start) / 1000 + "s");
}
某次执行Jprofiler观测到的运行状态
我发现复制的速度与CPU Load密切相关
NIO用在网络上效果很好,但用在文件存取上似乎作用并不明显(除非下载文件这种场景,例如Tomcat的
sendfile
特性)。用NIO和传统方法(FileInput/OutputStream)比较下就知道了。