'Technical Note'에 해당되는 글 134건

Technical Note/SPRING

poolPreparedStatement 속성 정의

DBCP사용시 설정 정보중 <poolPreparedStatement> 란 속성이 있다.
이 속성은 PreparedStatement의 풀 사용 여부를 의미하며, default 값은 "false"이다. 



Prepared Statement Pooling 이란?

statement preparation은 RDBMS의 실행에 있어 비용이 많이 드는 작업이며 복잡한 SQL문일수록 더욱 높은 비용이 소모된다. 
각각의 statement preparation은 문법 오류 검증(syntactical correctness), 컬럼 참조(column references)의 유효성 검증, 최적화된 접근경로와 execution plan의 식별등을 수행한다.

Preparation을 위해서 statement을 매번 RDBMS에서 얻어와야 하는데, 자주 실행되는 Statement를 사전에 Prepare하여 Application이 호출할 수 있는 Common Place(pool)에 저장해 놓는것이 보다 효과적인 방법일 것이다.
이렇게 pool에서 Prepare된 statement을 갖고 오는 방법을 Prepared Statement Pooling 이라고 한다. 

Prepared Statement Pool 생성 매커니즘

  • BasicDataSource 타입의 dataSource 객체에서 Connection 객체를 가져올때 아래의 그림과 같이 두가지의 단계가 수행된다.
    1. Connection Pool이 있다면 Pool에서 Connection을 얻어온다.
    2. PrepareStatement Pool이 생성된다. 이때 Pool 의 크기는 maxOpenPreparedStatements 값에 따라 결정된다. 



  • 얻어온 Connection 객체에서 PrepareStatement 를 수행할 때, PrepareStatement Pool에서 Statement 객체를 얻어온다. 

Prepared Statement Pooling 을 쓰는 이유

다음은 Prepared Statement Pooling을 사용함으로써 얻을 수 있는 이득이다.

  • RDBMS가 Statement를 Prepare하는 시간을 줄일 수 있기 때문에 Application이 RDBMS로부터 데이터를 로딩하는 성능이 향상된다.
  • Statement cashing이 자동으로 수행되므로, Application개발자는 Statement pooling에 관한 지식이 없이도 JDBC Application을 개발할 수 있다.
  • Statement pooling과 Connection pooling을 함께 사용하면 Connection pooling을 단독으로 사용하는 것보다 큰 성능향상을 가져온다.



Prepared Statement Pooling 과 Connection Pool을 함께 사용할 때의 장점

Connection pool을 사용하지 하지 않는 경우 Application이 disconnect 되었을 때 Prepared statement pool 이 사라진다. 
그러나 Connection pool과 Prepared statement pool을 동시에 사용하는 경우, Application이 disconnect 되어 사용하던 Connection이 pool로 return될때, Prepared statement pool도 함께 return 된다. 
그 후, Application이 Connect 했을 때 Statement Preparation을 다시 수행할 필요 없이, Connection에 남아 있는 Prepared statement pool 를 재 사용하게 된다. 

poolPreparedStatement 속성 사용시 주의 사항

  • poolPreparedStatement 속성을 "true"로 설정하였을 경우, 반드시 <maxOpenPreparedStatements> 속성을 함께 사용해 커넥션 당 풀링할 preparedstatement의 적절한 개수를 설정해줘야 한다.
    이 값이 적정하지 않을 경우, 런타임 시 Out of Memory 등의 에러가 발생할 수 있다.

    maxOpenPreparedStatements : 풀의 최대 PreparedStatement 의 개수이며 default는 "unlimited"이다.



poolPreparedStatement 가 성능에 미치는 영향

만약 사용하는 SQL 개수에 비하여 preparedstatement의 Pool의 크기가 작다면, statement를 얻기 위한 비용으로 인해 메모리 사용율이 높아진다. 
반면, Pool의 크기가 크다면 "SQL 길이(statement 객체 크기) * pool 갯수 * connection 갯수" 만큼의 메모리가 쓰여지므로, Out of memory가 발생할 수 있다.
그러므로 적정한 Pool의 크기를 정의해야 성능상 효과를 볼 수 있을 것이다.

