'Technical Note/JAVA'에 해당되는 글 17건

Technical Note/JAVA

Java에서 XML을 다루기 위한 API들입니다. DOM, SAX, JDOM,StAX

IBM의 developerworks에 보면 좋은 tutorial이 있습니다.
    XML 입문(한글) : http://www.ibm.com/developerworks/kr/xml/newto/

참고자료를 보면서 간단하게 정리한 내용은..

DOM(document object model) : 
   object-based(객체기반) 으로 트리구조를 이루며 XML 내용을 모두 분석하므로 느리고 메모리 사용이 많음
SAX(simple api for xml) : 
   event-based(이벤트 기반) 으로 순차적으로 읽기만 가능하며 DOM에 비해 빠르고 메모리 사용 적음
JDOM(java document object model) 
   object-based(객체기반)으로 DOM을 Java에 친숙하게 개량하고 DOM,SAX와의 상호 연동 지원
StAX(streaming api for xml) 
   cursor-based(커서 기반)으로 DOM, SAX를 보안한 방법으로 필요할때 정보를 추출

입니다.

다음은 DOM, SAX, StAX에 대한 장단점입니다.
(원본 출처가 oracle magazine이었는데 갈무리한 blog를 까먹었네요.)
□ XML 구문 분석 기술 요약    

    ㅁ 장점
    +-------------------------------------------------------------------------------------------------
    |DOM |* 사용 편의성                                                                              
    |        |* 편리한 탐색을 위한 풍부한 API 집합                                             
    |        |* 전체 트리가 메모리로 로드되므로 XML 문서에 대한 임의 액세스 허용
    +---------------------------------------------------------------------------------------
    |SAX  |* 전체 문서가 메모리에 로드되지 않으므로 메모리 소비량 감소            
    |        |* 여러 ContentHandler를 등록할 수 있는 푸시 모델                            
    +---------------------------------------------------------------------------------------
    |StAX |* 편의성과 성능을 위한 두 개의 구문 분석 모델이 포함되어 있음     
    |        |* 애플리케이션이 구문 분석을 제어하므로 여러 입력이 더 쉽게 지원됨
    |        |* 효율적인 데이타 검색을 가능하게 하는 강력한 필터링 기능 제공   
    +---------------------------------------------------------------------------------------
    
    ㅁ 단점
    +---------------------------------------------------------------------------------------
    |DOM |* 한 번에 구문 분석해야 함                                       
    |        |* 전체 트리를 메모리로 로드하는 경우 과도한 부담 발생
    |        |* 객체 유형 바인딩의 경우 일반 DOM 노드가 적합하지 않음           
    |        |  (모든 노드에 대한 객체 작성 필요)                               
    +---------------------------------------------------------------------------------------
    |SAX  |* 내장된 문서 탐색 지원 없음                            
    |        |* XML 문서에 대한 임의 액세스 없음                 
    |        |* XML 수정에 대한 지원 없음                           
    |        |* 이름 공간 범위 지정에 대한 지원 없음             
    +---------------------------------------------------------------------------------------
    |StAX |* 내장된 문서 탐색 지원 없음                           
    |        |* XML 문서에 대한 임의 액세스 없음                
    |        |* XML 수정에 대한 지원 없음                          
    +---------------------------------------------------------------------------------------
        
    ㅁ 가장 적합한 경우
    +---------------------------------------------------------------------------------------
    |DOM |* XML 문서를 수정해야 하거나 XSLT를 위한 애플리케이션            
    |        |  (읽기 전용 XML 애플리케이션에는 사용되지 않음)               
    +---------------------------------------------------------------------------------------
    |SAX  |* XML에서 읽기만 수행하는 애플리케이션                            
    |        |  (XML 문서 조작 또는 수정에는 사용되지 않음)                    
    +---------------------------------------------------------------------------------------
    |StAX |* 스트리밍 모델과 네임 스페이스에 대한 지원이 필요한 애플리케이션
    |        |  (XML 문서 조작 또는 수정에는 사용되지 않음)                     
    +---------------------------------------------------------------------------------------



이후 소스들은 참고자료를 보면서 따라해본 소스들이며 JDK 5에서 테스트 하였으며
샘플로 사용한 xml 파일은 상당히 단순합니다.

xml 샘플파일 a.xml



DOM 사용
DOM을 사용하여 XML 을 읽어 전체 읽기, 수정, 삭제, 다시 XML 파일로 저장의 작업 샘플입니다.
"Understanding DOM : http://www.ibm.com/developerworks/edu/x-dw-xudom-i.html" 를 보면서 따라한 샘플입니다.

