본문 바로가기

프로그래밍/JAVA

자바 IO&NIO 파일복사 (FileCopy) 방법



우선 자바IO는 NIO에 비해 상당히 느립니다. 

 

자바IO 는 네이티브 언어 (C, C++) 처럼 시스템콜을 직접적으로 사용할수가 없습니다.


대표적으로 두가지 문제점이 있습니다.

<그림 1> IO 흐름

 

첫째. 자바IO는 커널 영역의 버퍼를 직접 건들지를 못합니다.

두번째. 스트림 데이터가 처리되기 전까지 스트림을 사용하는 자바 스레드는 Blocking 됩니다.

 

예를들어, 파일읽기 작업시 커널에 명령을 전달하고, 커널은 시스템콜 (System Call) 을 사용해서 디스크 컨트롤러가

물리적 디스크로 부터 읽어온 파일 데이터를 커널 영역안의 버퍼로 저장합니다.

커널안의 버퍼로 데이터가 저장되면 JVM(프로세스)안의 버퍼로 복사를 시작하게 됩니다.

이때 만약 프로세스로 버퍼를 복사하지 않고 직접적으로 커널영역의 버퍼를 사용한다면 성능향상을 기대할수 있을 것입니다.

 

파일을 읽어오는 처리만 하더라도 커널버퍼 에서 프로세스 버퍼 (JVM 을 통한) 로 불필요한 복사를 한차례 진행하고, 

물리적 디스크에서 커널버퍼로 데이터를 쓰는건 DMA : Direct Memory Access 기술을 통해 CPU관여 없이 처리가 가능하지만,

커널 버퍼에서 프로세스 버퍼로 데이터가 전달되기 위해선 CPU의 관여가 필요 하면서 오버헤드가 발생 합니다.

그리고, 디스크의 파일 데이터를 커널 영역안의 버퍼로 복사할때까지 자바 스레드가 블럭킹되면 처리 속도가 느려지게 됩니다.

 

이런 자바IO 의 단점을 JDK 1.4 부터 개선해서 나온것이 NIO (new IO) 입니다.

NIO는 시스템 메모리를 직접 사용 할 수 있고(DirectByteBuffer) 그리고 채널을 통해 네이티브IO 를 사용할 수 있습니다.

 

 

※ 참고문헌 (이미지 포함)

[1] 자바 I/O & NIO 네트워크 프로그래밍   저자 : 김성박, 송지훈  출판 : 한빛미디어

 

 

 

이제 자바에서 파일복사 방법에 대해 소개 하겠습니다.

 

JAVA IO

 

자바IO 를 통한 파일복사는

 (1) 파일크기만큼의 버퍼에 채워서 한번에 쓰는방법

 (2) 버퍼크기만큼 채우는 방법

두가지가 있습니다.

	// Java IO Stream Fullbuffer Copy
	public static void FileIoCopy(String source, String dest) throws FileNotFoundException, IOException {
		try (FileInputStream fis = new FileInputStream(source); 
				FileOutputStream fos = new FileOutputStream(dest);) {
			int availableLen = fis.available();
			byte[] buf = new byte[availableLen];
			fis.read(buf);
			fos.write(buf);
		}
	}
	
	// Java IO Stream buffering Copy
	public static void FileIoBufferCopy(String source, String dest) throws IOException {
		try (FileInputStream fis = new FileInputStream(source);
				FileOutputStream fos = new FileOutputStream(dest);
				) {

			byte[] buf = new byte[1024];	
			int read;
			while((read = fis.read(buf)) != -1) {
				fos.write(buf, 0, read);
			}
		}
	}

 

 

 

 

JAVA NIO

 