Technical Note/SPRING

1. weaving의 개념 
cross-cutting concern을 구현한 코드를 핵심 로직안에 삽입하는 것 


2. 세가지 Weaving 방법

  • 컴파일시 Weaving : AspectJ에서 사용하는 방식, AOP가 적용된 class파일이 생성
  • 클래스 로딩 시에 Weaving : AOP라이브러리중 JVM이 클래스를 로딩할 때 클래스 정보를 변경할수있는 Agent를 제공
  • 런타임 시에 Weaving : 런타임시에 AOP를 적용할 때는 소스코드나 클래스 정보 자체를 변경하지 않음
    프록시를 이용하여 AOP를 적용, 프록시 기반에서는 메서드가 호출할때에만 Advice를 적용할수있기 때문에 필드값 변경과 같은 Jointpoint에 대해서는 적용할수 없는 한계가 있음




3. Spring 에서의 AOP
스프링은 자체적으로 프록시 기반의 AOP를 지원하고 있다. 따라서, 스프링 AOP는 메소드 호출 Jointpoint만을 지원한다. 필드 값 변경과같은 Jointpoint를 사용하고 싶다면 AspectJ와 같이 풍부한 기능을 지원하는 AOP도구를 사용해야만 한다.

Spring에서 프록시 객체를 생성할떄 대상 객체가 인터페이스를 구현하고 있어야 한다. 
왜냐하면 Spring은 자바 리플랙션 api가 제공하는 java.lang.reflect.Proxy를 이용하여 프록시 객체를 생성하기 때문이다.
대상 객체가 인터페이스를 구현하고 잇지 않다면, spring은 CGLIB를 이용하여 클래스에 대한 프록시 객체를 생성해야 한다.




4. Spring에서의 AOP 실습

실습 과제 : method 단위의 수행시간을 추출 
아래의 3가지 방식으로 weaving 을 구현해 보았다.
1. Spring API를 이용 
2. AspectJ 5에서 정의한 @Aspect 어노테이션을 이용
3. CGLIB 를 이용




5. 구간별 수행시간 추출 기능 요구사항 정리
1. method 단위 수행시간 계산
2. 내부 class 분석기능

  • preparedstatement등의 Jdbc api class에 접근하여 SQL문 추출 기능
  • HttpClient의 수행시간 계산, URL 추출 기능
  • 기타... 고민해야 할 부분

3. Multi thread 로 수행함으로써 동시에 여러 서비스 구간분석




6. 결과

  • Bean으로 만들어진 Class안에 모든 Method의 수행시간은 추출가능




7. 미해결 문제

  • Bean으로 만들어진 Class가 아닌 경우는 구간분석 툴에서 무시됨
  • preparedstatement등 내부에서 사용하는 Class가 설정파일에 정의된 Advice, Pointcut 정보를 가져오지 못함
  • 위의 항목이 해결되면 Multi Thread 방식으로 확장 계획




참고1 - 용어 정리

  • 공통 관심 사항 : cross-cutting concern
  • 핵심 관심 사항 : core concern
  • Aspect : 여러 객체에 공통으로 적용되는 공통 관심 사항을 Aspect라 함.
  • Advice : 언제 공통 관심 기능을 핵심 로직에 적용할지를 정의하고 있음. 예를 들어, '메서드를 호출하기 전에'(언제) 에 '트랜잭션을 시작한다'(공통기능) 기능을 적용한다는것을 정의
  • Joinpoint : Advice를 적용 가능한 지점. 메소드 호출, 필드값 변경 등 Joinpoint에 해당됨
  • Pointcut : Joinpoint의 부분집합으로써 실제로 advice가 적용되는 Joinpoint를 나타냄. 스프링에서는 정규 표현식이나 AspectJ의 문법을 이용하여 Pointcut을 정의




참고2 - AspectJ의 Pointcut 표현식

execution 명시자 (Advice를 적용할 메소드(핵심관심사항을 구현한 메소드)를 지정)

