(이미지 : http://wiki.ex-em.com/index.php/Java_Performance_Fundamental)

오랜만에 읽는 업무 관련 서적입니다. :)

그동안 전공 서적을 접어두고
가벼운 소설을 좀 읽다가
명상 책도 좀 읽고
미드를 보다가...
다시 업무 관련 서적을 집었습니다.

트위터에서 tohappy님이 트윗 하신 걸 보고
구입했지요


현재 3장을 다 읽고 4장을 읽으려고 하고 있는데요..

내용이 재미있습니다.

대략적으로 알고 있던 내용도 있고
전혀 몰랐던 내용도 많은데요
아주 깊이 있게 들어가지 않고...
fundamental이라는 제목에 맞게 적당한 수준에서
끊어주고 있는 것 같습니다.

ibm jvm과 hotspot jvm에 대해서 동시에 다루고 있어서
양이 많다고 느끼시면 개인적으로는 필요한 부분만 발췌해서
읽어도 좋을 것 같습니다.
(가비지콜렉터 부분을 그렇게 읽었네요 ㅎㅎ)

jvm에 관련 된 책을 꼭 한번 읽어 보고 싶었는데
이렇게 좋은 책이 나와서 좋네요.

아무도 후기를 작성하신 분이 없어서
책 값도 꽤 비싼데다가... 선듯 구입하기가 망설였었는데
현재까지 읽은 느낌으로는 상당히 만족스럽습니다.

읽다 보면 대학 전공 서적을 읽는 듯한 느낌도 납니다. ㅎㅎ

책 다 읽고 포스트 하려고 했는데
내용도 많고
요약해서 올릴 것도 아니고 해서
3장까지 읽고 느낌을 올려봅니다. 도움 받으실 분이 있으실지도 몰라서....

번역서였다면
굉장히 망설였을 겁니다. 이런 책이 번역 좀 이상하게 되면
이해불가 상태가 되어버리거든요.... 김한도님께서 지으셨는데
읽기도 편합니다.

자바 개발자라면..
한번 읽어보시면
얻어가실 것이 많을 것 같습니다.

관련 위키 페이지가 있네요.

http://wiki.ex-em.com/index.php/Java_Performance_Fundamental

11월 3일 내용을 추가합니다.

책을 이제 다 읽어 갑니다.

챕터별로 만족스러운 부분도 있고, 조금은 불만족스러운 부분도 있습니다. ^^

일단, 앞 부분은 이미 글을 작성했듯이 상당히 만족스러웠습니다.
(chapter 1,2,3,4,5)

chapter 6 같은 경우는 쓰레드 동기화에 대한 내용인데
일반적인 개발자가 알기에는 좀 깊은 내용인듯 하기도 하고..
제가 이쪽에 대해서 지식이 좀 얇아서 이해하기가 어려웠던 것 같습니다.
스레드, 병렬처리에 대한 내용을 보고 싶으시다면 "자바 병렬 프로그래밍" 책이 더 유용하실거구요..
이 책에서는 말 그대로 내부에서 일어나고 있는 일들이 설명되어 있습니다.

chapter7은 자바와 데이터베이스에 대한 내용인데
oracle로 픽스된 점도 큰 불만은 없지만
oracle에 대한 내용이 많아 oracle의 용어에 익숙하지 않은 저로써는
읽는데 조금 애 먹었습니다. 그래서 모든 페이지를 상세하게 보지 못 하고
필요한 부분만을 발췌해서 읽었네요. 천천히 자세하게 읽으면 또 다른 부분이 보일지도 모르겠습니다.
그리고, 처음 부분에서는 그렇게 거슬리지 않던 많은 영어 단어들이
이 챕터에서는 읽기 불편하게 느껴졌습니다.
아마도 대부분 처음 보는 오라클 관련 단어라서 그런 것 같습니다 ㅋㅋ

또 하나는 책이 무거워서.. 회사에서 밖에 읽지 못 했습니다.

1-5장에서 느낀 기쁨도 있었지만
뒷부분에서는 아쉬움도 있던 책입니다.
하지만, 한번 읽어보면 많은 것을 얻어 갈 수 있다는 점에서는
생각이 다르지 않습니다.

이 책을 읽는다고 당장 뛰어난 성능을 발휘하는 프로그램을 개발 할 수 있는 것은 아닙니다.. ^^



Posted by 용식

[JAVA] Pattern, Matcher

Java 2009.10.09 14:38
Posted by 용식
요즘 읽고 있는 책입니다.

"자바 병렬 프로그래밍" 이라는 제목으로 번역되어서 나왔구요

강철구님께서 번역해주신 책입니다.

루씬 인 액션 1판도 그랬지만, 이분의 번역은 정말 좋다고 생각합니다. ^^

(예전 신입때 같은 회사에서 거기다가 옆에서 같이 일 하시던 분이 이런 대단한 분이셨다니!!)

번역은 상당히 잘 되었다고 생각하구요...

스승이신 풍대리님이 읽고 계시길래 옆에서 보다가

재미있어 보여서 구입해서 따라보고 있습니다 -.-

지금의 저에게는 이해하기 조금 난해한 예제도 나오기는 하지만

내용 자체는 머리에 쏙쏙 들어옵니다.

지금...한.. 80페이지 가량 읽었는데요...

이정도만 읽으면서도.. 머리속으로 지금까지 짰던 코드들이 주마등처럼 흘러갑니다 -_-;;;;

'정말 아무 생각없이 코딩해왔구나...' 라는 생각을 지울 수가 없네요.

(그리고 동시에.. '이제 어쩌지.. -_-;;;;;')

더 큰일은 잘못하면 큰일이 나는건 이제 알겠는데

어떻게 해야 잘못을 안 할 수 있을지 잘 모른다는거.... 생각만 많아지겠네...

일단 아무튼! 나중에 다 읽고 나면 다시 한번 소감을 적어보려고 합니다.

아오.... ;;;;;;

Posted by 용식
TAG java

얼마전 블로그 지인의 소개로 읽어보게 된 책입니다.


사실 지금의 제가 읽기에는 어떻게 보면 많이 쉬운 책일수도 있고


보통 초보자분들이 책 추천을 원하면 많이들 추천해주시는


"소설같은자바" , "플래시로 배우는..", "헤드퍼스트자바"


이 3권에 들어갈정도로 기초적인 내용도 있습니다.


굳이 이 시점에서 이 책을 한번 읽어보게 된 것은 머리속의 내용을 한번 정리해보고 싶었기 때문

입니다.


이 책은 처음 책을 펼쳐보면 굉장히 산만합니다.


지금까지 봐왔던 교재들하고는 많이 다르죠. 일단 이미지도 많고 사진도 많고...


그래서 저는 자바를 처음 접하는 분들에게는 이 책보다는 소설같은 자바를 먼저 권해 드리고 싶습

니다.


그 책과 이책으로 병행해서 보시면 좋을 것 같다는 생각이 드네요..


제가 보기엔 두권의 책이 서로 모자라는 부분을 충분히 보완해주고 있다고 생각합니다.


헤드퍼스트자바 같은 경우 객체의 생성과 소멸, 원시자료타입의 생성과 소멸 등등에 대해서


내부적으로 어떤 레퍼런스를 가지고 있고 그것이 어떤식으로 사용되는지


이해를 시켜주기 위한 노력들이 많이 보입니다.


사실 그것 때문에 이 책을 한번 읽어보게 된 계기이기도 하구요..


머리가 복잡할때는 저 같은 경우 이런 책을 한번 쭉 읽어나가는 것이


정리에 많은 도움이 되더라구요..^^


생초보분께는 권하지 않지만, 자바를 조금 다를 줄 알고


