학습(구)/Java(구)

자바 성능 튜닝 - I/O에서 발생하는 병목 현상

잉아당 2021. 6. 7. 16:10
728x90

자바의 입출력은 스트림을 통해서 이루어 집니다.

어떠한 디바이스 혹은 네트워크를 통해 이루어지는 작업을 모두 IO에 포함되는 작업입니다.

IO작업은 성능에 많은 영향을 미치는데 IO에서 발생하는 시간은 CPU를 사용하는 시간과 대기 시간중 대기시간에 속하기 때문에 IO와 관련된 다비이스가 느리면 느릴수록 성능은 느려지게 됩니다.

 

바이트 기반 입력 스트림 처리 클래스 

ByteArrayInputStream

FileInputStream

FilterInputStream

ObjectInputStream

PipedInputStream

SequenceInputStream

 

입력 스트림 읽기 클래스

BufferedReader

CharArrayReader

FilterReader

FileReader

InputStreamReader

PipedReader

StringReader

 

스트림을 사용한 후 반드시 닫아주어야 나중에 리소스가 부족하지 않습니다.

파일을 처리할 경우 IOException을 따로 구분하여 처리하는 것이 좋습니다.

 

버퍼에서 char배열을 지속적으로 사용할 경우 초기화하면서 사용해야 합니다.

 

주로 파일 읽을때는 BufferedReader를 이용해 readLine()을 이용하여 라인 단위로 읽어야 성능이 좋습니다.

 

지속적으로 변경 되는 파일의 경우 데몬 스레드를 하나 생성하여 5분 혹은 10분에 한번씩 확인하도록 수정하도록 하면 매번 요청시 수정 여부를 확인하지 않아도 됩니다.

 

IO 수행 프로세스

1. 파일 읽는 메서드를 자바에 전달

2. 파일명을 받은 메서드가 운영체제의 커널에게 파일을 읽어달라고 ㄴ요청

3. 커널이 하드 디스크로부터 파일을 읽어 자신의 커널에 있는 버퍼에 복사하는 작업을 수행하는데 DMA에서 수행

4. 자바는 커널의 버퍼를 사용하지 못하므로 JVM으로 해당 데이터를 전달

5. JVM에서 메서드에 있는 스트림 관리 클래스를 사용하여 데이터를 처리

 

=> 여기서 3,4  작업에서 대기하는 시간이 발생하게 되는 이를 보완하기 위해 NIO가 나왔습니다.

 

NIO에서 추가된 개념

- 버퍼의 도입

- 채널 도입

- 문자열의 인코더와 디코더 제공

- Perl 스타일의 정규표현식에 기초한 패턴 매칭 방법 제공

- 파일을 잠그거나 메모리 매핑이 가능한 파일 인터페이스 제공

- 서버를 위해 복합적인 Non-blocking IO 제공

 

NIO 사용시 ByteBuffer를 사용하는 경우가 있는데 네트워크나 파일에 있는 데이터를 읽어 들일 때 사용합니다. 

ByteBuffer를 생성하는 메서드 중 allocateDirect()는 데이터를 JVM에 올려서 사용하는 것이 아닌 OS 메모리에 할당 된 메모리를 Native한 JNI(Java Native Interface)로 처리하는 DirectByteBuffer 객체를 생성하므로 계속 생성해서는 안됩니다.

*JNI : 네이티브 프로그램(운영체제와 플랫폼에 종속적인 프로그램) 및 다른 언어로 작성된 라이브러리들을 호출하거나 호출되어지게 하는 프레임워크

 

DirectByteBuffer를 계속 생성하게 될 경우 5~10초마다 FUll GC(Old영역 메모리가 부족할때 발생)가 발생하고 Old영역(오래된 객체가 존재하는 곳)의 메모리는 증가하지 않습니다.

java.nio에 아무런 접근 제어자가 없이 선언된 Bits라는 클래스의 reserveMemory()메서드를 호출하게 됩니다. 이 메서드는 JVM에 할되어 있는 메모리보다 더 많은 메모리를 요구할 경우 System.gc()메서드를 호출하도록 되어져 있습니다.  

=>생성자가 무차별적으로 생성이 될 경우 GC가 자주 발생하고 성능에 영향을 주게 됩니다. 

 

JDK6까지 자바에서 파일 변경을 확인하기 위해 lastModified()라는 메서드를 사용했습니다.

 

lastModifed() 프로세스 

1. System.getSecurityManager()메서드를 호출하여 SecurityManaver객체를 얻어옴

2. 만약 null이 아닐경우 SecurityManager의 checkRead()메서드 수행

3. File 클래스 내부에 있는 FileSystem이라는 클래스의 객체에서 getLastModifiedTime()메서드를 수행하여 결과 리턴

 

그러나 JDK7 부터 watch관련 클래스로 인해 편하게 모니터링이 가능합니다.