execution([접근자제어패턴] , 리턴타입패턴 [패키지패턴]메서드이름패턴(파라메터패턴)) [ ] 안의 패턴은 생략 가능

  • execution(public void set*(..))
    public에 리턴값이 없으며, 패키지명은 없고, 메서드는 set으로 시작하며 인자값은 0개 이상인 메서드 호출
  • execution(* com.peoplesgroove..())
    리턴타입에 상관없이 com.peoplesgroove패키지의 인자값이 없는 모든 메서드 호출
  • execution(* com.peoplesgroove...(..))
    리턴타입에 상관없이 com.peoplesgroove 패키지 및 하위 패키지에 있는, 인자값이 0개 이상인 메서드 호출
  • execution(Integer com.peoplesgroove.WriteArticleService.write(..))
    리턴 타입이 Integer인 WriteArticleServlce 의 인자값이 0개 이상인 write() 호출
  • execution(* get*(*))
    메서드 이름이 get으로 시작하는 인자값이 1개인 메서드 호출
  • execution(* get*(,))
    메서드 이름이 get으로 시작하는 인자값이 2개인 메서드 호출
  • execution(* get*(Integer, ..))
    메서드 이름이 get으로 시작하고 첫번째 인자값의 데이터타입이 Integer이며, 1개 이상의 인자값을 갖는 메서드 호출
  • execution(* com..*(..)) && @annotation(@annotation)
    @annotation이 있는 모든 메소드 호출
  • execution(* *(..,@annotation (*), ..))
    @annotation을 파라메터로 갖고 있는 모든 메소드 호출|
within 명시자 (특정 패키지나 클래스, 인터페이스 내의 메소드 지정)
  • within(test.bean.BoardService)
    --> BoardService 인터페이스의 모든 메소드
  • within(test.bean.*)
    --> test.bean 패키지 내의 모든 메소드
  • within(test.bean..*)
    --> test.bean 패키지 및 그 하위 패키지 내의 모든 메소드
bean 명시자 (빈 설정파일에 등록된 빈의 이름을 이용하여 메소드 지정)
  • bean(myBean)
    --> 이름이 myBean 인 빈의 메소드
  • bean(*Service)
    --> 이름이 Service로 끝나는 모든 빈의 메소드

레퍼런스


Technical Note/SPRING

멀티쓰레드 환경에서 Http Connection 을 사용할 때, 쓰레드안전(Thread Safe)하게 Http Connection을 이용하는 방법을 소개하고자 합니다.

필자는 간단한 웹서버 부하기를 만드는 과정에서, 
부하발생을 위한 쓰레드를 생성해서 HttpClient로 웹서버에 request를 보내는 프로그램을 개발하여
테스트 하는 중에 다음과 같은 에러에 직면합니다.

org.apache.http.impl.client.DefaultRequestDirector tryConnect
정보: I/O exception (java.net.BindException) caught when connecting to the target host: Address already in use: connect
2011. 8. 16 오후 3:05:20 org.apache.http.impl.client.DefaultRequestDirector tryConnect
정보: Retrying connect
 

10초간격으로 웹서버에 request를 보내는 부하용 쓰레드가 120개 되는 지점 부터,
위와 같이 BindException이 발생하였습니다. MultiThreadedHttpConnectionManager를 이용하여
Connection Pool을 사용하니, 5초에 300개 쓰레드까지 증가하였습니다.

[HttpClientFactory.java]

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;

public class HttpClientFactory {
 private static HttpClient client ;
 private static MultiThreadedHttpConnectionManager mgr ;
 
 public synchronized static HttpClient getThreadSafeClient() {
  if(client != null)    return client ;
  
  mgr = new MultiThreadedHttpConnectionManager() ;
  HttpConnectionManagerParams params = new HttpConnectionManagerParams() ;
  params.setMaxTotalConnections( 300 ) ;
  mgr.setParams(params) ;
  client = new HttpClient(mgr) ;

  return client ;
 }
 
 public static int getConnectionsInPool() {
  return mgr.getConnectionsInPool() ;
 }
}