한발자국만 더 깊이 들어가보고 싶다. 하시는 분들은 이 책을 권해드리고 싶습니다.


특히 이책의 "바보같은 질문은 없다" 의 챕터는 정말 좋다고 생각이 듭니다.


이책을 읽으시고 effective java를 소화하신다면


좋은 코스가 될 것 같네요 ^^

Posted by 용식

패턴이라는 것..

DevStory 2008.09.04 09:24
자바 공부를 할 때.. 몇 년 전입니다. 디자인 패턴 책을 본 적이 있습니다.

그때 책에 써있던 문구가..

"패턴은 커뮤니케이션을 위한 것.." 이라는 문구가 있었습니다.

자바에 대한 이해도가 부족했던 저로서는 저 문구가 이해 될리 없었고.. 패턴은 외워서 적용해야 하는 것.

이라고 생각 하고 있었죠...

그게 벌써 4년정도 된거 같습니다.

요새 루씬을 공부하면서 루씬 메일링 리스트에 가입을 해서 질문도 하고 답변도 하고 있습니다.

영어가 겁내 짧지만, 영어 공부도 할 겸... 루씬에 대한 레퍼런스가 적은 우리나라에서의 한계도 극복 할 겸..

이래저래 좋다고 생각이 되는데요..아무튼 얼마전 메일링 리스트로 한 질문이 올라왔고 그것에 대한 답변을 하는

과정에서

"싱글턴 패턴을 적용해 보는 것이 좋겠다." 라고 대답을 하게 되었습니다. 짧은 영어죠. ㅋㅋㅋ

답을 보내놓고 곰곰히 생각을 해보니

특별한 설명을 하지 않았는데, 무엇을 어떻게 바꾸라는 구체적인 설명이 없이 단지 "그 클래스를 싱글턴 패턴으로
적용해 보는 것이 좋겠다." 라고 얘기한 것 만으로 그들이 알아듣고 (더군다나 막 영어!) 그것을 적용을 해봤다는

것이 새삼 놀라웠습니다.

그때 문득 생각 나더라구요.. 아..이게 책에서 얘기했던 커뮤니케이션을 위한 것이구나..

한번의 경험이 책 10번 읽은 것 보다 더 강렬하게 가르쳐주었습니다.

외운다고 외워질 것도 아닌 이 패턴...

하지만 알고 있으면 개발자인생에 도움이 될 패턴...

커뮤니케이션을 위한 패턴에 대해 다시 한번 생각해보게 된 계기였네요..
Posted by 용식
TAG java, 패턴

보통 개발 할 때 업캐스팅을 많이 사용한다.

인터페이스던 추상클래스던 ....

다형성을 위해서..

아래의 예를 보자.

public class Shape {
 public void draw() {
  ...
}
}

public class Circle extneds Shape {
 public void draw() {
...
}

public void myDraw() {
...
}
}

public class Test {
 public static void main(String args[]) {
 Circle c = new Circle();
c.draw();
c.myDraw();
Shape s;
s = c;
s.draw();
s.myDraw();
}
}

보면
에러가 날까.. 아니면 정상적으로 작동할까..
일반적으로 업캐스팅을 하고나면 , 그냥 아무 생각없이 하위 클래스의 메서드를
쓰게 되는데 내부적으로 봤을때 하위 클래스의 메서드를 사용 할 수 있는 경우는
"그 메서드가 상위 클래스에 존재 할때"
에 하위클래스의 메서드를 사용 할 수 있는 것이다.

만약, 하위클래스에서 오버라이드 하지 않았다면,
상위클래스의 메서드가 호출될 것이다.

오버라이드 했다면 당연히 하위 클래스의 메서드가 호출될 것이지만, 만약 상위 클래스에 없는데 위

경우처럼 s.myDraw();를 호출하면 참조 할 수 없는 영역이기 때문에 컴파일 에러가 발생하게 된다.

그런고로..
위 예제에서는
s.myDraw();

부분에서 컴파일 에러가 발생하게 된다.

참고 : jabook.org

Posted by 용식

상속과 생성자

Java 2008.07.10 11:18

자바에서 사용되는 상속에 대해서 정리 해본다.

GrandFather
Father
Son

이 3가지의 클래스가 있고 각각 상위의 클래스를 상속받고 있다 라고 한다면

public GrandFather {
...
}


public Father extends GrandFather {
...
}

public Son extends Father {
...
}


Son son = new Son();

이렇게 Son을 생성한다면 어떻게 될까나..~

기본적으로 상속을 받은 후 하위의 클래스 인스턴스를 생성할때는

상위 클래스의 생성자를 호출해 주어야 한다.

위와 같은 경우에는 디폴트 생성자에서 그 역할을 해주기 때문에

오류가 나지 않는다.

즉 Son의 디폴트 생성자에서 Father의 디폴트 생성자를 호출하고

Father의 디폴트 생성자에서 GrandFather의 디폴트 생성자를 호출하게 된다.


그럼 아래와 같은 경우를 보자.

public class Father {
private String name = "";

public Father(String name) {

this. name=name;

}


public class Son extends Father {

public Son (String name) {
}


public static void main(String[] args) {
 Son son = new Son("이름");
}


위의 소스는 어떨까?

컴파일에러가 발생하게 된다. 왜냐하면 Father의 생성자를 오버라이드 했기 때문에

Father의 디폴트 생성자는 사용 할 수 없게 되고, Son의 인스턴스를 생성할때
Father의 생성자를 호출해주지 않았으므로, 에러가 발생하는 것이다.

아래의 경우에는 에러가 발생하지 않는다.

1. Father의 생성자를 디폴트 생성자로 정의.
혹은
1. Son의 생성자에서 super(name); 을 호출
혹은
1. Father에 디폴트 생성자를 추가.

요는 최하위 클래스의 인스턴스를 생성 하게 되면 최상위부터 차상위까지 클래스들의
인스턴스까지 생성이 되게 된다. 클래스가 생성이 되면서 가장 먼저 실행하게 되는 것이 바로 생성자 메서드인데, 만일 디폴트 생성자 대신 오버로드한 생성자가 존재하고, 하위 클래스에서 상위 클래스의 이 오버로드된 생성자를 호출하지 않는다면, 생성자를 실행하지 못해 컴파일 에러가 발생하는 것이다.

참고 : jabook

Posted by 용식
TAG java, Super, 상속

jvm GC와 메모리 튜닝

Java 2008.06.19 15:06
JVM GC와 메모리 튜닝




자바스터디 네트워크 [www.javastudy.co.kr]

조대협 [bcho_N_O_SPAM@j2eestudy.co.kr]




모든 Java Application은 JVM(Java Virtual Machine)위에서 동작한다.
이 JVM이 동작하는데 있어서, 메모리의 구조와 특히 GC는 Application의 응답시간과 성능에 밀접한 관계를 미친다. 이번 강좌에서는 JVM 의 메모리 구조와 GC 알고리즘 (JDK 1.4.X에 포함된 새로운 알고리즘 포함) 그리고, JVM의 메모리 튜닝을 통한 Application의 성능향상방법에 대해서 알아보도록 하자.


1.GC란 무엇인가?


GC는 Garbage Collection의 약자로 Java 언어의 중요한 특징중의 하나이다.
GC는 Java Application에서 사용하지 않는 메모리를 자동으로 수거하는 기능을 말한다.
예 전의 전통적인 언어 C등의 경우 malloc, free등을 이용해서 메모리를 할당하고, 일일이 그 메모리를 수거해줘야했다. 그러나 Java 언어에서는 GC 기술을 사용함에 따라서 개발자로 하여금 메모리 관리에서 부터 좀더 자유롭게 해주었다.


2.GC의 동작 방법은 어떻게 되는가?


1) JVM 메모리 영역

GC의 동작 방법을 이해하기 위해서는 Java의 메모리 구조를 먼저 이해할 필요가 있다.
일반적으로 Application에서 사용되는 객체는 오래 유지 되는 객체보다, 생성되고 얼마안있어서 사용되지 않는 경우가 많다. <그림 1 참조>


<그림 1. 메모리 foot print>


그래서 Java에서는 크게 두가지 영역으로 메모리를 나누는데 Young 영역과 Old 영역이 그것이다.
Young 영역은 생긴지 얼마 안된 객체들을 저장하는 장소이고, Old영역은 생성된지 오래된 객체를 저장하는 장소이다. 각 영역의 성격이 다른 만큼 GC의 방법도 다르다.
먼저 Java의 메모리 구조를 살펴보자.


<그림 2. Java 메모리 구조>


Java의 메모리 영역은 앞에서 이야기한 두 영역 (Young 영역,Old 영역)과 Perm 영역 이렇게 3가지로 영역으로 구성된다.


<표 1. Java 메모리 영역>



2) GC 알고리즘

