자바 NIO 큰 파일 비교(win 7 과 mac)
2G 파일 을 쓰 고 일괄 적 으로 기록 하 며 일괄 적 으로 128 MB 를 기록 합 니 다.
각각 Win 7 시스템(3G 메모리,쌍 핵,32 비트,T 시리즈 프로세서)과 MacOS 시스템(8G 메모리,4 핵,64 비트,i7 시리즈 프로세서)에서 테스트 를 실행 합 니 다.이론 적 으로 하 드 디스크 의 유형 과 배치 와 도 관계 가 있 으 므 로 여 기 는 더 이상 붙 이지 않 습 니 다.
테스트 코드
package rwbigfile;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.channels.ReadableByteChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import util.StopWatch;
/**
* NIO
* @author Will
*
*/
public class WriteBigFileComparison {
// data chunk be written per time
private static final int DATA_CHUNK = 128 * 1024 * 1024;
// total data size is 2G
private static final long LEN = 2L * 1024 * 1024 * 1024L;
public static void writeWithFileChannel() throws IOException {
File file = new File("e:/test/fc.dat");
if (file.exists()) {
file.delete();
}
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel fileChannel = raf.getChannel();
byte[] data = null;
long len = LEN;
ByteBuffer buf = ByteBuffer.allocate(DATA_CHUNK);
int dataChunk = DATA_CHUNK / (1024 * 1024);
while (len >= DATA_CHUNK) {
System.out.println("write a data chunk: " + dataChunk + "MB");
buf.clear(); // clear for re-write
data = new byte[DATA_CHUNK];
for (int i = 0; i < DATA_CHUNK; i++) {
buf.put(data[i]);
}
data = null;
buf.flip(); // switches a Buffer from writing mode to reading mode
fileChannel.write(buf);
fileChannel.force(true);
len -= DATA_CHUNK;
}
if (len > 0) {
System.out.println("write rest data chunk: " + len + "B");
buf = ByteBuffer.allocateDirect((int) len);
data = new byte[(int) len];
for (int i = 0; i < len; i++) {
buf.put(data[i]);
}
buf.flip(); // switches a Buffer from writing mode to reading mode, position to 0, limit not changed
fileChannel.write(buf);
fileChannel.force(true);
data = null;
}
fileChannel.close();
raf.close();
}
/**
* write big file with MappedByteBuffer
* @throws IOException
*/
public static void writeWithMappedByteBuffer() throws IOException {
File file = new File("e:/test/mb.dat");
if (file.exists()) {
file.delete();
}
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel fileChannel = raf.getChannel();
int pos = 0;
MappedByteBuffer mbb = null;
byte[] data = null;
long len = LEN;
int dataChunk = DATA_CHUNK / (1024 * 1024);
while (len >= DATA_CHUNK) {
System.out.println("write a data chunk: " + dataChunk + "MB");
mbb = fileChannel.map(MapMode.READ_WRITE, pos, DATA_CHUNK);
data = new byte[DATA_CHUNK];
mbb.put(data);
data = null;
len -= DATA_CHUNK;
pos += DATA_CHUNK;
}
if (len > 0) {
System.out.println("write rest data chunk: " + len + "B");
mbb = fileChannel.map(MapMode.READ_WRITE, pos, len);
data = new byte[(int) len];
mbb.put(data);
}
data = null;
unmap(mbb); // release MappedByteBuffer
fileChannel.close();
}
public static void writeWithTransferTo() throws IOException {
File file = new File("e:/test/transfer.dat");
if (file.exists()) {
file.delete();
}
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel toFileChannel = raf.getChannel();
long len = LEN;
byte[] data = null;
ByteArrayInputStream bais = null;
ReadableByteChannel fromByteChannel = null;
long position = 0;
int dataChunk = DATA_CHUNK / (1024 * 1024);
while (len >= DATA_CHUNK) {
System.out.println("write a data chunk: " + dataChunk + "MB");
data = new byte[DATA_CHUNK];
bais = new ByteArrayInputStream(data);
fromByteChannel = Channels.newChannel(bais);
long count = DATA_CHUNK;
toFileChannel.transferFrom(fromByteChannel, position, count);
data = null;
position += DATA_CHUNK;
len -= DATA_CHUNK;
}
if (len > 0) {
System.out.println("write rest data chunk: " + len + "B");
data = new byte[(int) len];
bais = new ByteArrayInputStream(data);
fromByteChannel = Channels.newChannel(bais);
long count = len;
toFileChannel.transferFrom(fromByteChannel, position, count);
}
data = null;
toFileChannel.close();
fromByteChannel.close();
}
/**
* MappedByteBuffer jvm crash,
* , crash 。
*
* @param mappedByteBuffer
*/
public static void unmap(final MappedByteBuffer mappedByteBuffer) {
try {
if (mappedByteBuffer == null) {
return;
}
mappedByteBuffer.force();
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
@SuppressWarnings("restriction")
public Object run() {
try {
Method getCleanerMethod = mappedByteBuffer.getClass()
.getMethod("cleaner", new Class[0]);
getCleanerMethod.setAccessible(true);
sun.misc.Cleaner cleaner =
(sun.misc.Cleaner) getCleanerMethod
.invoke(mappedByteBuffer, new Object[0]);
cleaner.clean();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("clean MappedByteBuffer completed");
return null;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
StopWatch sw = new StopWatch();
sw.startWithTaskName("write with file channel's write(ByteBuffer)");
writeWithFileChannel();
sw.stopAndPrint();
sw.startWithTaskName("write with file channel's transferTo");
writeWithTransferTo();
sw.stopAndPrint();
sw.startWithTaskName("write with MappedByteBuffer");
writeWithMappedByteBuffer();
sw.stopAndPrint();
}
}
테스트 결과(Y 축 은 소모 시간 초)분명히 writeWith MappedByteBuffer 방식 의 성능 이 가장 좋 고 하드웨어 설정 이 비교적 높 은 상황 에서 장점 이 더욱 뚜렷 하 다
MappedByteBuffer 는'두 배'메모리(대상 JVM 더미 메모리 와 Direct Byte Buffer 메모리)를 사용 해 야 합 니 다.-XX:MaxDirectMemory Size 매개 변 수 를 통 해 후자 의 최대 크기 를 설정 할 수 있 습 니 다.
MappedByteBuffer 의 force()방법 을 자주 호출 하지 마 십시오.이 방법 은 OS 가 메모리 에 있 는 데 이 터 를 디스크 에 새로 고침 하도록 강제 하기 때문에 약간의 성능 향상(IO 방식 에 비해)만 얻 을 수 있 습 니 다.뒤의 코드 인 스 턴 스 로 정시,정량 새로 고침 을 할 수 있 습 니 다.
갑자기 전기 가 끊 기거 나 서버 가 갑자기 다운 되면 메모리 맵 파일 데이터 가 디스크 에 기록 되 지 않 았 을 수도 있 습 니 다.이 때 데 이 터 를 잃 어 버 립 니 다.이러한 위험 을 낮 추기 위해 MappedByteBuffer 로 초대형 파일 을 쓰 는 것 을 피하 고 큰 파일 을 몇 개의 작은 파일 로 나 눌 수 있 지만 너무 작 으 면 안 됩 니 다(그렇지 않 으 면 성능 우 위 를 잃 게 됩 니 다)
ByteBuffer 의 rewind()방법 은 position 속성 을 0 으로 설정 하기 때문에 buffer 의 데 이 터 를 다시 읽 을 수 있 습 니 다.limit 속성 이 변 하지 않 기 때문에 읽 을 수 있 는 바이트 수 는 변 하지 않 습 니 다.
ByteBuffer 의 flip()방법 은 Buffer 를 쓰기 모드 에서 읽 기 모드 로 전환 합 니 다.
ByteBuffer 의 clear()와 compact()는 ByteBuffer 의 데 이 터 를 읽 은 후에 다시 쓰기 모드 를 자 를 수 있 습 니 다.다른 것 은 clear()가 position 를 0,limit 을 capacity 로 설정 한 다 는 것 입 니 다.다시 말 하면 Buffer 는 비 워 졌 지만 Buffer 내 데 이 터 는 비 워 지지 않 았 습 니 다.Buffer 에 읽 히 지 않 은 데이터 가 있다 면 clear()를 호출 하면 이 데 이 터 는'잊 혀 짐'되 고 다시 쓰 면 읽 히 지 않 은 데 이 터 를 덮어 씁 니 다.compcat()를 호출 한 후에 도 읽 지 않 은 데 이 터 는 보존 할 수 있 습 니 다.읽 지 않 은 데 이 터 를 Buffer 의 왼쪽 에 복사 한 다음 position 를 읽 지 않 은 데이터 에 이 어 limit 를 capacity 로 설정 하고 읽 지 않 은 데 이 터 는 덮어 쓰 지 않 습 니 다.
디스크 에 메모리 맵 파일 을 정기 적 으로 새로 고 칩 니 다.
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
public class MappedFile {
//
private String fileName;
//
private String fileDirPath;
//
private File file;
private MappedByteBuffer mappedByteBuffer;
private FileChannel fileChannel;
private boolean boundSuccess = false;
// 50MB
private final static long MAX_FILE_SIZE = 1024 * 1024 * 50;
// 512KB,
private long MAX_FLUSH_DATA_SIZE = 1024 * 512;
// ,
private long MAX_FLUSH_TIME_GAP = 1000;
//
private long writePosition = 0;
//
private long lastFlushTime;
//
private long lastFlushFilePosition = 0;
public MappedFile(String fileName, String fileDirPath) {
super();
this.fileName = fileName;
this.fileDirPath = fileDirPath;
this.file = new File(fileDirPath + "/" + fileName);
if (!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
*
*
* @return
*/
public synchronized boolean boundChannelToByteBuffer() {
try {
RandomAccessFile raf = new RandomAccessFile(file, "rw");
this.fileChannel = raf.getChannel();
} catch (Exception e) {
e.printStackTrace();
this.boundSuccess = false;
return false;
}
try {
this.mappedByteBuffer = this.fileChannel
.map(FileChannel.MapMode.READ_WRITE, 0, MAX_FILE_SIZE);
} catch (IOException e) {
e.printStackTrace();
this.boundSuccess = false;
return false;
}
this.boundSuccess = true;
return true;
}
/**
* :
* @param data
* @return
*/
public synchronized boolean writeData(byte[] data) {
return false;
}
/**
*
* @param data
* @return
* @throws Exception
*/
public synchronized boolean appendData(byte[] data) throws Exception {
if (!boundSuccess) {
boundChannelToByteBuffer();
}
writePosition = writePosition + data.length;
if (writePosition >= MAX_FILE_SIZE) { // data ,
flush();
writePosition = writePosition - data.length;
System.out.println("File="
+ file.toURI().toString()
+ " is written full.");
System.out.println("already write data length:"
+ writePosition
+ ", max file size=" + MAX_FILE_SIZE);
return false;
}
this.mappedByteBuffer.put(data);
//
if ( (writePosition - lastFlushFilePosition > this.MAX_FLUSH_DATA_SIZE)
||
(System.currentTimeMillis() - lastFlushTime > this.MAX_FLUSH_TIME_GAP
&& writePosition > lastFlushFilePosition) ) {
flush(); //
}
return true;
}
public synchronized void flush() {
this.mappedByteBuffer.force();
this.lastFlushTime = System.currentTimeMillis();
this.lastFlushFilePosition = writePosition;
}
public long getLastFlushTime() {
return lastFlushTime;
}
public String getFileName() {
return fileName;
}
public String getFileDirPath() {
return fileDirPath;
}
public boolean isBundSuccess() {
return boundSuccess;
}
public File getFile() {
return file;
}
public static long getMaxFileSize() {
return MAX_FILE_SIZE;
}
public long getWritePosition() {
return writePosition;
}
public long getLastFlushFilePosition() {
return lastFlushFilePosition;
}
public long getMAX_FLUSH_DATA_SIZE() {
return MAX_FLUSH_DATA_SIZE;
}
public long getMAX_FLUSH_TIME_GAP() {
return MAX_FLUSH_TIME_GAP;
}
}
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Is Eclipse IDE dying?In 2014 the Eclipse IDE is the leading development environment for Java with a market share of approximately 65%. but ac...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.