DOM 샘플 : DOM.java




SAX 사용
"Understanding SAX : http://www.ibm.com/developerworks/edu/x-dw-xusax-i.html" 을 보면서 따라한 샘플입니다.
소스 파일이 2개 있는데 SAX.java와 필터를 사용한 SAX_Filter.java입니다.
SAX.java는 기초적인 사용 예제로 DefaultHandler라는 여러 핸들러를 implement한 껍데기 클래스입니다.
Content나 Error등의 각기 인터페이스 클래스가 제공되므로 따로 구현해도 됩니다.

SAX 샘플 : SAX.java


SAX_XMLFilter.java는 필터 기능을 넣는 방법으로 각 event 전 후에 해당 메소드들이 실행되며
XMLFilterImpl 클래스를 상속하여 구현합니다.

SAX Filter 사용 샘플 : SAX_XMLFilter.java



JDOM
일단, JDOM 홈페이지(http://www.jdom.org/) 에서 jdom.jar 를 받아야 합니다.
"Simplify XML programming with JDOM : http://www.ibm.com/developerworks/java/library/j-jdom/" 와 jdom 다운시 들어있는 샘플을 보고 돌려봤습니다.

JDOM 사용 샘플 : JDOM.java



StAX
StAX의 경우 API와 RI(구현체)가 여러가지 있습니다. (처음에 BEA에서 만들었군요)
http://stax.codehaus.org/ 와 http://woodstox.codehaus.org/ 에서 API와 RI의 .jar 를 받을 수 있는데 
아래 샘플에선 stax.codehaus.org에서 받은 라이브러리를 사용했습니다.
다른 분들은 woodstox를 사용하시는 것 같습니다.

샘플에서 XML 파일을 읽고 쓰는데 2가지 방식(XMLStreamReader와 XMLEventWriter) 이 있습니다.
참고자료의 "StAX'ing up XML, Part1,2,3" 을 보고 만들었습니다.

StAX 사용 샘플 : StAX.java



참고자료 : (IBM developerworks의 경우 일부 글들은 가입하셔야 볼 수 있습니다.)

XML 입문(한글) : http://www.ibm.com/developerworks/kr/xml/newto/

DOM
    Understanding DOM : http://www.ibm.com/developerworks/edu/x-dw-xudom-i.html
    Java와 XML 5장 DOM : http://kwon37xi.springnote.com/pages/1231818
    Java와 XML 6장 Advanced DOM : http://kwon37xi.springnote.com/pages/1231820

SAX - 홈 : http://www.saxproject.org/
    Understanding SAX : http://www.ibm.com/developerworks/edu/x-dw-xusax-i.html
    Java와 XML 3장 SAX : http://kwon37xi.springnote.com/pages/1231802
    Java와 XML 5장 Advanced SAX : http://kwon37xi.springnote.com/pages/1231814

JDOM - 홈 : http://www.jdom.org/
    Simplify XML programming with JDOM : http://www.ibm.com/developerworks/java/library/j-jdom/
    Java와 XML 7장 JDOM : http://kwon37xi.springnote.com/pages/1231824
    Java와 XML 8장 Advanced JDOM : http://kwon37xi.springnote.com/pages/1231828

StAX
    StAX'ing up XML, Part 1: An introduction to Streaming API for XML (StAX): 
        http://www.ibm.com/developerworks/library/x-stax1.html
    StAX'ing up XML, Part 2: Pull parsing and events :
        http://www.ibm.com/developerworks/java/library/x-stax2.html
    StAX'ing up XML, Part 3: Using custom events and writing XML :
        http://www.ibm.com/developerworks/xml/library/x-stax3.html
    StAX API 및 RI : 
        http://stax.codehaus.org/
        http://woodstox.codehaus.org/


Technical Note/JAVA

java에서 동영상의 스틸컷을 추출하기 위해 ffmpeg을 Runtime.exec()로 실행하는데, ffmpeg이 실행이 종료되지 않고 뭄추는 현상이 발생했다. 확인해 본 결과 ffmpeg이 쏫아내는 에러 출력 메시지 때문이었다. Runtime.exec()로 ffmpeg Processor를 생성한 뒤에 아래 코드와 같이 에러 출력 스트림으로부터 데이터를 읽어오기만 하면 블록킹 없이 ffmpeg이 실행된다.

참고로, 위 코드는 동영상으로부터 특정 시점의 썸네일 이미지를 추출하는 코드이다.



public File extractImage(File videoFile, int position,

File creatingImageFile) {

try {

int seconds = position % 60;

int minutes = (position - seconds) / 60;

int hours = (position - minutes * 60 - seconds) / 60 / 60;


String videoFilePath = videoFile.getAbsolutePath();

String imageFilePath = creatingImageFile.getAbsolutePath();


String[] commands = { "ffmpeg", "-ss",

String.format("%02d:%02d:%02d", hours, minutes, seconds),

"-i", videoFilePath, "-an", "-vframes", "1", "-y",

imageFilePath };


Process processor = Runtime.getRuntime().exec(commands);


String line1 = null;

BufferedReader error = new BufferedReader(new InputStreamReader(

processor.getErrorStream()));

while ((line1 = error.readLine()) != null) {

logger.debug(line1);

}

processor.waitFor();

int exitValue = processor.exitValue();

if (exitValue != 0) {

throw new RuntimeException("exit code is not 0 [" + exitValue

+ "]");

}

return creatingImageFile;

} catch (IOException e) {

throw new RuntimeException(e);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

Technical Note/JAVA

NullPointerException을 예방하는 방법

댓글을 달아주세요

NullPointerException은 null 때문에 발생하는 Runtime Exception입니다.

null인 객체에 접근하여 의도치 않게 NPE가 발생할 수 있지만 더 중요한 문제는 null 자체의 의미가 모호해 다양한 버그를 만들어냅니다. 예를 들어 메소드의 호출 결과로 null이 반환 되었을 경우 데이터가 없음을 의미할 수도 있지만 실패를 의미할 수도 있습니다. 심지어 성공을 의미하는 경우도 있을 수 있죠.

1
2
3
4
Map<String, String> map = new HashMap<String, String>();
map.put("hello", null);
map.get("hello");   // "hello" key의 value인 null을 return
map.get("nice");    // "nice" key가 없으므로 null을 return

위의 경우도 key에 대한 value가 null인지, key가 없어서 null을 return 한건지 return 받은 null 값을 가지고는 그 의미가 모호합니다. 누가 null을 만들었는지 참 얄밉지만 어쨋든 null 체크를 제대로 안했던지, 설계상의 결함이던지 null로 인한 버그는 결국 개발자 책임입니다.

똥이 무서워서 피하기보단 더러워서 피한다고… NPE를 예방하는 방법에 대해 정리해봤습니다.
들어가기에 앞서 null에 대한 프로그래머 대가들의 한마디…

“Null Sucks!”
- Doug Lea : Concurrent Programming In Java 저자 & JDK의 concurrency utilities 개발자

“I call it my billion-dollar mistake.”
- C.A.R. Hoare 2009년 어느 컨퍼런스에서.
1965년 Algol W 언어에서 처음으로 null reference를 만든 장본인 (Quick Sort도 발명)

코딩 습관 들이기

객체가 언제든 null일 수 있다는 판단하에 접근하면 도움이 될 것 같습니다.

1. equals 메소드 사용시
문자열 비교시 non-null String 기준으로 비교합니다. equals 메소드는 symmetric하므로 a.equals(b)와 b.equals(a)가 동일합니다. 그렇다면 null 일 수 있는 객체에서 equals 메소드 호출은 피하는게 낫겠죠?

1
2
3
4
5
6
public void doSomething() {
   // name이 null일 경우, NPE 발생!
   if (name.equals("BAD")) {
      // do something
   }
}
1
2
3
4
5
6
public void doSomething() {
   // name이 null이어도 NPE 발생 안함
   if ("BAD".equals(name)) {
      // do something
   }
}

다만 equals를 통과한 null 객체가 if문 안에서 메소드 호출 등의 이유로 다시 사용된다면 동일한 NPE가 발생할 수 있습니다. 이럴 경우엔 미리 null 체크를 하는게 좋겠죠?

2. toString()보다는 valueOf()를 사용할 것
1번 처럼 null 일 수 있는 객체에서 메소드 호출은 NPE 발생 위험이 있겠죠? static으로 제공되는 valueOf()를 사용하면 null을 Parameter로 넘겨도 null을 return할 뿐 NPE는 발생하지 않습니다. (valueOf()는 String, Boxed Primitives 클래스(Integer, Double 등)에서 static 메소드로 제공 됨)

1
2
3
BigDecimal bd = getPrice();
System.out.println(String.valueOf(bd)); // NPE 발생안함
System.out.println(bd.toString()); // NPE 발생

3. 메소드에서 null return하지 않기
가장 간단한 방법은 메소드 구현시 의미없는 null을 return하지 않도록 처리합니다. null이 아닌 빈 문자열 또는 빈 콜렉션을 return 하도록 합니다.

1
2
3
4
5
6
7
8
9
10
11
public List<User> getUsers() {
   // ...
 
   Result result = executeNamedQuery(GET_ALL_USERS);
 
   if (!result.isEmpty()) {
      return result;
   }
 
   return Collections.EMPTY_LIST; // or EMPTY_SET or EMPTY_MAP, etc. Depending on your return type
}

4. null을 Parameter로 넘기지 말 것
null을 Parameter로 넘길 경우 받는 쪽에서도 어찌보면 불필요한 null 체크가 필요하겠죠? null 자체에 어떤 특별한 의미가 없다면 주지도 받지도 맙시다.

5. 불필요한 autoboxing, unautoboxing 피하기 & Object 보다는 기본형 사용하기
Boxed Primitives 클래스와 기본형(primitive) 타입간에 자동으로 autoboxing, unboxing을 해주는데요. 자동으로 기본형 변환을 해주기 때문에 헷갈리기 싶습니다. (그냥 기본값이 들어가겠거니…) 가능하면 null reference를 가질 수 있는 객체가 아닌 자바 기본형(Primitive)을 이용하는 것도 방법입니다.

1
2
3
Person ram = new Person("ram");
// getPhone()에서 return된 결과가 null일 경우 NPE 발생
int phone = ram.getPhone();
1
2
3
4
5
6
private static Integer count;
 
// NPE 발생
if( count <= 0) {
  System.out.println("Count is not started yet");
}

6. Chaining 메소드 호출 자제하기

1
String city = getPerson(id).getAddress().getCity();

중간에 return 받은 값이 null일 경우 NPE가 발생하며 Stack Trace에서도 해당 line 위치만 출력되기 때문에 어디서 에러가 발생했는지 디버깅하기도 어렵습니다.

Library 이용하기

Apache Commons lang, Google Guava(예전 Goolge Collections) 등 null safe한 method를 이용하는 방법입니다. 자세한 사용은 Library의 JavaDoc을 읽어보길 권합니다.

1. Apache Commons의 StringUtils
String의 null 체크를 간단히 할 때 많이 사용하는 클래스입니다. StringUtils.isNotEmpty(), isBlank(), isNumeric(), isWhiteSpace() 등이 있죠.

1
2
3
4
5
6
7
8
9
10
11
// StringUtils methods are null safe, they don't throw NullPointerException
System.out.println(StringUtils.isEmpty(null));
System.out.println(StringUtils.isBlank(null));
System.out.println(StringUtils.isNumeric(null));
System.out.println(StringUtils.isAllUpperCase(null));
 
Output:
true
true
false
false

2. Guava의 Optional 클래스 이용하기
Optional는 nullable한 T를 non-null 값으로 대체시키기 위한 방법인데요. (말이 어렵죠?)
Optional 객체는 non-null인 T reference를 포함하거나 아무것도 포함하고 있지 않습니다.

한마디로 Optional 객체는 명시적으로 null 값을 갖지 않는다는거죠.
NullObject Pattern을 일반화시켰다고나 할까요? 바로 이 점을 이용해서 NPE를 예방합니다.

– absent : 아무 것도 포함하고 있지 않은 상태
– present : non-null 값을 갖은 상태

1) Optional 객체의 생성 (static 메소드)

Optional.of(T)
- T로 받은 non-null 값을 포함하는 Optional 객체 반환, T가 null 일 경우 NPE 발생
Optional.absent()
- 아무것도 포함하고 있지 않는 absent Optional 객체 반환
Optional.fromNullable(T)
- T로 받은 값이 non-null일 경우 present로, null일 경우 absent로 처리한 Optional 객체 반환

2) Optional 객체를 다루기 위한 메소드

boolean isPresent()
- Optional 객체가 non-null 인스턴스를 포함할 경우 true 반환
T get()
- Optional 객체가 present 일 경우 포함하고 있는 인스턴스를 반환, absent일 경우 IllegalStateException 발생
T or(T)
- Optional 객체의 present 값을 반환. 만일 값이 없을 경우 명시한 T를 반환 (기본값)
T orNull()
- Optional 객체의 present 값을 반환, 만일 값이 없을 경우 null을 반환. fromNullable의 역임
Set asSet()
- Optional 객체에서 포함하고 있는 인스턴스의 변경 불가능한 싱글톤 Set을 반환. 만일 인스턴스가 없다면 변경불가능한 Empty set을 반환

3) 간단 예제