그러면 이 메모리 영역을 JVM이 어떻게 관리하는지에 대해서 알아보자.
JVM 은 New/Young 영역과, Old영역 이 두영역에 대해서만 GC를 수행한다. Perm영역은 앞에서 설명했듯이 Code가 올라가는 부분이기 때문에, GC가 일어날 필요가 없다. Perm영역은 Code가 모두 Load되고 나면 거의 일정한 수치를 유지한다.


○ Minor GC
먼저 New/Young영역의 GC방법을 살펴보자 New/Young 영역의 GC를 Minor GC라고 부르는데, New/Young영역은 Eden과 Survivor라는 두가지 영역으로 또 나뉘어 진다. Eden영역은 Java 객체가 생성되자 마자 저장이 되는곳이다. 이렇게 생성된 객체는 Minor GC가 발생할때 Survivor 영역으로 이동된다.

Survivor 영역은 Survivor 1과 Suvivor2 영역 두 영역으로 나뉘어 지는데, Minor GC가 발생하면 Eden과 Survivor1에 Alive되어 있는 객체를 Suvivor2로 복사한다. 그리고 Alive되어 있지 않는 객체는 자연히 Suvivor1에 남아있게 되고, Survivor1과 Eden영역을 Clear한다. (결과적으로 Alive된 객체만 Survivor2로 이동한것이다.)
다음번 Minor GC가 발생하면 같은 원리로 Eden과 Survivor2영역에서 Alive되어 있는 객체를 Survivor1에 복사한다. 계속 이런 방법을 반복적으로 수행하면서 Minor GC를 수행한다.

이렇게 Minor GC를 수행하다가, Survivor영역에서 오래된 객체는 Old영역으로 옮기게 된다.

이 런 방식의 GC 알고리즘을 Copy & Scavenge라고 한다. 이 방법은 매우 속도가 빠르며 작은 크기의 메모리를 Collecting하는데 매우 효과적이다. Minor GC의 경우에는 자주 일어나기 때문에, GC에 소요되는 시간이 짧은 알고리즘이 적합하다.

이 내용을 그림을 보면서 살펴보도록 하자.


<그림 3-1. 1st Minor GC>


Eden에서 Alive된 객체를 Suvivor1으로 이동한다. Eden 영역을 Clear한다.


<그림 3-2. 2nd Minor GC>


Eden영역에 Alive된 객체와 Suvivor1영역에 Alive된 객체를 Survivor 2에 copy한다.
Eden영역과 Suvivor2영역을 clear한다.


<그림 3-3. 3rd Minor GC>


객체가 생성된 시간이 오래지나면 Eden과 Suvivor영역에 있는 오래된 객체들을 Old 영역으로 이동한다.


○ Full GC

Old 영역의 Garbage Collection을 Full GC라고 부르며, Full GC에 사용되는 알고리즘은 Mark & Compact라는 알고리즘을 이용한다. Mark & Compact 알고리즘은 전체 객체들의 reference를 쭉 따라가다면서 reference가 연결되지 않는 객체를 Mark한다. 이 작업이 끝나면 사용되지 않는 객체를 모두 Mark가 되고, 이 mark된 객체를 삭제한다.<그림 4 참고> (실제로는 compact라고 해서, mark된 객체로 생기는 부분을 unmark된 즉 사용하는 객체로 메꾸어 버리는 방법이다.)

Full GC는 매우 속도가 느리며, Full GC가 일어나는 도중에는 순간적으로 Java Application이 멈춰 버리기 때문에, Full GC가 일어나는 정도와 Full GC에 소요되는 시간은 Application의 성능과 안정성에 아주 큰 영향을 준다.


<그림 4. Full GC>




3. GC가 왜 중요한가?


Garbage Collection중에서 Minor GC의 경우 보통 0.5초 이내에 끝나기 때문에 큰문제가 되지 않는다. 그러나 Full GC의 경우 보통 수초가 소요가 되고, Full GC동안에는 Java Application이 멈춰버리기 때문에 문제가 될 수 있다.
예를 들어 게임 서버와 같은 Real Time Server를 구현을 했을때, Full GC가 일어나서 5초동안 시스템이 멈춘다고 생각해보자.
또 일반 WAS에서도 5~10초동안 멈추면, 멈추는동안의 사용자의 Request가 Queue에 저장되었다가 Full GC가 끝난후에 그 요청이 한꺼번에 들어오게 되면 과부하에 의한 여러 장애를 만들 수 있다..
그래서 원할한 서비스를 위해서는 GC를 어떻게 일어나게 하느냐가 시스템의 안정성과 성능에 큰 변수로 작용할 수 있다.


4. 다양한 GC 알고리즘


앞 에서 설명한 기본적인 GC방법 (Scavenge 와 Mark and compact)이외에 JVM에서는 좀더 다양한 GC 방법을 제공하고 그 동작방법이나 사용방법도 틀리다. 이번에는 다양한 GC 알고리즘에 대해서 알아보자. 현재 (JDK 1.4)까지 나와 있는 JVM의 GC방법은 크게 아래 4가지를 지원하고 있다.

- Default Collector
- Parallel GC for young generation (from JDK 1.4 )
- Concurrent GC for old generation (from JDK 1.4)
- Incremental GC (Train GC)

1) Default Collector
이 GC 방법은 앞에서 설명한 전통적인 GC방법으로 Minor GC에 Scavenge를, Full GC에 Mark & compact 알고리즘을 사용하는 방법이다. 이 알고리즘에는 이미 앞에서 설명했기 때문에 별도의 설명을 하지는 않는다.

JDK 1.4에서부터 새로 적용되는 GC방법은 Parallel GC와 Concurrent GC 두가지 방법이 있다. Parallel GC는 Minor GC를 좀더 빨리하게 하는 방법이고 (Throughput 위주) Concurrent GC는 Full GC시에 시스템의 멈춤(Pause)현상을 최소화하는 GC방법이다.

2) Parallel GC
JDK1.3까지 GC는 하나의 Thread에서 이루어진다. Java가 Multi Thread환경을 지원함에도 불구하고, 1 CPU에서는 동시에 하나의 Thread만을 수행할 수 밖에 없기때문에, 예전에는 하나의 CPU에서만 GC를 수행했지만, 근래에 들어서 하나의 CPU에서 동시에 여러개의 Thread를 실행할 수 있는 Hyper Threading기술이나, 여러개의 CPU를 동시에 장착한 HW의 보급으로 하나의 HW Box에서 동시에 여러개의 Thread를 수행할 수 있게 되었다.

JDK 1.4부터 지원되는 Parallel GC는 Minor GC를 동시에 여러개의 Thread를 이용해서 GC를 수행하는 방법으로 하나의 Thread를 이용하는것보다 훨씬 빨리 GC를 수행할 수 있다.