[사용 예]
...

  HttpClient httpClient = HttpClientFactory.getThreadSafeClient() ;

  PostMethod post = new PostMethod(spUrl) ;
  String timestamp = getTimeStamp() ;
  String serialkey = getSerialKey(from, message, timestamp) ;
  post.addParameter( new NameValuePair(__PUSH_PARAM_TOKEY__, to)) ;
  post.addParameter( new NameValuePair(__PUSH_PARAM_FROMKEY__, from )) ;
  post.addParameter( new NameValuePair(__PUSH_PARAM_MESSAGE__, message )) ;
  post.addParameter( new NameValuePair(__PUSH_PARAM_TIMESTAMP__, timestamp )) ; 
  post.addParameter( new NameValuePair(__PUSH_PARAM_SERIALKEY__, serialkey )) ; 
  post.addParameter( new NameValuePair(__PUSH_PARAM_APPID__, "0")) ;

  try {
   httpClient.executeMethod(post) ;
  }
  catch(UnsupportedEncodingException e) {e.printStackTrace() ;} 
  catch (IOException e) {e.printStackTrace() ;}
  finally {
   post.releaseConnection() ;
  }

...
 private static final String __PUSH_PARAM_TOKEY__ = "phone" ;
 private static final String __PUSH_PARAM_FROMKEY__ = "from" ;
 private static final String __PUSH_PARAM_MESSAGE__ = "message" ;
 private static final String __PUSH_PARAM_SERIALKEY__ = "serialkey" ;
 private static final String __PUSH_PARAM_TIMESTAMP__ = "timestamp" ;
 private static final String __PUSH_PARAM_APPID__ = "appid" ;

Technical Note/JAVA

Log4J에서 제공하는 AsyncAppender를 사용하기 위해서는 XML 설정을 사용해야만 한다.

Property 방식으로는 제공되지 않기 때문이다.

코드로도 가능한 듯하나, 사용할 일이 없을 듯하여 확인해보지는 않았다.

샘플은 다음과 같다.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="%d{yyyy/MM/dd HH:mm:ss,SSS} [%t] %-5p %c %x - %m%n" />
  </layout>
 </appender>
 <appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
  <param name="BufferSize" value="500" />
  <appender-ref ref="stdout" />
 </appender>
 <logger name="com.spartanjava" additivity="false">
  <level value="debug" />
  <appender-ref ref="ASYNC" />
 </logger>
 <root>
  <level value="debug" />
  <appender-ref ref="ASYNC" />
 </root>
</log4j:configuration>

동일한 내용이 두번씩 출력된다면, additivity 속성이 false임을 확인한다.

Reference:
http://www.spartanjava.com/2009/asynchronous-logging-with-log4j/

Technical Note/JAVA

GC의 종류는 아래와 같다. 
========================================================================================================
1. Serial Collector
  - 단일 Thread로 GC 작업을 수행
  - "-XX:+UseSerialGC" 옵션 사용하여 활성화 할 수 있다. 
  - 요즘 H/W환경에서는 사용할 필요가 없는? GC라고 생각된다.
 
2. Parallel Collector
  - 복수의 Thread로 GC작업을 수행한다 
  - "-XX:+UseParallelGC" 옵션을 사용하여 Minor GC 에서 parallel collector를 활성화 할 수 있다.
  - "-XX:+UseParallelOldGC" 옵션을 사용하여 Major GC에서 parallel collector를 활성화 할 수 있다. 
  - 복수의 Thread를 사용하여 GC를 수행하기 때문에 Serial Collector에 비해서 GC가 빨리 수행된다.
  - 최대 성능을 내기 위한 GC라고 생각된다.