1
2
3
4
5
6
7
Optional<Integer> possible = Optional.of(5);
System.out.println(possible.isPresent());
System.out.println(possible.get());
 
Output:
true
5

4) 응용 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Integer a = 10;
Integer b = null;
 
Optional present = Optional.fromNullable(a);
Optional absent = Optional.fromNullable(b);
 
System.out.println("a is present : " + present.isPresent());
System.out.println("b is present : " + absent.isPresent());
System.out.println("a value : " + present.or(0));
System.out.println("b value : " + absent.or(0));
 
Output:
a is present : true
b is present : false
a value : 10
b value : 0

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Optional.html

https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained#Optional

요건 Wrapper 메소드 형식으로 Optional 객체를 반환하게끔 사용하면 좋을 것 같네요.

3. Guava의 Preconditions 또는 NullPointerTester 클래스 이용하기
http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/base/Preconditions.html#checkNotNull(T)
http://guava-libraries.googlecode.com/svn-history/r144/trunk/javadoc/com/google/common/testing/NullPointerTester.html

Nullness Annotation 활용하기

메소드의 return값 또는 파라미터의 null 허용여부를 Annotation을 이용하여 지정합니다. 코딩 습관 들이기 3, 4번 항목을 제약하기 위해 사용하는데요.

IDE에서 지원하는 @Nullable, @NotNull 등의 Annotation을 사용하면 코드에서 NPE 발생 가능여부를 미리 경고해줍니다. 또 해당 Annotation 제약사항을 위반했을 경우 컴파일시 NPE가 아닌 IllegalArgumentException 또는 IllegalStateException이 발생됩니다. Argument로 NotNull이어야 한다고 정했는데 Null이 들어왔다면 의미상 NPE보다는 잘못된 Argument가 맞겠죠?