<그림 7. Parallel GC 개념도>


< 그림 7> 을 보자 왼쪽의 Default GC방법은 GC가 일어날때 Thread들이 작업을 멈추고, GC를 수행하는 thread만 gc를 수행한다. (그림에서 파란영역), Parallel GC에서는 여러 thread들이 gc를 수행이 가능하기 때문에, gc에 소요되는 시간이 낮아진다.

Parallel GC가 언제나 유익한것은 아니다. 앞에서도 말했듯이 1CPU에서는 동시에 여러개의 thread를 실행할 수 없기 때문에 오히혀 Parallel GC가 Default GC에 비해서 느리다. 2 CPU에서도 Multi thread에 대한 지원이나 계산등을 위해서 CPU Power가 사용되기 때문에, 최소한 4CPU의 256M 정도의 메모리를 가지고 있는 HW에서 Parallel GC가 유용하게 사용된다.

Parallel GC는 크게 두가지 종류의 옵션을 가지고 있는데,Low-pause 방식과 Throughput 방식의 GC방식이 있다.

Solaris 기준에서 Low-pause Parallel GC는 ?XX:+UseParNewGC 옵션을 사용한다. 이 모델은 Old 영역을 GC할때 다음에 설명할 Concurrent GC방법과 함께 사용할 수 있다. 이 방법은 GC가 일어날때 빨리 GC하는것이 아니라 GC가 발생할때 Application이 멈춰지는 현상(pause)를 최소화하는데 역점을 뒀다.

Throughput 방식의 Parallel GC는 ?XX:+UseParallelGC (Solaris 기준) 옵션을 이용하며 Old 영역을 GC할때는 Default GC (Mark and compact)방법만을 사용하도록 되어 있다.Minor GC가 발생했을때, 되도록이면 빨리 수행하도록 throughput에 역점을 두었다.

그외에도 ParallelGC를 수행할때 동시에 몇개의 Thread를 이용하여 Minor영역을 Parallel GC할지를 결정할 수 있는데, -XX:ParallelGCThreads= 옵션을 이용하여 Parallel GC에 사용되는 Thread의 수를 지정할 수 있다.

3) Concurrent GC

앞에서도 설명했듯이, Full GC즉 Old 영역을 GC하는 경우에는 그 시간이 길고 Application이 순간적으로 멈춰버리기 때문에, 시스템 운용에 문제가 된다.

그래서 JDK1.4부터 제공하는 Concurrent GC는 기존의 이런 Full GC의 단점을 보완하기 위해서 Full GC에 의해서 Application이 멈추어 지는 현상을 최소화 하기 위한 GC방법이다.
Full GC에 소요되는 작업을 Application을 멈추고 진행하는것이 아니라, 일부는 Application이 돌아가는 단계에서 수행하고, 최소한의 작업만을 Application이 멈췄을때 수행하는 방법으로 Application이 멈추는 시간을 최소화한다.


<그림 8. Concurrent GC 개념도>


그 림 8에서와 같이 Application이 수행중일때(붉은 라인) Full GC를 위한 작업을 수행한다. (Sweep,mark) Application을 멈추고 수행하는 작업은 일부분 (initial-mark, remark 작업)만을 수행하기 때문에, 기존 Default GC의 Mark & Sweep Collector에 비해서 Application이 멈추는 시간이 현저하게 줄어든다.

Solaris JVM에서는 -XX:+UseConcMarkSweepGC Parameter를 이용해 세팅한다.

4) Incremental GC (Train GC)

Incremental GC또는 Train GC라고도 불리는 GC방법은 JDK 1.3에서부터 지원된 GC방법이다. 앞에서 설명한 Concurrent GC와 비슷하게, 의도 자체는 Full GC에 의해서 Application이 멈추는 시간을 줄이고자 하는데 있다.

Incremental GC의 작동방법은 간단하다. Minor GC가 일어날때 마다 Old영역을 조금씩 GC를 해서 Full GC가 발생하는 횟수나 시간을 줄이는 방법이다.


<그림 9. Incremental GC 개념도>


그 림 9에서 보듯이. 왼쪽의 Default GC는 FullGC가 일어난후에나 Old 영역이 Clear된다. 그러나, 오른쪽의 Incremental GC를 보면 Minor GC가 일어난후에, Old 영역이 일부 Collect된것을 볼 수 있다.

Incremental GC를 사용하는 방법은 JVM 옵션에 ?Xinc 옵션을 사용하면 된다.
Incremental GC는 많은 자원을 소모하고, Minor GC를 자주일으키고, 그리고 Incremental GC를 사용한다고 Full GC가 없어지거나 그 횟수가 획기적으로 줄어드는 것은 아니다. 오히려 느려지는 경우가 많다. 필히 테스트 후에 사용하도록 하자.

※ Default GC이외의 알고리즘은 Application의 형태나 HW Spec(CPU수, Hyper threading 지원 여부), 그리고 JVM 버전(JDK 1.4.1이냐 1.4.2냐)에 따라서 차이가 매우 크다. 이론상으로는 실제로 성능이 좋아보일 수 있으나, 운영환경에서는 여러 요인으로 인해서 기대했던것만큼의 성능이 안나올 수 있기 때문에, 실환경에서 미리 충분한 테스트를 거쳐서 검증한후에 사용해야 한다.


5. GC 로그는 어떻게 수집과 분석


JVM에서는 GC 상황에 대한 로그를 남기기 위해서 옵션을 제공하고 있다.
Java 옵션에 ?verbosegc 라는 옵션을 주면되고 HP Unix의 경우 ?verbosegc ?Xverbosegc 옵션을 주면 좀더 자세한 GC정보를 얻을 수 있다. GC 정보는 stdout으로 출력이 되기 때문에 “>” redirection등을 이용해서 file에 저장해놓고 분석할 수 있다.

Example ) java ?verbosegc MyApplication

그럼 실제로 나온 GC로그를 어떻게 보는지를 알아보자.


<그림 5. 일반적인 GC 로그, Windows, Solaris>