자바NIO 를 통한 파일복사는

 (1) Buffer 만큼 읽어 처리하여 복사

 (2) MappedByteBuffer 를 통한 복사

 (3) FileChannel 에서 제공하는 메서드를 통한 복사

	// Java NIO Fullbuffer Copy
	public static void FileNioFullBufferCopy(String source, String dest) throws IOException {
		try (FileInputStream fis = new FileInputStream(source);
				FileOutputStream fos = new FileOutputStream(dest);
				FileChannel isbc = fis.getChannel();
				FileChannel ogbc = fos.getChannel();) {
			
			ByteBuffer buf = ByteBuffer.allocateDirect(fis.available());
			isbc.read(buf);
			buf.flip();
			ogbc.write(buf);
		}
	}

	//Java NIO ByteBuffering Copy
	public static void FileNioByteBufferingCopy(String source, String dest) throws IOException {
		try (FileInputStream fis = new FileInputStream(source);
				FileOutputStream fos = new FileOutputStream(dest);
				FileChannel isbc = fis.getChannel();
				FileChannel ogbc = fos.getChannel();) {

			ByteBuffer buf = ByteBuffer.allocateDirect(1024);
			while (isbc.read(buf) != -1) {
				buf.flip();
				ogbc.write(buf);
				buf.clear();
			}

		}
	}

	//Java NIO FileNioMappedByteBuffer Copy
	public static void FileNioMappedByteBufferCopy(String source, String dest) throws IOException {
		try (FileInputStream fis = new FileInputStream(source);
				FileOutputStream fos = new FileOutputStream(dest);
				FileChannel ifc = fis.getChannel();
				FileChannel ofc = fos.getChannel();) {

			MappedByteBuffer mbb =  ifc.map(FileChannel.MapMode.READ_ONLY, 0, ifc.size());
			ofc.write(mbb);
		}
	}
	
	//Java NIO Channel Copy #1
	public static void FileNioChannelFromCopy(String source, String dest) throws IOException {
		try (FileInputStream fis = new FileInputStream(source);
				FileOutputStream fos = new FileOutputStream(dest);
				FileChannel fic = fis.getChannel();
				FileChannel foc = fos.getChannel();) {
				foc.transferFrom(fic, 0, fic.size());
		}
	}

	//Java NIO Channel Copy #2
	public static void FileNioChannelToCopy(String source, String dest) throws IOException {
		try (FileInputStream fis = new FileInputStream(source);
				FileOutputStream fos = new FileOutputStream(dest);
				FileChannel fic = fis.getChannel();
				FileChannel foc = fos.getChannel();) {
				fic.transferTo(0, fic.size(), foc);
		}
	}

 

 

 

JAVA NIO.2

 

 자바 NIO.2 (JDK 1.7 이상) 에서는 Files 클래스를 제공하고 있으며, Copy 메서드를 통해 파일복사를 할 수 있습니다.

	
	//Java NIO.2 Copy
	public static void FileNio2Copy(String source, String dest) throws IOException {
		Files.copy(new File(source).toPath(), new File(dest).toPath());
	}

 

 

 

 

그외 여러 파일복사 방법이 존재하지만 대표적으로 위에 방법으로 포스팅을 하였습니다.

당연히 이론적으로 보자면 파일복사 평균속도는 NIO.2 > NIO > IO 입니다.

무엇이 빠르냐 느리냐 속도에 대한 결과는 이미 다른 블로그등에서 다루고 있으니 생략하겠습니다.

한가지 확실한건 파일복사에 있어서는 엄청 눈에 띌정도로 속도를 측정하긴 어렵습니다.

하지만, 네트워크 프로그래밍 부분에 있어서는 IO&NIO 의 엄청난 속도 차이를 체감할수 있습니다.

(실제 네트워크 프로젝트를 진행하면서 다중접속 봇을 만들어 봤었는데 IO 서버 에서 100명 동시접속이 5초가 걸렸다면

NIO 서버 환경에서는 고작 0.X 초 만에 동시접속이 이루어졌던 경험을 했습니다.)

 

 

※ 참고

 본 포스팅의 소스코드은 자바7 Try-With-Resources 문법으로 코드를 표현하였습니다.

 따라서 JDK7 이상에서 작동합니다.

'프로그래밍 > JAVA' 카테고리의 다른 글

WAS JMX 에러해결 방법  (0) 2015.02.18
String 클래스 깊숙히 이해하기  (3) 2014.11.09
JOptionPane 클래스  (0) 2013.08.31
JFileChooser 클래스  (0) 2013.08.31
Swing LookAndFeel  (0) 2013.08.31