티스토리 뷰

자바 NIO 등장이유

자바가 느리다는 얘기를 듣던 이유는 IO 때문이다. 이러한 문제사항을 해결하기 위해 NIO(New Input Output) 이 등장하였다.

자바 IO가 느린 이유

애플리케이션의 IO 과정에는 유저 영역커널 영역이 있다. 유저 영역은 H/W 에 직접 접근하는 것이 불가는 하고 커널 영역에 시스템 콜을 보내 드라이버 등을 통해 접근 가능하다. 커널 영역을 거치게 되면서 불필요한 버퍼와 복사 과정이 추가된다.

자바 I/O의 비효율

  1. 커널 영역을 거쳐 I/O를 진행하기 때문에 쓸 때없는 과정(버퍼 복사)이 추가된다.
    • 예시로 비디오 카드(모니터) -> 커널 영역의 버퍼 로 데이터를 이동하는 것은 DMA(Direct Memory Access) 기술을 통해 CPU 자원이 필요없다. 하지만 커널 -> 프로세스(유저) 영역 으로 이동은 CPU가 반드시 개입해야한다. 때문에 JVM 내 버퍼로 복사해 오는 것이 아니라 커널영역에 버퍼(다이렉트 버퍼)의 데이터를 직접 사용할 수 있다면 CPU 자원 낭비를 막을 수있다.
  2. 디스크(HDD, SSD) -> 커널 영역 으로 데이터를 복사해오는 동안 JVM 쪽으로 바로 복사되지 않고 충분히 커널영역 내 버퍼가 찰 때까지 블로킹 된다. 충분히 커널 영역에 버퍼가 찼을 때 JVM으로 해당 데이터를 전송한다.

IO 향상을 위한 운영체제 수준의 기술

버퍼

한 개씩 여러번 반복적으로 데이터 전달을 하는 것보다 중간 버퍼를 두고 데이터를 모아 한 번에 전달하는 방식이다.

Scatter/Gather

만약 애플리케이션에 버퍼를 여러개 만들어 사용할 때 해당 애플리케이션 버퍼를 커널영역의 버퍼로 이동시키기 위해서 시스템 콜을 해당 버퍼의 숫자만큼 진행한다. 이는 다수의 시스템 콜을 발생 시켜 비효율적이다. Scatter/Gather는 이러한 문제 해결해주는 방법으로 시스템 콜을 한 번만 호출할 수 있게 해준다. 한 번에 시스템 콜을 통해 해당 버퍼의 주소목록을 넘겨주고 운영체제에서 최적화해서(가상 메모리주소를 물리 메모리 주소로 변환해) 버퍼를 읽거나 쓴다.

  • 자바 NIO 에서는 ScatteringByteChannel GatheringByteChannel 를 제공한다.
    • Scatter : 커널 영역 -> 유저 영역
    • Gather : 유저 영역 -> 커널 영역

가상 메모리

  • 프로그램이 사용할 주소 공간을 늘이기 위해 OS에서 지원하는 기술로 OS는 가상 메모리를 페이지라는 고정된 크기(4kb)로 나누고 메모리가 아닌 디스크와 나누어 저장한다.실제 프로그램이 실행되는데 필요한 페이지의 가상주소만 물리적 메모리로 바꾸어 실제 메인 메모리에 올려놓는 것
    • 실제 메모리 크기보다 큰 메모리 공간을 사용할 수 있다.
    • 여러 개의 가상 주소가 하나의 메모리 주소를 참조해 효율적으로 사용할 수 있게 한다.
  • 표현 하자면 실행 시간 바인딩 방식이다. 커널과 유저 영역에 버퍼에서 해당 가상 메모리 주소를 공유하고 실제 사용할 때 디스크에서 메모리로 이동시켜 사용한다. 즉,각 영역에서 서로 다른 메모리 공간을 보는 것이 아니라같은 메모리 공간을 보게해 버퍼 복사 과정을 생략한다. (같은 메모리 내에서 같은 데이터가 복사하는 과정 생기지 않음)

메모리 맵 파일

  • 변경이 잦은 애플리케이션은 잦은 시스템 콜이 발생한다. 또한 잦은 버퍼 간 복사로 많은 가비지가 발생하여 가비지 컬렉터가 가비지를 수거하는 작업이 생긴다. 이 때, OS 에서 제공하는 Memory-mapped IO를 통해
    파일 시스템의 페이지와 유저 영역의 버퍼를 가상메모리로 매핑한다.
  • 메모리 맵 작업은 프로세스가 파일 데이터(디스크의 파일의 주소 값)를 가상 메모리로 바라봐 시스템 콜 작업이 필요없도록 한다. 또한 큰 파일 복사를 위해 메모리를 소비하지 않아도 된다. ( File(Disk) Direct Access )
  • NIO 에 MappedByteBuffer 가 해당 개념이다.

파일 락

  • 파일 역시 동시 접근을 막기 위해 락을 사용한다. 공유 락과 배타 락 두가지가 존재하는데 공유 락은 읽기에 배타 락은 쓰기 작업에 사용된다. 이러한 파일 락 기능을 자바 IO 에서는 제공하지 않았고 해당 내용을 NIO에 추가되었다.

NIO 에 변화된 것

자바 포인터 버퍼 도입

  • NIO 에는 커널에 의해 관리되는 시스템 메모리를 사용 할 수 있게 되었다.
  • OS에서 제공하는 DMA, 캐시, 가상메모리 등의 효율적인 IO 기술을 사용할 수 있게 되었다.
  • C/C++ 의 포인터의 기능을 간접적으로 제공한다.
  • ByteBuffer를 의미한다.

네이티브 IO 를 제공하는 채널 도입

  • 단반향 스트림이 아닌 양방향 통신이 가능한 채널(Channel)을 도입했다.
  • OS에서 제공하는 네이티브 IO 기능을 사용할 수 있게됨
  • Scatter/Gather 을 사용할 수 있도록 구현했다.

셀렉터 도입

  • POSA2 에서 소개된 Reactor 패턴의 구현체로 버퍼와 채널을 함께 사용해 한 개의 스레드로 수많은 사용자를 처리할 수 있도록 함.

 

 

참조

 

자바 I/O & NIO 네트워크 프로그래밍 - 교보문고

네트워크 프로그래밍은 할 줄 알지만 기본개념이 약햇던 사람 기본 개념은 있지만 응용이 약핸던 사람등을 위해 각 장별로 네트워크 기초 지식과 개념 스레드 구현 방법, 자바 I/O 의 기본개념

www.kyobobook.co.kr

 

댓글