<그림 5>는 GC로그 결과를 모아논 내용이다. (실제로는 Application의 stdout으로 출력되는 내용과 섞여서 출력된다.)
Minor GC는 ”[GC “로 표기되고, Full GC는 “[Full GC”로 표기된다.
그 다음값은 Heap size before GC인데,GC 전에 Heap 사용량 ( New/Young 영역 + Old 영역 + Perm 영역)의 크기를 나타낸다.

Heap size after GC는 GC가 발생한후에 Heap의 사용량이다. Minor GC가 발생했을때는 Eden과 Survivor 영역으 GC가 됨으로 Heap size after GC는 Old영역의 용량과 유사하다.(Minor GC에서 GC되지 않은 하나의 Survivor영역내의 Object들의 크기도 포함해야한다.)

Total Heap Size는 현재 JVM이 사용하는 Heap Memory양이다. 이 크기는 Java에서 ?ms와 ?mx 옵션으로 조정이 가능한데. 예를 들어 ?ms512m ?mx1024m로 해놓으면 Java Heap은 메모리 사용량에 따라서 512~1024m사이의 크기에서 적절하게 늘었다 줄었다한다. (이 늘어나는 기준과 줄어드는 기준은 (-XX:MaxHeapFreeRatio와 ?XX:MinHeapFreeRation를 이용해서 조정할 수 있으나 JVM vendor에 따라서 차이가 나기때문에 각 vendor별 JVM 메뉴얼을 참고하기 바란다.) Parameter에 대한 이야기는 추후에 좀더 자세히하도록 하자.

그 다음값은 GC에 소요된 시간이다.

< 그림 5>의 GC로그를 보면 Minor GC가 일어날때마다 약 20,000K 정도의 Collection이 일어난다. Minor GC는 Eden과 Suvivor영역 하나를 GC하는 것이기 때문에 New/Young 영역을 20,000Kbyte 정도로 생각할 수 있다.

Full GC때를 보면 약44,000Kbyte에서 1,749Kbyte로 GC가 되었음을 볼 수 있다. Old영역에 큰 데이타가 많지 않은 경우이다. Data를 많이 사용하는 Application의 경우 전체 Heap이 512이라고 가정할때, Full GC후에도 480M정도로 유지되는 경우가 있다. 이런 경우에는 실제로 Application에서 Memory를 많이 사용하고 있다고 판단할 수 있기 때문에 전체 Heap Size를 늘려줄 필요가 있다.

이렇게 수집된 GC로그는 다소 보기가 어렵기 때문에, 좀더 쉽게 분석할 수 있게 하기 위해서 GC로그를 awk 스크립트를 이용해서 정제하면 분석이 용이하다.


<표 2. gc.awk 스크립트>


이 스크립트를 작성한후에 Unix의 awk 명령을 이용해서

% awk ?f gc.awk GC로그파일명

을 쳐주면 아래<표 3>와 같이 정리된 형태로 GC 로그만 추출하여 보여준다.


<표 3. gc.awk 스크립트에 의해서 정재된 로그>


Minor와 Major는 각각 Minor GC와 Full GC가 일어날때 소요된 시간을 나타내며, Alive는 GC후에 남아있는 메모리양, 그리고 Freed는 GC에 의해서 collect된 메모리 양이다.

이 로그파일은 excel등을 이용하여 그래프등으로 변환해서 보면 좀더 다각적인 분석이 가능해진다.

※ JDK 1.4에서부터는 ?XX:+PrintGCDetails 옵션이 추가되어서 좀더 자세한 GC정보를 수집할 수 있다.


※ HP JVM의 GC Log 수집

HP JVM은 전체 heap 뿐 아니라 ?Xverbosegc 옵션을 통해서 Perm,Eden,Old등의 모든 영역에 대한 GC정보를 좀더 정확하게 수집할 수 있다.

Example ) java ?verbosegc ?Xverbosegc MyApplication ß (HP JVM Only)

HP JVM의 GC정보는 18개의 필드를 제공하는데 그 내용을 정리해보면 <표 4.>와 같다.

<GC : %1 %2 %3 %4 %5 %6 %7 %8 %9 %10 %11 %12 %13 %14 %15 %16 %17 %18>


<표 4. HP JVM GC 로그 필드별 의미>


이 로그를 직접 보면서 분석하기는 쉽지가 않다. 그래서, HP에서는 좀더 Visual한 환경에서 분석이 가능하도록 HPJtune이라는 툴을 제공한다. 다음 URL에서 다운로드 받을 수 있다.

http://www.hp.com/products1/unix/java/java2/hpjtune/index.html


<그림 6. HP Jtune을 이용해서 GC후 Old영역의 변화 추이를 모니터링하는 화면>




6. GC 관련 Parameter


GC 관련 설정값을 보기전에 앞서서 ?X와 ?XX 옵션에 대해서 먼저 언급하자. 이 옵션들은 표준 옵션이 아니라, 벤더별 JVM에서 따로 제공하는 옵션이기 때문에, 예고 없이 변경되거나 없어질 수 있기 때문에, 사용전에 미리 JVM 벤더 홈페이지를 통해서 검증한다음에 사용해야한다.

1) 전체 Heap Size 조정 옵션

전체 Heap size는 ?ms와 ?mx로 Heap 사이즈의 영역을 조정할 수 있다. 예를 들어 ?ms512m ?mx 1024m로 설정하면 JVM은 전체 Heap size를 application의 상황에 따라서 512m~1024m byte 사이에서 사용하게 된다. 그림2의 Total heap size

메모리가 모자를때는 heap을 늘리고, 남을때는 heap을 줄이는 heap growing과 shirinking 작업을 수행하는데, 메모리 변화량이 큰 애플리케이션이 아니라면 이 min heap size와 max heap size는 동일하게 설정하는 것이 좋다. 일반적으로 1GB까지의 Heap을 설정하는데에는 문제가 없으나, 1GB가 넘는 대용량 메모리를 설정하고자 할 경우에는 별도의 JVM 옵션이 필요한 경우가 있기때문에 미리 자료를 참고할 필요가 있다.

※ IBM AIX JVM의 경우
%export LDR_CNTRL=MAXDATA=0x10000000
%java -Xms1500m -Xmx1500m MyApplication

2) Perm size 조정 옵션

Perm Size는 앞에서도 설명했듯이, Java Application 자체(Java class etc..)가 로딩되는 영역이다. J2EE application의 경우에는 application 자체의 크기가 큰 편에 속하기 때문에, Default로 설정된 Perm Size로는 application class가 loading되기에 모자른 경우가 대부분이기 때문에, WAS start초기나, 가동 초기에 Out Of Memory 에러를 유발하는 경우가 많다.

PermSize는 -XX:MaxPermSize=128m 식으로 지정할 수 있다.
일반적으로 WAS에서 PermSize는 64~256m 사이가 적절하다.

3) New 영역과 Old 영역의 조정New 영역은 ?XX:NewRatio=2 에 의해서 조정이 된다.
NewRatio Old/New Size의 값이다. 전체 Heap Size가 768일때, NewRatio=2이면 New영역이 256m, Old 영역이 512m 로 설정이 된다.
JVM 1.4.X에서는 ?XX:NewSize=128m 옵션을 이용해서 직접 New 영역의 크기를 지정하는 것이 가능하다.

4) Survivor 영역 조정 옵션
-XX:SurvivorRatio=64 (eden/survivor 의 비율) :64이면 eden 이 128m일때, survivor영역은 2m가 된다.

5) -server와 ?client 옵션
JVM에는 일반적으로 server와 client 두가지 옵션을 제공한다.
결 론만 말하면 server 옵션은 WAS와 같은 Server환경에 최적화된 옵션이고, client옵션은 워드프로세서와 같은 client application에 최적화된 옵션이다. 그냥 언뜻 보기에는 단순한 옵션 하나로보일 수 있지만, 내부에서 돌아가는 hotspot compiler에 대한 최적화 방법과 메모리 구조자체가 아예 틀리다.

○ -server 옵션

server 용 application에 최적화된 옵션이다. Server application은 boot up 시간 보다는 user에 대한 response time이 중요하고, 많은 사용자가 동시에 사용하기 때문에 session등의 user data를 다루는게 일반적이다. 그래서 server 옵션으로 제공되는 hotspot compiler는 java application을 최적화 해서 빠른 response time을 내는데 집중되어 있다.

또한 메모리 모델 역시, 서버의 경우에는 특정 사용자가 서버 운영시간동안 계속 서버를 사용하는게 아니기 때문에 (Login하고, 사용한 후에는 Logout되기 때문에..) 사용자에 관련된 객체들이 오래 지속되는 경우가 드물다. 그래서 상대적으로 Old영역이 작고 New 영역이 크게 배정된다. <그림 7. 참조 >

○ -client 옵션

client application은 워드프로세서 처럼 혼자 사용하는 application이다. 그래서 client application은 response time보다는 빨리 기동되는데에 최적화가 되어 있다. 또한대부분의 client application을 구성하는 object는GUI Component와 같이 application이 종료될때까지 남아있는 object의 비중이 높기 때문에 상대적으로 Old 영역의 비율이 높다.


<그림 7. ?server와 ?client 옵션에 따른 JVM Old와 New영역>


