Java NIO의 DirectByteBuffer는 필요한 갯수만큼만 만들어서 사용하자.
어제 성능 테스트를 하다가 성능이 안나오는 프로그램이 있어 확인을 하던 중,
어떤 프로그램에서 System.gc()라는 불러서는 안되는 코드를 부른 다는 것을 직감(?)했다.
개발된 코드를 뒤져도 그 코드는 안나오고,
어디를 뒤져오 안나오길래 아주 단순한(?) 방법으로 원인이 되는 놈을 찾아 내었다.
(여기서 단순한 방법은 http://www.tuning-java.com/421 를 참고하시길...)
우선 관련해서 DirectByteBuffer를 막 생성하면 안된다고 이야기해 주신 정보를 제공해주신 훌륭한 "응주박" 선수에게 감사하다는 말을 전한다.
이러한 문제가 발생하는 원인을 확인해 보기 위해서 아침에 출근하자마자 샘플 코드를 작성해봤다. 먼저 다음의 코드를 보자.
import java.nio.ByteBuffer;
public class DirectByteBufferCheck {
public static void main(String[] args) {
DirectByteBufferCheck check=new DirectByteBufferCheck();
for(int loop=1;loop<99999999;loop++) {
check.getDirectByteBuffer();
if(loop%100==0) System.out.println(loop);
}
}
public ByteBuffer getDirectByteBuffer() {
ByteBuffer buffer;
buffer = ByteBuffer.allocateDirect(65536);
return buffer;
}
}
뭐 별거 없다.
루프 돌면서 ByteBuffer의 allocateDirect() 메소드를 호출해서 DirectByteBuffer를 생성한다.
이 프로그램 돌리고 GC 상태를 보면 다음과 같이 나온다.
$ jstat -gcutil 1484 1s
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 0.00 2.00 0.90 22.04 0 0.000 77 0.736 0.736
0.00 0.00 0.00 0.90 22.07 0 0.000 84 0.795 0.795
0.00 0.00 0.00 0.90 22.07 0 0.000 91 0.855 0.855
0.00 0.00 0.00 0.90 22.08 0 0.000 97 0.911 0.911
0.00 0.00 0.00 0.90 22.08 0 0.000 104 0.975 0.975
0.00 0.00 0.00 0.90 22.08 0 0.000 111 1.041 1.041
0.00 0.00 0.00 0.90 22.08 0 0.000 117 1.093 1.093
빨간색으로 표시한 Full GC 횟수가 1초에도 무지막지하게 증가하는 것을 볼 수 있다.
왜 이런 현상이 발생할까?
그 원인은 ByteBuffer.allocateDirect(65536) 메소드 호출 부분에 있다.
이 메소드를 부르면
1. DirectByteBuffer라는 클래스의 생성자가 호출되고
2. 그 생성자에서는 Bits.reserveMemory(65536) 이라는 static 메소드를 호출한다.
Bits 클래스의 소스는 "요기링크"를 참조하기 바란다.
링크를 보면 왜 DirectByteBuffer클래스의 객체를 계속 생성하면 System.gc()가 계속 발생할 수가 있는지를 확인해 볼 수 있다.