java GC 원인 및 해결방안

2014. 3. 28. 09:5099. 정리전 - IT/11. Java

GC원인

-------  (출처 : http://ir.bagesoft.com/100 )

 

 Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded

원인: 메모리 한계 초과
해결: -Xmx 옵션으로 메모리를 더 할당.

메모리가 부족하여 가비지 컬렉션이 이루어 졌으나,

 새로 확보된 메모리가 전체 메모리의 2% 미만이어서 생기는 오류.

한 마디로 더 이상 가비지 컬렉션을 할 수 없을 정도로 메모리를 사용한다는 것임.

 

기존) java -Xms1g -Xmx1g -jar Project.jar
해결) java -Xms4g -Xmx4g -jar Project.jar

 

 

아래 출처 : http://ir.bagesoft.com/97

 64bit OS 를 사용하고, 아끼지 말고 RAM을 추가

CPU만 지원한다면, 무조건 64bit 운영체제를 설치해서 사용하라. RAM이 4GB 이하라도 설치해라.

CPU의 처리 단위 증가 뿐만 아니라, 메모리를 최대한 사용할 수 있기 때문에 GC의 회수가 감소하여 속도가 더 높아진다.

Windows7 64bit 에서는 OS가 사용하는 메모리를 제외하고 모두 JVM이 사용할 수 있었다. 

1GB 파일을 생성하는데, 대략 5~10배 정도의 실행 시간 차이가 있었다. 

xms, xmx 옵션을 최대한 크게, 그리고 같게

(1) Windows7 64bit (물리적 메모리가 4g인 경우)

java -jar -xms4g -xmx4g xxx.jar  
재미있는 것은 Windows7 64bit 에서는 이 두 값을 물리적 메모리 보다 크게 잡아도 실행이 된다. 이유는 모르겠다.

(2) Windows XP 32bit (물리적 메모리가 4g인 경우)
java -jar -xms1.5g -xmx1.5g xxx.jar  
아마  1~1.5 사이까지 가능할 것이다. 넘어 가면 실행 자체가 안 되는데 실행가능한 최대값을 찾아 사용하자.

String 대신 StringBuilder를 사용

문자열에 여러 가지 조작을 가할 경우이다. 변하지 않는 문자열의 경우, String을 사용하면 된다.

String을 사용하여도 컴파일러가 적당히 변환해 준다고는 하지만, 컴파일러는 항상 개발자보다 무식하다.

참고로 실행 속도는 StringBuilder > StringBuffer >> String 이며,  
메모리사용량은 StringBuilder = StringBuffer << String 이다.

String.split() 대신 StringTokenizer.nextToken()을 사용

StringTokenizer.nextToken()는 사용이 불편하지만 빠르며, String.split()는 사용하기 편하지만 느리다.
token이 빈 문자열일 경우, String.split() 은 ""를 해당 배열 원소에 저장해 주지만, StringTokenizer.nextToken() 는 null을 리턴한다.
단 정규식을 구분자로 사용할 경우는 어쩔 수 없이  String.split() 을 사용해야 한다.
성능비교는  http://ir.bagesoft.com/622 를 참고

Buffered~계열 클래스 (BufferedReader, BufferedWriter 등) 를 사용

자세한 것은 검색을 통해

 

 

 

아래 부터의 출처

http://bcho.tistory.com/288

 

실행문장

 public void testSuite() throws Exception{

  int stringSize[]={100,200,500,1000,5000};

  for(int i=0;i<stringSize.length;i++){

   LOG_STRING="";

   for(int j=0;j<stringSize[i];j++) LOG_STRING+="1234567890";

   log(stringSize[i]+"0 bytes");

   testFileWriter();

   System.gc();

   testBufferedWriter();

   System.gc();

   testFileOutputStream();

   System.gc();

   testFileBufferedOutputStream();

   System.gc();

   testFileChannel();

   System.gc();

   testFileChannelOneBuffer();

   System.gc();

  }

 }

 

 

1. FileWriter fw = new FileWriter(LOG_HOME+"writer.log");

 

 // java.io.FileWriter
 private void testFileWriter() throws Exception {
  FileWriter fw = new FileWriter(LOG_HOME+"writer.log");
  long st = Timer.getCurrentTime();
  for(int i =0;i<loop;i++){
   fw.write(LOG_STRING);
  }
  log("FileWriter :"+Timer.getElapsedTime(st) +"ms");
  fw.close();
 }

 

 

 