이 두옵션은 가장 간단한 옵션이지만, JVM의 최적화에 아주 큰부분을 차지하고 있는 옵션이기 때문에, 반드시 Application의 성격에 맞춰서 적용하기 바란다.
(※ 참고로, SUN JVM은 default가 client, HPJVM는 default가 server로 세팅되어 있다.)

○ GC 방식에 대한 옵션

GC 방식에 대한 옵션은 앞에서도 설명했지만, 일반적인 GC방식이외에, Concurrent GC,Parallel GC,Inceremental GC와 같이 추가적인 GC Algorithm이 존재한다. 옵션과 내용은 앞장에서 설명한 “다양한 GC알고리즘” 을 참고하기 바란다.


7.JVM GC 튜닝


그러면 이제부터 지금까지 설명한 내용을 기반으로 실제로 JVM 튜닝을 어떻게 하는지 알아보도록 하자.

STEP 1. Application의 종류와 튜닝목표값을 결정한다.

JVM 튜닝을 하기위해서 가장 중요한것은 JVM 튜닝의 목표를 설정하는것이다. 메모리를 적게 쓰는것이 목표인지, GC 횟수를 줄이는것이 목표인지, GC에 소요되는시간이 목표인지, Application의 성능(Throughput or response time) 향상인지를 먼저 정의한후에. 그 목표치에 근접하도록 JVM Parameter를 조정하는것이 필요하다.

STEP 2. Heap size와 Perm size를 설정한다.

-ms 와 ?mx 옵션을 이용해서 Heap Size를 정한다. 일반적으로 server application인 경우에는 ms와 mx 사이즈를 같게 하는것이 Memory의 growing과 shrinking에 의한 불필요한 로드를 막을 수 있어서 권장할만하다.

ms와mx사이즈를 다르게 하는 경우는 Application의 시간대별 memory 사용량이 급격하게 변화가 있는 Application에 효과적이다.
PermSize 는 JVM vendor에 따라 다소 차이가 있으나 일반적으로 16m정도이다. Client application의 경우에는 문제가 없을 수 있지만, J2EE Server Application의 경우 64~128m 사이로 사용이 된다.

Heap Size와 Perm Size는 아래 과정을 통해서 적정 수치를 얻어가야한다.

STEP 3. 테스트 & 로그 분석.

JVM Option에 GC 로그를 수집하기 위한 ?verbosegc 옵션을 적용한다. (HP의 경우 ?Xverbosegc 옵션을 적용한다.)

LoadRunner 나 MS Strest(무료로 MS社의 홈페이지에서 다운로드 받을 수 있다.)와 같은 Strest Test툴을 통해서 Application에 Strest를 줘서. 그 log를 수집한다. 튜닝에서 있어서 가장 중요한것은 목표산정이지만, 그만큼이나 중요한것은 실제 Tuning한 Parameter가 Application에 어떤 영향을 주는지를 테스트하는 방법이 매우 중요하다. 그런 의미에서 적절한 Strest Tool의 선정과, Strest Test 시나리오는 정확한 Tuning을 위해서 매우 중요한 요인이다.

○ Perm size 조정
아래 그림8.은 HP JVM에서 ?Xverbosegc 옵션으로 수집한 GC log를 HP Jtune을 통해서 graph로 나타낸 그래프이다. 그림을 보면 Application이 startup되었을때 Perm 영역이 40m에서. 시간이 지난후에도 50m 이하로 유지되는것을 볼 수 있다. 특별하게 동적 classloading등이 수십m byte가 일어나지 않는등의 큰 변화요인이 없을때, 이 application의 적정 Perm 영역은 64m로 판단할 수 있다.


<그림 8. GC 결과중 Perm 영역 그래프>


○ GC Time 수행 시간 분석

다음은 GC에 걸린 시간을 분석해보자. 앞에 강좌 내용에서도 설명햇듯이. GC Tuning에서 중요한 부분중 하나가 GC에 소요되는 시간 특히 Full GC 시간이다.

지금부터 볼 Log는 모社의 물류 시스템의 WAS 시스템 GC Log이다. HP JVM을 사용하며, -server ?ms512m ?mx512m 옵션으로 기동되는 시스템이다.

그림 9를 보면 Peak 시간 (첫번째 동그라미) 14시간동안에 Full GC(동그란점)가 7번일어난것을 볼 수 있다. 각각에 걸린 시간은2.5~6sec 사이이다.
여기서 STEP 1.에서 설정한 AP Tuning의 목표치를 참고해야하는데.

Full GC가 길게 일어나서 Full GC에 수행되는 시간을 줄이고자 한다면 Old 영역을 줄이면 Full GC가 일어나는 횟수는 늘어나고, 반대로 Full GC가 일어나는 시간을 줄어들것이다.

반대로 Full GC가 일어나는 횟수가 많다면, Old 영역을 늘려주면 Full GC가 일어나는 횟수는 상대적으로 줄어들것이고 반대로 Full GC 수행시간이 늘어날 것이다.

특 히 Server Application의 경우Full GC가 일어날때는 JVM자체가 멈춰버리기 때문에, 그림 9의 instance는 14시간동안 총 7번 시스템이 멈추고, 그때마다 2.5~6sec가량 시스템이 response를 못하는 상태가 된것이다. 그래서 멈춘 시간이 고객이 납득할만한 시간인지를 판단해야 하고, 거기에 적절한 Tuning을 해야한다.

Server Application에서 Full GC를 적게일어나게하고, Full GC 시간을 양쪽다 줄이기 위해서는 Old영역을 적게한후에, 여러개의 Instance를 동시에 뛰어서 Load Balancing을 해주면, Load가 분산되기 때문에 Full GC가 일어나는 횟수가 줄어들테고, Old 영역을 줄였기 때문에, Full GC에 드는 시간도 줄어들것이다. 또한 각각의 FullGC가 일어나는동안 하나의 서버 instance가 멈춰져 있어도, Load Balancing이 되는 다른 서버가 response를 하고 있기때문에, Full GC로 인한 Application이 멈추는것에 의한 영향을 최소화할 수 있다.


<그림 9. GC 소요시간>


데이타에 따라서 GC Tuning을 진행한후에는 다시 Strest Test를 진행해서 응답시간과 TPS(Throughput Per Second)를 체크해서 어떤 변화를 주었는지를 반드시 체크해봐야한다.


<그림 10. GC후의 Old 영역>


그림 10은 GC후에 Old 영역의 메모리 변화량을 나타낸다.

금 요일 업무시간에 메모리 사용량이 올라가다가. 주말에가서 완만한 곡선을 그리는것을 볼 수 있다. 월요일 근무시간에 메모리 사용량이 매우 많고, 화요일에도 어느정도 메모리 사용량이 있는것을 볼 수 있다. 월요일에 메모리 사용량이 많은것을 볼때, 이 시스템의 사용자들이 월요일에 시스템 사용량이 많을 수 있다고 생각할 수 있고, 또는 다른 주의 로그를 분석해봤을때 이 주만 월요일 사용량이 많았다면, 특별한 요인이나 Application 변경등이 있었는지를 고려해봐야할것이다.

이 그래프만을 봤을때 Full GC가 일어난후에도 월요일 근무시간을 보면 Old 영역이 180M를 유지하고 있는것을 볼 수 있다. 이 시스템의 Full GC후의 Old영역은 80M~180M를 유지하는것을 볼 수 있다. 그래서 이 시스템은 최소 180M이상의 Old 영역을 필요로하는것으로 판단할 수 있다.

STEP 4. Parameter 변경.
STEP 3에서 구한 각 영역의 허용 범위를 기준으로 Old영역과 New 영역을 적절하게 조절한다.
PermSize와 New영역의 배분 (Eden,Survivor)영역등을 조정한다.
PermSize 는 대부분 Log에서 명확하게 나타나기 때문에, 크게 조정이 필요가 없고 New영역내의 Eden과 Survivor는 거의 조정하지 않는다. 가장 중요한것은 Old영역과 New 영역의 비율을 어떻게 조정하는가가 관건이다.