현재 ItelliJ, Eclipse IDE 및 Find Bugs에서는 서로 다른 Annotation 라이브러리를 사용하는데요. JCP에 Software Defect Detection하기 위한 Annotation이 JSR-305로 요청 중이나 현재 dormant(중단) 상태네요. 언제 JDK에 포함될런지는 모르겠네요 :’(

상세 사용법은 아래를 참고하세요.

1. Eclipse (Juno 이상)
http://help.eclipse.org/juno/index.jsp?topic=%2Forg.eclipse.jdt.doc.user%2Ftasks%2Ftask-using_null_annotations.htm

2. IntelliJ
http://www.jetbrains.com/idea/documentation/howto.html

3. FindBugs
http://findbugs.sourceforge.net/manual/annotations.html

4. IntelliJ 예제
– 프로젝트에 /REDIST/annotations.jar 추가
– Project Settings > Inspections > Probable bugs 체크 > Constant conditions & exception 체크 > Sueggest @Nullable annotation …. 체크

1
2
3
4
5
6
7
8
9
10
11
12
@Nullable
private String getDefinition() {
   // ...
}
 
public void doSomething() {
   if (getDefinition().equals("DEFINITION")) {
      // ...
   }
 
   // ...
}

@Nullable인 getDefinition()는 NPE가 발생할 수 있다.’고 IntelliJ에서 경고합니다.

1
2
3
4
5
6
7
8
9
10
private User findUser(@NotNull final String id) {
   // ...
}
 
public void doSomething() {
   // try invoking with null parameter
   User user = findUser(null);
 
   // ...
}

@NotNull 사용한 곳에서 null 값을 전달하고 있다고 IntelliJ에서 경고합니다.

기타

1. DB 테이블 컬럼 생성시 default값 설정하기
사용자로부터 입력 받은 값 중 null을 허용하더라도 default값을 설정했다면,
추후 해당 테이블에서 값을 불러와 사용할 경우 null을 예방할 수 있겠죠.

2. Null Object Pattern, Factory Pattern 사용하기

3. Spring MVC에서 지원되는 Bean Validation에서 @NotNull 사용하기 (JSR-303)

결론

1. 개발자가 코딩시 주의하여 NPE를 발생시키는 코딩 습관은 자제한다.
2. 간단한 Apache Commons나 Guava Library의 null safe 관련 클래스를 학습한 후 적용한다.
3. IDE에서 지원하는 Annotation 등을 이용해서 NPE 발생 부분은 미리 캐치하여 예방한다.

# 참고 사이트
http://javarevisited.blogspot.kr/2013/05/ava-tips-and-best-practices-to-avoid-nullpointerexception-program-application.html
http://howtodoinjava.com/2013/04/05/how-to-effectively-handle-nullpointerexception-in-java/
http://isagoksu.com/2009/development/java/how-to-avoid-nullpointerexceptions-npe/
https://code.google.com/p/guava-libraries/wiki/UsingAndAvoidingNullExplained
http://www.scottlogic.com/blog/2013/09/09/nullable-in-kepler.html


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 2 3 4
블로그 이미지

zzikjh