2. BufferedWriter bw = new BufferedWriter(new FileWriter(LOG_HOME+"writer.log"));

 
 // java.io.BufferedWriter
 private void testBufferedWriter() throws Exception {
  BufferedWriter bw = new BufferedWriter(new FileWriter(LOG_HOME+"writer.log"));
  long st = Timer.getCurrentTime();
  for(int i =0;i<loop;i++){
   bw.write(LOG_STRING);
  }
  log("BufferedWriter :"+Timer.getElapsedTime(st) +"ms");
  bw.close();
 }

 

 

 

3. FileOutputStream fos = new FileOutputStream(LOG_HOME+"outputstream.log");

 
 // java.io.FileOutputStream
 private void testFileOutputStream() throws Exception{
  FileOutputStream fos = new FileOutputStream(LOG_HOME+"outputstream.log");
  long st = Timer.getCurrentTime();
  for(int i=0;i<loop;i++){
   byte[] buf = LOG_STRING.getBytes();
   fos.write(buf);
  }
  log("FileOutputStream :"+Timer.getElapsedTime(st) +"ms");
  fos.close();
 }

 

 

4. BufferedOutputStream fos =  new BufferedOutputStream(new FileOutputStream(LOG_HOME+"bufferedoutputstream.log"));

 
 // java.io.FileOutputStream
 // + java.io.BufferedOutputStream
 private void testFileBufferedOutputStream() throws Exception{
  BufferedOutputStream fos =
   new BufferedOutputStream(
     new FileOutputStream(LOG_HOME+"bufferedoutputstream.log"));
  long st = Timer.getCurrentTime();
  for(int i=0;i<loop;i++){
   byte[] buf = LOG_STRING.getBytes();
   fos.write(buf);
  }
  log("FileBufferedOutputStream :"+Timer.getElapsedTime(st) +"ms");
  fos.close();
 }

 

 

5. FileChannel fc =(new FileOutputStream(new File(LOG_HOME+"filechannel.log"))).getChannel(); + Byte Buffer 매번 생성

 
 private void testFileChannel() throws Exception {
  FileChannel fc =(new FileOutputStream(new File(LOG_HOME+"filechannel.log"))).getChannel();
  long st = Timer.getCurrentTime();
  for(int i=0;i<loop;i++){
   byte[] buf = LOG_STRING.getBytes();
   ByteBuffer bytebuffer = ByteBuffer.allocate(buf.length);
   bytebuffer.put(buf);
   bytebuffer.flip();
   fc.write(bytebuffer);
  }
  log("FileChannel  :"+Timer.getElapsedTime(st) +"ms");
  fc.close();
 }

 

 

6. FileChannel fc =(new FileOutputStream(new File(LOG_HOME+"filechannel.log"))).getChannel(); + ByteBuffer 재사용

 
 private void testFileChannelOneBuffer() throws Exception {
  FileChannel fc =(new FileOutputStream(new File(LOG_HOME+"filechannelonebuf.log"))).getChannel();
  int BUF_SIZE=1000;
  long st = Timer.getCurrentTime();
  ByteBuffer bytebuffer = ByteBuffer.allocate(BUF_SIZE);
  for(int i=0;i<loop;i++){
   byte[] buf = LOG_STRING.getBytes();
   int offset=0;
   int length= buf.length;
   while(offset < length){
    int chunkSize = BUF_SIZE > length-offset ? length-offset-1 : BUF_SIZE;
    bytebuffer.put(buf, offset, chunkSize);
    bytebuffer.flip();
    offset+=BUF_SIZE;
    fc.write(bytebuffer);
    bytebuffer.clear();
   }
  }
  log("FileChannel with reusing buffer:"+Timer.getElapsedTime(st) +"ms");
  fc.close();
 }

 

 

 

수행시간
   1K  2K  5K  10K  50K
 FileWriter  31  32    94  203  1281
 FileWriter + BufferedWriter  15  31    94  188  1000
 FileOutputStream  32  47  109  188  1063
 FileOutputStream + BufferedOutputStream  31  47  109  203  1578
FileChannel  47  63  109  219  2906
FileChannel + Byte Buffer 재사용  31  47  188  250  2766

(해당 레코드를 1000번씩 write)