이 비율을 결정하면서, STEP1에서 세운 튜닝 목표에 따라서 JVM의 GC Algorithm을 적용한다. GC Algorithm을 결정하는 기본적인 판단 내용은 아래와 같다.



이렇게 Parameter를 변경하면서 테스트를 진행하고, 다시 변경하고 테스트를 진행하는 과정을 거쳐서 최적의 Parameter와 GC Algorithm을 찾아내는것이 JVM의 메모리 튜닝의 이상적인 절차이다.


지금까지 JVM의 메모리 구조와 GC 모델 그리고 GC 튜닝에 대해서 알아보았다.

정 리하자면 GC 튜닝은 Application의 구조나 성격 그리고, 사용자의 이용 Pattern에 따라서 크게 좌우 되기때문에, 얼마만큼의 Parameter를 많이 아느냐 보다는 얼마만큼의 테스트와 로그를 통해서 목표 값에 접근하느냐가 가장 중요하다.


출처 : http://www.j2eestudy.co.kr/lecture_data/lecture0401_1_1_index.html
Posted by 용식
TAG GC, java

Collection 하나 더..

Java 2008.06.03 11:31

여태 개발하면서 무지 많이 써오던 컬랙션들. 성능이나 별다른 고민없이 걍 대충 써왔던 것 같다. "켄트 벡의 구현 패턴"이란 책을 보다 보니 자세한 설명이 있어서 그 동안 알고 있던것과 더불어 정리해 두는 게 조을 것 같다.


1. 인터페이스

The core collection interfaces.