3. CMS Collector
  - Major GC 실행시 Application Thread와 GC Thread가 동시에 수행된다. (GC의 6단계 중에서 2단계에서만 Application Thread를 Block 하며 나머지 4단계는 동시에 수해된다.)
     * 참조 :  http://www.javaperformancetuning.com/news/qotm026.shtml
  - "-XX:+UseConcMarkSweepGC" 옵션을 사용하여 활성화 할 수 있다. 
  - Minor GC에서 Parallel Collector를 활성화하기 위해서는 "-XX:+UseParNewGC" 옵션을 사용해야 하며,  "-XX:+UseParallelGC" 와는 같이 사용해서는 않된다!!!
  -  Parallel Collector GC 보다는 최대 성능이 낮게 나오지만, GC로 인한 Application Thread 정지 시간을 최소화 하여 응답시간 지연을 줄이고자 하는 경우 사용된다.
  - CMS Collector에 의한 Major GC가 수행되는 동안 Application에 의해서 Old generation 영역이 꽉차게 되면, 모든 Application Thread를 멈추고, Full GC를 수행하게 된다.
========================================================================================================

JVM Heap 메모리 및 GC 옵션 예제
  - 최소 사이즈 : -Xms128m
  - 최대 사이즈 : -Xmx384m
 
 - GC 로그 옵션 
   verbosegc -Xloggc:app_gc.log -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
  - Young Gen 영역을 위한 옵션 
    > -XX:NewSize=100m -XX:MaxNewSize=100m -XX:SurvivorRatio=32
  - GC 소요시간 최소화 옵션
    > -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:-CMSParallelRemarkEnabled
  - 성능을 위한 기타 추가 옵션
    > -XX:MaxTenuringThreshold=0 -XX:CMSInitiatingOccupancyFraction=60
  - OutOfMemoryError 발생시 자동으로 힙덤프 생성 옵션
    > -XX:+HeapDumpOnOutOfmemoryError -XX:HeapDumpPath=/path

JVM 옵션 상세 정보 참고자료 :  http://wiki.ex-em.com/index.php/JVM_Options

