본문 바로가기

프로그래밍/Java

NIO(3)

반응형

NIO(3)

날짜: 2021년 6월 14일

학습목표

NIO를 이해하기 위해 Non-Block VS Block의 방식에 대한 개념을 공부했고, 그와 유사한 개념인 Sync VS Async 까지 공부했다. 그럼 배웠던 개념들을 토대로 왜?? NIO가 IO보다 빠른지, 또 NIO의 특징인 Buffer클래스는 어떤 특징이 있는지 알아보자.

  • NIO는 왜 IO보다 빠른가?
  • NIO의 버퍼란?

NIO는 왜 IO보다 더 빠를까?

먼저 기존 자바의 I/O는 왜 느린지 알아보자.

기존 Java I/O가 느렸던 이유

!

http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/

http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/

위의 두 그림은 기존 Java I/O의 과정을 간단하게 표현한 그림이다. 딱히 그림만 봐서는 왜 느린지 알수없다. 왜일까?

  1. OS에서 관리하는 커널버퍼에 직접 접근할 수 없다.
  2. Blocking I/O 방식에 따른 성능저하

커널 버퍼를 직접 핸들링 할수없다.

기존 자바 I/O는 커널 버퍼에 직접 접근할 수없었다. 소켓(네트워크 통신)이나 파일에서 Stream이 들어오면 커널 버퍼에 쓰여지게 되는데, 이를 접근할 방법이 없었다. 때문에 JVM내부의 메모리에 불러온 이후에 데이터에 접근이 가능했고, 이때 "커널에서 JVM내부로 데이터를 복사한다" 라는 오버헤드가 존재했기 때문에 느렸던 것이다. 그림을 보면서 더 이해해보자.

http://eincs.com/2009/08/java-nio-bytebuffer-channel-file/

기존 자바 I/O의 과정은 다음과 같다.

  1. Process(JVM)이 file을 읽기위해 kernel에 명령 전달
  2. Kernel은 시스템 콜(read()) 사용
  3. 디스크 컨트롤러가 물리적 디스크로부터 파일을 읽어옴
  4. DMA를 이용해 Kernel 버퍼로 복사
  5. Process(JVM) 내부 버퍼로 복사

따라서 다음과 같은 오버헤드가 생긴다.

  • JVM 내부 버퍼로 복사할때 CPU 관여
  • 복사 Buffer 활용 후 GC의 대상이된다.
  • 복사가 과정에서의 I/O가 요청한 Thread Blocking

위 3가지 오버헤드에 대해서 더 자세히 알아보자.

소중한 CPU 자원이 낭비된다.

물리적 디스크에서 커널영역으로 복사하는 것은 DMA의 도움으로 CPU가 관여하지 않기 때문에, 오버헤드가 적다. 이에반해 커널영역의 버퍼를 JVM내부 버퍼로 복사하는 것은 CPU까 관여하게 된다. 따라서 직접 커널버퍼를 사용한다면 CPU는 그만큼 다른 일을 처리할 수 있게되어 효율성이 증가된다.

내부 버퍼사용으로 인한 메모리가 GC의 대상이 된다.

기존 Java I/O에서 내부 버퍼로 사용한 데이터 변수들은 GC의 대상이 된다. 버퍼를 한번 사용하고 난 후에는 사용 될 일이 없기 때문이다. 그리고 Java에서 GC는 오버헤드이다.

기존 Java I/O는 Blocking I/O 이다.

Thread에서 블로킹이 발생하기 때문에 느리다.

위의 그림에서, 커널버퍼에서 JVM버퍼로 데이터를 복사해올때, I/O를 요청한 Process 정확히는 Thread는 블록킹이 된다. 따라서 커널버퍼에서 JVM버퍼로 복사되는 동안 프로세스(쓰레드)는 다른 작업을 하지 못하고 대기해야 한다.

NIO는 왜 더 빠른가?

NIO는 Direct Buffer로 커널버퍼를 직접 핸들링한다!

기존 커널에서 JVM으로 복사 과정을 거쳐야 했던것과 달리, NIO에서는 직접 커널버퍼에 접근할수 있는 Buffer클래스를 제공한다. 내부적으로 커널 버퍼를 참조하고 있다. 따라서 JVM 내부 버퍼로 복사를 하게되면서 발생하는 모든 문제가 해결된다. 참고로 DirectBuffer는 오로지 ByteBuffer만 지원한다.

NIO에서 System Call을 간접적으로 사용하게 해주기 때문이다!

C나 C++로 만들어진 서버 프로그램은 Thread를 생성하지 않고도 많은 수의 클라이언트를 처리할 수있다. 이것을 가능케 해주는 것이 OS레벨에서 지원하는 Scatter/Gather 기술과 Select() 시스템 콜이다.

기존 자바에서는 이런 방식을 사용할 수 없었으나, Java의 NIO는 Channel과 Selector 클래스를 통해 이를 지원한다.

Feedback

공부하면 할수록 모르는게 더 많아지는 느낌.... 원리나 개념을 이해하는 것보다도 당장 암기하는것도 벅찬것 같다...

NIO와 IO의 차이점에 대해서 대략적으로 알수있었다. 단순히 Nonblocinkg I/O를 지원해서 더 빠를것 이라고 생각했는데, 좀더 심오한 내용이 있었다.

커널버퍼에 직접접근 할수있는가의 차이가 큰것같다. AtomicXXX 도 그렇고 결국 Java 코드만으로 성능을 개선하는것은 근본적인 한계가 있는것 같다. JNI를 통해 성능을 개선하는게 답인듯...

알게된 점

  • NIO는 왜 IO보다 빠른가?
    • IO는 커널버퍼에 접근이 불가능, 이에따라 커널 → JVM으로 데이터가 복사되면서 발생하는 오버헤드를 NIO는 커널버퍼에 직접 접근하면서 모두 해결
    • IO는 Blocking 방식이지만, NIO는 Non-Blocking 방식으로 I/O를 요청한 프로세스가 대기하는 일이 없다.
  • NIO의 버퍼란?
    • NIO에서 제공하는 Buffer클래스를 통해서 커널영역에 접근할 수있다.
    • 이중 ByteBuffer만이 DirectBuffer를 통해 커널에 접근이 가능하다.

알아야 할 점

  • 버퍼의 '위치'로 인한 버퍼의 특성
    • 위치에 따른 특성은 크게 중요하지 않을것 같다...
  • 채널에 관해서 알아보는것도 좋을것 같다.

References

[NIO] JAVA NIO의 ByteBuffer와 Channel로 File Handling에서 더 좋은 Perfermance 내기!

반응형

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

네트워크 통신(1)  (0) 2021.06.15
Buffer  (0) 2021.06.14
NIO(2)  (0) 2021.06.14
NIO(1)  (0) 2021.06.14
Serializable(3)  (0) 2021.06.13