Queue는 거의 사용하지 않고 책에 없으니까 생략 ^^


  - 배열

     가장 단순하지만 가장 유연하지 못한 컬렉션.

     크기가 고정되어 있고 원소 접근 방법이 용이하면 빠르다.

     단순한 연산의 경우 배열은 다른 컬렉션에 비해 시간, 공간 모든 면에서 효율적이다.

     일반적으로 배열 접근(element[i])은 ArrayList를 사용했을 때(elements.get(i))에 비해 10배 이상

     빠르다고 한다.

     대부분의 경우 유연성 문제 때문에 배열보다는 다른 컬렉션을 사용하고, 프로그램의 일부에서 성능이

     중요한 경우 배열을 사용하는 것도 고려하는 것이 좋을 듯


  - Iterable

     기본적인 컬렉션 인터페이스로 순차 열람(iteration)을 지원한다.

     어떤 변수를 Iterable로 선언하는 것은 그 변수가 여러 개의 값을 갖고 있음을 뜻할 뿐이다.

     실제로 Iterable 인터페이스를 살펴 보면 자바 컬렉션의 모든 인터페이스, 구현 클래스들이 implement

     하고 있는 것을 확인할 수 있고 Iterable에 정의된 메소드는 Iterator<T> iterator() 뿐이다.

     Iterator를 이용하면 Iterator 인터페이스에서 지원하는 세가지 메소드

     (hasNext(), next(), remove())를 사용할 수 있다.

     자바 5에서는 암묵적으로 iterator() 메소드를 호출하여

     for (Element element : elements) {

         ......

     }

     의 형식으로 간편하게 루프를 구성할 수 있게 한다.

     실제 프로그램에서는 Iterable 인터페이스를 직접 사용할 일은 없으니 이런게 있다고 정도만 알아두면

     될것이다.


  - Collection

     Iterable을 상속하며, 원소의 추가, 삭제, 검색, 크기 지원 등의 메소드를 추가로 지원한다.

    

  - List

     원소의 순서가 정의되어 있으며, 컬렉션상의 위치를 통해 원소에 접근할 수 있다.

     따라서 List를 사용하면 컬렉션 상에서의 인덱스를 통해 어떤 원소를 접근 할 수 있다.

    원소간의 순서가 중요한 경우, 예를 들어 도착 순서대로 메세지를 처리하는 큐의 경우에는 리스트를

    사용해야 한다.


  - Set

     중복된 원소가 없는 컬렉션

     중복원소(상호간 equals()의 결과가 참인 원소)를 허용하니 않는 컬렉션

     원소 사이의 순서가 없으므로, 이전 순차 열람할 때의 원소 순서가 다음 순차 열람할 때 보장되지

     않는다.

    

  - SortedSet

     중복된 원소가 없으며 원소간의 순서가 정해진 컬렉션

     컬렉션에 추가된 순서나 명시적인 인덱스 번호에 따라 순서가 정해지는 List와 달리

     SortedSet은 Comparator에 의해 순서를 정한다. 명시적인 순서를 제공하지 않는 경우에는

     "자연 순서(natural order)"가 사용된다. 예를 들어 문자열은 알파벳 순으로 정렬

    

     아래는 Comparator의 사용예

     public Collection<String> getAlphabeticalAuthors() {

         Comparator<Author> sorter = new Comparator<Author>() {

             public int compare(Author o1, Author o2) {

                 if (o1.getLastName().equals(o2.getLastName())) {

                     return o1.getFirstName().compareTo(o2.getFirstName());

                 return o1.getLastName().compareTo(o2.getLastName());

             }

         };

         SortedSet<Author> results = new TreeSet<Author>(sorter);

         for (Book each: getBooks()) {

             results.add(each.getAuthor());

         }

         return results;

     }


  - Map

     키에 의해 원소를 저장하고 접근하는 컬렉션

     Map은 List처럼 키를 사용해서 원소를 저장하지만, List가 정수만을 키로 사용할 수 있는 반면

     Map은 임의의 객체를 키로사용할 수 있다.

     또 Map는 다른 컬렉션 인터페이스와는 형태가 상이하여 다른 컬렉션 인터페이스를 상속하지 않고,

     내부적으로 키에 대한 컬렉션과 데이터에 대한 컬렉션의 2개 컬렉션을 유지한다.


컬렉션을 사용할 때는 항상 인터페이스를 선언하여 사용

List<String> list = new ArrayList<String>();

Map<String, String> map = new HashMap<String, String>();

Collection 인터페이스를 사용하면 유연성은 가장 높겠지만 실제 사용한 적은 거의 없는 것 같다.

List, Set, Map이면 ㅇㅋㅂㄹ


2. 구현

컬렉션에 대해 구현 클래스를 선택하는 것은 주로 성능과 관련이 있다.

위의 표에 소개한 구현 이외에도 무지하게 많은 구현들이 있다. 각 구현체의 특성을 살펴보고 필요한 것을

가져다 사용하면 된다.

일단 가장 단순한 구현을 사용하여 시작하고 추후 경험에 따라 튜닝하는 것이 좋다.

컬렉션중 가장 많이 사용되는 클래스는 ArrayList이며, 그 다음은 HashSet이다.

(이클립스와 JDK에서 ArrayList는 3400번, HashSet은 800번 사용되었다고 한다.)


  - Collection 구현

     Collection 인터페이스만 구현한 클래스는 없는 듯 하다. 단순한 컬렉션이 필요한 경우

     그냥 ArrayList를 사용하자. ArrayList 사용시 성능상 문제되는 부분은 컬렉션의 크기에 비례해서

     연산 시간이 커지는 contains(Ojbect)와 이 메소드를 이용하는 다른 메소드(remove() 등)이 있다.

     이 때 중복 원소들을 제거해도 상관이 없다면 HashSet으로 교체하면 좋다. 그러나 중복 원소가 이미

     없는 경우라면 별 차이가 없을 수도 있다.


  - List 구현

     ArrayList와 LinkedList

     ArrayList는 원소 접근이 빠르고 원소 추가 및 제거가 느린 반면

     LinkedList는 원소 접근이 느리지만 원소 추가와 제거는 빠르다.


  - Set, SortedSet 구현

     HashSet은 가장 빠르지만 원소간의 순서를 보장해주지 않는다.

     LinkedHashSet은 원소 간 순서를 보장해 주지만 원소 추가 삭제 시 30% 정도 시간이 더 걸린다.

     TreeSet은 Comparator에 따라서 원소를 정렬하지만 원소 추가 삭제 시간이

     logn(n은 컬렉션의 크기)에 비례해서 커진다.


  - Map 구현

     Map 구현은 Set 구현과 비슷한 패턴을 보인다.

     HashMap은 가장 빠르고 단순하다.

     LinkedHashMap은 컬렉션에 추가된 원소 간의 순서를 보장한다.

     TreeMap(SortedMap 의 구현)은 키의 순서에 따라 순차 열람이 가능하지만 원소의 추가 제거 시간이

     logn(n은 컬렉션의 크기)에 비례한다.


3. Collections

Collections는 다른 컬렉션 인터페이스에 넣기 적절치 않은 기능들을 모아 놓은 유틸리티 클래스이다.


  - 검색

     indexOf() 연산에 걸리는 시간은 리스트의 크기에 비례한다. 원소들이 정렬되어 있을 경우

     Collections.binarySearch(list, element)를 사용하여 log2n에 비례하는 시간에 검색할 수 있다.

     원소가 리스트에 존재하지 않는다면 음수를 반환하고, 리스트가 정렬되어 있지 않다면 결과는 예측불가


  - 정렬

     reverse(list)는 리스트에 속해 있는 모든 원소 간의 순서를 거꾸로 바꾼다.

     shuffle(list)는 순서를 임의로 바꾼다.

     sort(list), sort(list, comparator)는 오름차순으로 원소를 정렬한다.

     이진 검색과 달리 ArrayList와 LinkedList에서 정렬 수행 성능은 거의 같다. 정렬을 수행할 경우

     컬렉션의 원소들이 일단 배열로 복사되어 정렬된 후 다시 본래의 컬렉션으로 복사되기 때문


  - 수정 불가능한 컬렉션

     신뢰할 수 없는 코드에 컬렉션을 전달하는 경우 Collections.unmodifiableCollection() 메소드를

     이용하면 클라이언트가 수정하려 들 경우 예외를 발생시키도록 할 수 있다.


  - 단일 원소 컬렉션

     하나의 원소를 전달해야 하지만 컬렉션 인터페이스를 사용해야 하는 경우 사용

     Set의 경우 Collections.singleton(T o), List와 Map의 경우 singletonList(T o),

     singletonMap(K key, V value)를 사용


  - 무원소 컬렉션

     컬렉션 인터페이스를 사용해야 하지만 전달할 원소가 없는 경우에는 Collections에서 수정할 수 없는

     무원소 컬렉션을 생성해서 사용

     Collections.emptyList(), emptySet(), emptyMap()


  - 동기화 컬렉션

     이전 시대의 유물인 Vector와 Hashtable이 ArrayList와 HashMap간의 차이점은 전자가 쓰레드 안전인

     반면 후자는 아니라는 것이다.

     동기화가 필요없는 경우라면 ArrayList, HashMap을 사용하고 동기화가 필요한 경우

     Collections.synchronizedCollection(), Collections.synchronizedList(),

     Collections.synchronizedSet(), Collections.synchronizedMap()를 사용하여

     ArrayList, HashMap을 래핑하면 멀티 쓰레드 환경에서도 걱정이 사라진다.

[출처] 자바 Collection|작성자 바람의혼

Posted by 용식

TV들이 아주 작 작동하는 군요. 하지만 사용자는 인터페이스를 통해서 조작하고 있습니다. 물론 TV를 만들어지만 TV내부는 각 회사에서 알아서 하겠죠. 하지만 TV의 인터페이스가 같기 때문에 어느 회사의 제품이나 조작하기는 편할 것입니다. 데이터베이스의 드라이브도 이러한 방식으로 Sun사에서 Database의 interface를 정의해두고 그 interface에 의해서 만들도록 하는 것입니다.

 

                           TVBoard s = new SSgTV();

                           TVBoard g = new LGgTV();

 

이 두 줄을 유심히 봐 주시기 바랍니다. 일반적인 Upcasting의 개념이 그대로 interface에서도 적용되고 있습니다. 이것은 인터페이스 자체도 클래스이기 때문에 Upcasting을 적용할 수 있는 것입니다. 사용자 측면에서 인터페이스가 고정되어 있으면 많은 시간과 비용을 절약할 수 있을 것입니다. 모든 TV가 같은 인터페이스로 되어 있다면 하나의 조작법만 안다면 모든 TV를 조작 할 수 있을 것입니다. 이것이 바로 인터페이스의 본질적인 역할입니다. 말 그대로 인터페이스는 인터페이스입니다.

 

 

 

 

 

 

5.4.3 Abstract와 Interface에서의 Upcasting

 

 우리는 upcasting을 일반적인 클래스 구조에서만 살펴 보았습니다. 하지만 이것은 abstract와 interface에서도 적용 되어 질 수 있습니다. 그 구조는 다음과 같을 것입니다.

그런데 다음과 같은 질문을 할 수 있을 것입니다.

n         abstract는 객체를 생성할 수 없다.

n         interface는 객체를 생성할 수 없다.

이러한 말에 부딪히게 됩니다. abstract와 interface는 객체를 생성하는 것이 아니라 만들어진 객체를 할당 받는 것입니다. 형은 같기 때문에 이러한 것이 가능합니다. abstract나 interface로는 new연산자를 사용하지 않습니다. 단지 만들어진 객체를 upcasting을 이용해서 넘겨 받을 뿐입니다. 이러한 것은 많은 장점을 내포하고 있습니다. 알고 보면 interface는 빈 껍데기에 불과 합니다.

사용자는 용량이 적은 빈 껍데기만 가지고 있어도 모든 작업을 전부 수행 할 수 있습니다. 우의 그림에서는 설명을 위해서 임의의 interface만들었습니다. 이 인터페이스를 이용해서 우리는 다음과 같은 코드를 작성 할 수 있을 것입니다.

 

n         Shape s = new Circle();

n         Action a = new Circle();

n         Attribute ab = new Circle();

n         Circle c = new Circle();

 

이 코드의 장점은 사용자는 내부를 몰라도 된다는 것입니다. interface만 있다면 어떠한 작업이라도 전부 할 수 있습니다. 이러한 장점은 우리가 직접 만들어서 작업하기 보다는 내부적으로 이루어지는 경우가 아주 많습니다. 하지만 이것을 잘 이용하면 보다 정교한 프로그램작업을 할 수 있으리라 생각됩니다.

 

 

5.4.4 결론

여러분들이 잘 모르고 사용하는 Upcasting은 아주 많은 장점을 제공하고 있습니다. 자바도 여러 종류이지만 고급 자바로 갈수록 이 Upcasting개념은 더욱 철저하게 적용됩니다. RMI, Enterprise Java Beans, Mobile Java, Personal Java, Java Dynamic Management Kit등 사용 안 되는 곳이 없습니다. 어차피 자바를 배우면 실무에서 다루지 않겠습니까?

 

다형성을 주장하는 Upcasting의 장점은 abstract, interface, encapsulation, overriding 등 아주 많은 부분과 관련이 있습니다. 이 upcasting과 interface에 대해서 제대로 이해했다면 자바는 바로 여러분들의 손에 있는 것입니다.

 

출처 : www.jabook.org

Posted by 용식