기타 툴
  - Java Stack Trace (dead lock 분석)
    > jps   [java 프로세스 확인]
    > kill -3 <pid>  [IBM JDK]

    > $JAVA_HOME/bin/jstack <pid>   [Hotspot JDK
  - Runtime JVM Heap Dump 생성
    > jmap -dump:format=b,file=heap.hprof [pid]
  - 힙덤프 분석 툴 : jhat, MemoryAnalyzer, visualvm


Java GC Option & Type



minor gc (New 영역에서 발생하는 gc)  261823K->261823K(261824K), 0.0000240 secs

Xms1024m -Xmx1024m -XX:MaxPermSize=256m // Xms 와 Xmx 의 차이는 virtual 메모리임. 
-XX:NewSize=100m -XX:MaxNewSize=100m -XX:SurvivorRatio=32 // young generation을 위한 옵션 
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:-CMSParallelRemarkEnabled // gc 시간을 최소화 
-XX:-DisableExplicitGC // System.gc() 를 disable 한다. 
-XX:MaxTenuringThreshold=0 -XX:CMSInitiatingOccupancyFraction=60 // 성능을 위한 기타 추가 옵션 
-verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails // gc print 옵션 

[GC 설명] 

GC 는 minor gc(young generation) 와 major gc (tenured generation) 로 구분된다. 
minor gc 는 로그에 [GC] 로 표시되며 major gc 는 [Full GC]로 표시된다. 


JVM 영역 


young generation 이 클수록 minor gc 는 적게 일어난다. 그러나 heap 사이즈의 영역에서 young generation 이 크다는 것은 
상대적으로 tenured generation 작다는 것을 의미하므로 major gc 가 자주 일어나게 된다. 
young generation 은 NewRatio 값으로 제어할 수 있다. 예를 들어 -XX:NewRatio=3 옵션은 young:tenured 가 1:3 의 비율을 
유지한다는 것을 의미한다. 
young generation 은 NewSize와 MaxNewSize로 지정할 수 있는데 두 합을 같이하면 Xms, Mmx 를 합한것을 사용하듯이 
young generation 의 크기를 정할 수 있다. 

 
full gc (Old (Tenured) 영역에서 발생하는 gc) 461932K->284190K(581248K), 5.1710580 secs
 
total gc 소요 시간 : 5.1713640 secs  ( minor gc + full gc + perm space gc)
 
이미 알고 계시겠지만, 단적으로 말하면 GC라는 것이 JVM에서 필요할 때 수행하는 것아니겠습니까?
 
필요해서 발생한 GC가 5.1 초의 긴 pause time을 유발하였다면..
 
pause time을 최소화 할 수 있는 Parallel GC 관련 옵션을 설정할것을 권고 드립니다.
 
-XX:+UseConcMarkSweepGC        :: old 영역 병렬 gc 옵션
-XX:+CMSParallelRemarkEnabled   :: new 영역 병렬 Remark 옵션
-XX:+UseParNewGC                     :: new 영역 병렬 gc 옵셥

 

Xms1024m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m
-Djeus.servlet.context.attribute.serialize=false
-Dfile.encoding=euc-kr
-Ddefault.client.encoding=euc-kr
-verbose:gc -Xloggc:/jeus/jeus42/logs/gclog/con1.log
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-XX:+DisableExplicitGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseParNewGC
 


young generation은 eden 영역과 first survivor, second survivor 영역으로 구성된다. 이상적인 minor gc는 eden과 first survivor에 
저장되어 사용중인 객체가 second survivor로 복사되는 것이다. 그러나 second survivor 영역이 충분하다고 보장되지는 않는다. 
그러므로 minor gc 를 성공하기 위해서 tenured generation에 오든 객체가 저장될 수 있는 충분한 메모리가 확보되어야 한다. 
만약 tenured generation 영역과 eden + survivor 영역의 사용중인 객체 메모리가 같다면 더이상 minor gc 로 tenured generation 영역을 
사용할 수 없으므로 major gc가 발생하게 된다. 

SurvivorRatio 파라미터를 사용하면 survivor 영역을 지정할 수 있다. 예를 들어 -XX:SurvivorRatio=6 이라고 한다면 
각 survivor 영역과 eden 의 영역이 1:6 이라는 의미이다. 그러므로 하나의 survivor 는 총 young generation 영역에서 1/8 이 된다. 
(second 가 있으므로 1/7 이 아니라 1/8 이 된다.) 


[4가지 type 의 collector] 

1. default garbage collector 

2. The throughput collector 

-XX:+UseParallelGC 를 사용하는 방법 

3. The concurrent low pause collector 

CPU 가 2장이상 많으면 많을수록 적용하기 적합한 collector 이다. 
concurrent low pause collector 는 운영중인 서버에 gc로 인한 pause 를 최소화 하기 위해서 사용되는 옵션이다. 
tenured generation 도 함께 일어난다. 
이 옵션을 적용할려면 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC 두가지 옵션을 주면 된다. 
그러나 -XX:+UseParallelGC 옵션과 -XX:+UseParNewGC 옵션을 동시에 사용하면 안된다. 

4. The incremental (sometimes called train) low pause collector 

일부분은 tenured generation 이 일어나며 major gc 의 일시멈춤현상을 최소화 할 수 있다. 
그러나 throughput 을 고려할 때 default collector 보다 느리다. 
-Xincgc 옵션을 사용하면 되나 다른 옵션과 함께 사용하면 JVM이 어떻게 동작할지 예상할 수 없으므로 
같이 사용하면 안된다. 


[GC log format] 

[GC [: -> , secs] -> , secs] 

is an internal name for the collector used in the minor collection 
is the occupancy of the young generation before the collection 
is the occupancy of the young generation after the collection 
is the pause time in seconds for the minor collection. 
is the occupancy of the entire heap before the collection 
is the occupancy of the entire heap after the collection 
is the pause time for the entire garbage collection. This would include the time for a major collection is one was done. 

[ GC log case 해석 ] 

1. Young generation size is too small 

[GC [DefNew: 4032K->64K(4032K), 0.0429742 secs] 9350K->7748K(32704K), 0.0431096 secs] 

-> gc 가 잘 된것 같으나 9350K->7748K 로 줄어들었으니 총 줄어든 경우가 1602K 정도 밖에 되지 않는다. 
전체 메모리중에서 40% 는 여전히 사용되고 있으니 이는 young generation 이 너무 작다는 이야기이다. 

2. The young generation size is too large 

[GC [DefNew: 16000K->16000K(16192K), 0.0000574 secs][Tenured: 2973K->2704K(16384K), 0.1012650 secs] 18973K->2704K(32576K), 0.1015066 secs] 

-> 앞부분에 보면 16000K->16000K로 변함이 없다. 왜냐하면 이를 복사할 Tenured 가 상대적으로 적으니 변함이 없는 것이다. 
이 경우는 young generation 줄어들려면 major gc 가 일어나야 줄어들 수 있다. 
그래도 옵션에 의해서 Tenured 영역에 일부 gc가 일어나 18973K->2704K(32576K) 만큼 줄어든 결과를 보이고 있다. 

3. Is the tenured generation too large or too small? 

[GC [DefNew: 8128K->8128K(8128K), 0.0000558 secs][Tenured: 17746K->2309K(24576K), 0.1247669 secs] 25874K->2309K(32704K), 0.1250098 secs] 

-> 총 메모리가 32M 일 경우 major gc 가 일어나 약 0.13초가 걸렸다. 

[GC [DefNew: 8128K->8128K(8128K), 0.0000369 secs][Tenured: 50059K->5338K(57344K), 0.2218912 secs] 58187K->5338K(65472K), 0.2221317 secs] 

-> 총 메모리가 64M 일 경우 major gc 가 일어나 약 0.23초가 걸렸다. 

위의 둘을 비교해 볼 때 첫번째가 좋을 것 같으나 이는 gc 의 빈번도를 알아봐야 최종 결론을 낼 수 있다. 

111.042: [GC 111.042: [DefNew: 8128K->8128K(8128K), 0.0000505 secs]111.042: [Tenured: 18154K->2311K(24576K), 0.1290354 secs] 26282K->2311K(32704K), 0.1293306 secs] 
122.463: [GC 122.463: [DefNew: 8128K->8128K(8128K), 0.0000560 secs]122.463: [Tenured: 18630K->2366K(24576K), 0.1322560 secs] 26758K->2366K(32704K), 0.1325284 secs] 
133.896: [GC 133.897: [DefNew: 8128K->8128K(8128K), 0.0000443 secs]133.897: [Tenured: 18240K->2573K(24576K), 0.1340199 secs] 26368K->2573K(32704K), 0.1343218 secs] 

-> 첫번째는 11초마다 일어난 반면에 

90.597: [GC 90.597: [DefNew: 8128K->8128K(8128K), 0.0000542 secs]90.597: [Tenured: 49841K->5141K(57344K), 0.2129882 secs] 57969K->5141K(65472K), 0.2133274 secs] 
120.899: [GC 120.899: [DefNew: 8128K->8128K(8128K), 0.0000550 secs]120.899: [Tenured: 50384K->2430K(57344K), 0.2216590 secs] 58512K->2430K(65472K), 0.2219384 secs] 
153.968: [GC 153.968: [DefNew: 8128K->8128K(8128K), 0.0000511 secs]153.968: [Tenured: 51164K->2309K(57344K), 0.2193906 secs] 59292K->2309K(65472K), 0.2196372 secs] 

-> 두번째는 30초마다 일어났으므로 두번째 메모리 세팅이 훨씬 좋음을 알 수 있다. 

[The concurrent low pause collector 측정방법] 

* -verbose:gc 와 -XX:+PrintGCDetails 옵션을 적용한다. 
* CMS-initial-mark : initial marking 상태에 대한 GC 통계를 보여준다. 
* CMS-concurrent-mark : concurrent marking 상태에 대한 GC 통계를 보여준다. 
* CMS-concurrent-sweep : concurrent sweeping 상태에 대한 통계를 보여준다. 
* CMS-concurrent-preclean : concurrently 하게 작동할 수 있도록 결정된 작업의 통계 
* CMS-remark : remarking 상태에 대한 통계 
* CMS-concurrent-reset : concurrent 작업이 끝나고 다음 collection을 준비한다. 

[The concurrent low pause collector 사용시 Parallel Minor Collection 옵션] 

* -XX:+UseParNewGC : 다중 CPU에서 멀티쓰레드를 사용한 young generation collection을 사용하는 옵션. 
* -XX:+CMSParallelRemarkEnabled : remark 시 일시 중단을 멈추는 옵션 
그러나 이 옵션은 jvm 1.4.2 에서 버그로 판명되어 (-) 로 사용해야 한다. 
default 가 (+) 이므로 -XX:-CMSParallelRemarkEnabled 명시적으로 사용함. 
JVM 1.5.0 (tiger)에서는 해결되었음 

[기타 옵션] 

1. -XX:MaxTenuringThreshold=0 

default는 31 이며 이 옵션은 young generation 시에 객체가 살아 있을 수 있는 age 를 나타낸다. 
이 옵션이 0 가 되면 young generation 시에 older generation 에 남아있는 객체가 복사되는 
시간을 줄일 수 있다. 

2. -XX:CMSInitiatingOccupancyFraction=60 

이 옵션의 값이 특정 지정한 값보다 작으면 gc 가 자주 일어나면 값이 크면 gc 의 효율이 떨어질 수 있다. 
그러므로 해당 값을 잘 정하는 것이 좋으며 일반적으로 60 으로 세팅하여 로그를 보고 조절하면 된다. 


[ 기타 사항] 

1. jsp 와 같이 동적으로 generation 되는 어플리케이션은 permanent generation 이 성능저하의 한 요소가 될 수 있으므로 
-XX:MaxPermSize=256m 와 같은 옵션을 사용하여 해당 메모리를 늘려줄 필요가 있다. 

2. 프로그래머가 System.gc() 를 호출하면 major gc 가 일어나므로 성능의 영향을 줄 수 있다. 
그러므로 -XX:+DisableExplicitGC 옵션을 사용하여 프로그래머가 gc 를 일으키지 않도록 제한해야 한다. 
JVM 1.4.2 에서는 버그가 있어 -XX:-DisableExplicitGC 와 같이 (-) 로 사용해야 해당 기능을 적용할 수 있다. 

3. Concurrent low pause collector 는 Parallel Collector 와 Concurrent mark-sweep (CMS) collector 의 기능을 
모두 가지고 있다. 


[example] 
java -Xmx512m -Xms512m -XX:MaxNewSize=24m -XX:NewSize=24m -XX:SurvivorRatio=128 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=0 -XX:CMSInitiatingOccupancyFraction=60

---------------------------------------------------------------------------------


minor gc (New 영역에서 발생하는 gc)  261823K->261823K(261824K), 0.0000240 secs
 
full gc (Old (Tenured) 영역에서 발생하는 gc) 461932K->284190K(581248K), 5.1710580 secs
 
total gc 소요 시간 : 5.1713640 secs  ( minor gc + full gc + perm space gc)
 
이미 알고 계시겠지만, 단적으로 말하면 GC라는 것이 JVM에서 필요할 때 수행하는 것아니겠습니까?
 
필요해서 발생한 GC가 5.1 초의 긴 pause time을 유발하였다면..
 
pause time을 최소화 할 수 있는 Parallel GC 관련 옵션을 설정할것을 권고 드립니다.
 
-XX:+UseConcMarkSweepGC        :: old 영역 병렬 gc 옵션
-XX:+CMSParallelRemarkEnabled   :: new 영역 병렬 Remark 옵션
-XX:+UseParNewGC                     :: new 영역 병렬 gc 옵셥

 

Xms1024m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=256m
-Djeus.servlet.context.attribute.serialize=false
-Dfile.encoding=euc-kr
-Ddefault.client.encoding=euc-kr
-verbose:gc -Xloggc:/jeus/jeus42/logs/gclog/con1.log
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-XX:+DisableExplicitGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseParNewGC


1 ··· 19 20 21 22 23 24 25 ··· 27
블로그 이미지

zzikjh