반응형
public class test  
{
public static void main(String[] args)  
{
java.util.Enumeration e = System.getProperties().propertyNames();

while(e.hasMoreElements()){
String obj = (String)e.nextElement();

System.out.print(obj + " ===> ");
System.out.println(System.getProperty(obj));
}
}
}


: result
java.runtime.name ===> Java(TM) 2 Runtime Environment, Standard Edition
sun.boot.library.path ===> C:\j2sdk1.4.2_03\jre\bin
java.vm.version ===> 1.4.2_03-b02
java.vm.vendor ===> Sun Microsystems Inc.
java.vendor.url ===> http://java.sun.com/
path.separator ===> ;
java.vm.name ===> Java HotSpot(TM) Client VM
file.encoding.pkg ===> sun.io
user.country ===> KR
sun.os.patch.level ===> Service Pack 4
java.vm.specification.name ===> Java Virtual Machine Specification
user.dir ===> C:\Documents and Settings\Administrator\바탕 화면
java.runtime.version ===> 1.4.2_03-b02
java.awt.graphicsenv ===> sun.awt.Win32GraphicsEnvironment
java.endorsed.dirs ===> C:\j2sdk1.4.2_03\jre\lib\endorsed
os.arch ===> x86
java.io.tmpdir ===> C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\
line.separator ===>

java.vm.specification.vendor ===> Sun Microsystems Inc.
user.variant ===>
os.name ===> Windows 2000
sun.java2d.fontpath ===>
java.library.path ===> C:\j2sdk1.4.2_03\bin;.;C:\WINNT\system32;C:\WINNT;.;C:\j2
sdk1.4.2_03\bin;C:\oracle\ora92\bin;C:\Program Files\Oracle\jre\1.3.1\bin;C:\Pro
gram Files\Oracle\jre\1.1.8\bin;C:\WINNT\system32;C:\WINNT;C:\WINNT\System32\Wbe
m;F:\apache-ant-1.5.3-1\bin;C:\Program Files\SecureCRT\;C:\Program Files\SSH Com
munications Security\SSH Secure Shell
java.specification.name ===> Java Platform API Specification
java.class.version ===> 48.0
java.util.prefs.PreferencesFactory ===> java.util.prefs.WindowsPreferencesFactor
y
os.version ===> 5.0
user.home ===> C:\Documents and Settings\Administrator
user.timezone ===>
java.awt.printerjob ===> sun.awt.windows.WPrinterJob
file.encoding ===> MS949
java.specification.version ===> 1.4
user.name ===> Administrator
java.class.path ===> .;
java.vm.specification.version ===> 1.0
sun.arch.data.model ===> 32
java.home ===> C:\j2sdk1.4.2_03\jre
java.specification.vendor ===> Sun Microsystems Inc.
user.language ===> ko
awt.toolkit ===> sun.awt.windows.WToolkit
java.vm.info ===> mixed mode
java.version ===> 1.4.2_03
java.ext.dirs ===> C:\j2sdk1.4.2_03\jre\lib\ext
sun.boot.class.path ===> C:\j2sdk1.4.2_03\jre\lib\rt.jar;C:\j2sdk1.4.2_03\jre\li
b\i18n.jar;C:\j2sdk1.4.2_03\jre\lib\sunrsasign.jar;C:\j2sdk1.4.2_03\jre\lib\jsse
.jar;C:\j2sdk1.4.2_03\jre\lib\jce.jar;C:\j2sdk1.4.2_03\jre\lib\charsets.jar;C:\j
2sdk1.4.2_03\jre\classes
java.vendor ===> Sun Microsystems Inc.
file.separator ===> \
java.vendor.url.bug ===> http://java.sun.com/cgi-bin/bugreport.cgi
sun.cpu.endian ===> little
sun.io.unicode.encoding ===> UnicodeLittle
sun.cpu.isalist ===> pentium i486 i386


가끔 OS 및 JVM에 의존적인 환경변수들을 가져올 필요가 있습니다.
그럴때, API 레퍼런스를 찾아보면 나오지만, 그래도 정리를 해 봅니다..

Key Description of Associated Value
java.version Java Runtime Environment version
java.vendor Java Runtime Environment vendor
java.vendor.url Java vendor URL
java.home Java installation directory
java.vm.specification.version Java Virtual Machine specification version
java.vm.specification.vendor Java Virtual Machine specification vendor
java.vm.specification.name Java Virtual Machine specification name
java.vm.version Java Virtual Machine implementation version
java.vm.vendor Java Virtual Machine implementation vendor
java.vm.name Java Virtual Machine implementation name
java.specification.version Java Runtime Environment specification version
java.specification.vendor Java Runtime Environment specification vendor
java.specification.name Java Runtime Environment specification name
java.class.version Java class format version number
java.class.path Java class path
java.library.path List of paths to search when loading libraries
java.io.tmpdir Default temp file path
java.compiler Name of JIT compiler to use
java.ext.dirs Path of extension directory or directories
os.name Operating system name
os.arch Operating system architecture
os.version Operating system version
file.separator File separator ("/" on UNIX)
path.separator Path separator (":" on UNIX)
line.separator Line separator ("\n" on UNIX)
user.name User's account name
user.home User's home directory
user.dir User's current working directory

반응형
Enumeration : 자바 초기버젼에서 개발된 것으로, HashTable과 Vector에서 사용가능
Iterator : jdk 1.2버젼에서 개발된 것으로, Collection 인터페이스를 구현한 모든 클래스에서 사용가능.

API를 확인해볼까요??
Vector와 HashTable에 있는 메소드입니다.
사용자 삽입 이미지
(HashTable에는 Enumeration 타입의 key()메소드도 존재합니다.)

하지만, ArrayList, LinkedList, HashMap에서는 찾아볼 수 없었습니다.
iterator는 HashTable이든 HashMap이든, Vector든 LinkedList든,
모두 쓸 수 있다는 것이죠.

Iterator에 대해서는 저번에 알아봤으니까.
이번에는 Enumeration의 API를 간단하게 뒤져봐야겠네요.

hasMoreElements

boolean hasMoreElements()
Tests if this enumeration contains more elements.
Returns:
true if and only if this enumeration object contains at least one more element to provide; false otherwise
Iterator의 hasNext()와 똑같은 일을 하는군요!!

nextElement

E nextElement()
Returns the next element of this enumeration if this enumeration object has at least one more element to provide.
Returns:
the next element of this enumeration.
Throws:
NoSuchElementException - if no more elements exist.
다음 elements가 존재하지 않으면, Exception이 발생하네요 ㅋㅋ
이것 또한 Iterator의 next()와 같아 보이는군요..ㅋㅋ

Vector의 요소들을 출력하는 예제입니다.
for (Enumeration e = v.elements() ; e.hasMoreElements() ;) {
System.out.println(e.nextElement());

}
자 그럼, 본격적으로 차이점에 대해서 알아보도록 합시다.

Snap Shot
Enumeration과 Iterator의 가장 큰 차이점은 바로 Snap Shot입니다.
Enumeration은 Snap Shot을 지원합니다. 여기서 'fail-fast
'라는 개념이 나오는데요.
Iterator는 fail-fast 방식이라고 합니다.
그럼 과연 이것이 무엇일까요?? 좋은 아티클이 있어서 긁어왔습니다...-ㅅ-;;

Fail-Fast

자바 2 이전 버전에서 사용되던 Vector, Hashtable의 뷰 객체인 Enumeration은 fail-fast 방식이 아니었으나, 자바 2의 콜렉션 프레임워크에서 콜렉션 뷰인 Iterator, ListIterator 객체는 fail-fast 방식이라는 점이다.

콜렉션 뷰는 콜렉션 객체에 저장된 객체들에 대한 순차적 접근을 제공한다. 그러나, 뷰 객체인 Enumeration 또는 Iterator 객체를 얻고 나서 순차적 접근이 끝나기 전에 뷰 객체를 얻은 하부 콜렉션 객체에 변경이 일어날 경우, 순차적 접근에 실패하게 된다. 여기서 변경이라는 것은 콜렉션에 객체가 추가되거나 제거되는 것과 같이 콜렉션 구조의 변경이 일어나는 경우를 말한다.

이런 상황은 멀티쓰레드 구조와 이벤트 구동 모델에서 일어날 수 있으며, 개발자가 혼자 테스트할 경우 발견하기 어려운 문제이다. 따라서 정확한 이해와 예상이 필요하며, 이에 대한 대처 방안을 마련해야 한다.

하부 콜렉션 객체에 변경이 일어나 순차적 접근에 실패하면 Enumeration 객체는 실패를 무시하고 순차적 접근을 끝까지 제공한다. Iterator 객체는 하부 콜렉션 객체에 변경이 일어나 순차적 접근에 실패하면 ConcurrentModificationException 예외를 발생한다. 이처럼 순차적 접근에 실패하면 예외를 발생하도록 되어 있는 방식을 fail-fast라고 한다.

Iterator는 fail-fast 방식으로 하부 콜렉션에 변경이 발생했을 경우, 신속하고 결함이 없는 상태를 만들기 위해 Iterator의 탐색을 실패한 것으로 하여 예외를 발생하며, 이렇게 함으로써 안전하지 않을지도 모르는 행위를 수행하는 위험을 막는다. 왜냐하면 이러한 위험은 실행 중 불특정한 시간에 멋대로 결정되지 않은 행위를 할 가능성이 있기 때문에 안전하지 않다고 할 수 있기 때문이다.


멋집니다..-ㅅ-; 더이상 설명할 것이 없을 것 같은데요.
흐음.. 일단 정리를 해보면, iterator는 일종의 pointer가 있습니다.
예전에 LinkedList 개념을 그림으로 그려보았는데요.
그거랑 매우 비슷합니다.

사용자 삽입 이미지

포인터가 Array의 자기자신과 다음 객체를 가르키는 것이지요.
그래서 위에 아티클에 써져있는 것처럼 삭제나 수정시 참조무결성 원칙에 어긋나게되어서
에러가 발생하게 되는것입니다.

아티클에 SnapShot에 대한 좋은 내용도 있더군요.

스냅샷

이처럼 콜렉션 뷰 객체인 Iterator와 ListIterator 객체가 fail-fast 방식인 이유는 스냅샷에 대한 보장을 포함하고 있지 않기 때문이다. 즉, 콜렉션의 뷰인 Iterator 객체를 얻었을 때 그 순간의 상태를 따로 생성하지 않기 때문에, Iterator 객체에서 순차적으로 하부 콜렉션 객체를 접근할 때, 콜렉션 객체의 변경에 영향을 받게 된다.

따라서, 콜렉션 뷰인 Iterator를 통하여 순차적 접근을 행하는 도중에 하부 콜렉션 객체에 변경이 일어날 가능성이 있는 경우 스냅샷을 명시적으로 생성하는 것이 좋다. ArrayList를 이용한 스냅샷을 생성하는 간단한 방법은 다음과 같다.

public Iterator snapshotIterator(Collection collection) {
return new ArrayList(collection).iterator();
}

이렇게 스냅샷을 이용하면 스냅샷이 생성된 순간의 상태에 대하여 Iterator를 통한
콜렉션의 뷰를 생성하게 된다. 하부 콜렉션의 변경이 일어나도 스냅샷에는 반영되지
않으며 Iterator는 실패없이 실행된다.

콜렉션 계열 중에 ArrayList를 사용하는 이유는 다른 콜렉션 계열 클래스들보다 생성과순차적 접근이 빠르기 때문이다. ArrayList가 삽입과 삭제에 불리한 단점이 있으나
이 경우에는 삽입과 삭제가 없으므로 고려되지 않는다.


조금 더 쉽게 정리를 해보자면,
Enumeration은 객체를 복사해서 저장합니다. 그래서 삭제나 수정시 전체 콜렉션에는
큰 타격을 주지 않는다는 것이죠.

Synchronization

제가 원래 알고 있던 지식은 "Enumeration은 동기화를 지원한다." 였는데,
어디서 잘 못 주워들었나봅니다. -ㅛ-
아티클에 <<콜렉션 프레임워크의 클래스들은 동기화를 보장하고 있지 않다.>>
라고 써져있더라구요. 그래서 찾아보았습니다. 역시 API에서 Enumeration부분을 봐도,
동기화에 대해서 정의된 부분은 찾을 수 없더라구요.
하지만,  Vector와 ArrayList의 차이점을 공부한 글에서도 알 수 있을 듯이
Vector는 동기화가 아주 넘쳐납니다-ㅅ-; 그러한 사실은 이클립스에서 Vector.class를
가보면, synchronized로 도배가 되어있는 것을 볼 수 있답니다.

아티클에는,
<<멀티쓰레드에서 콜렉션 구현 객체에 동시에 추가, 삭제를 위한 접근이 행해진다면, 그러한 행위가 발생하는 부분에 동기화를 명시적으로 해주어야 한다.>>
라고 써져있더군요. 방법까지 친절하게 명시해주고 있었습니다.
요렇게-ㅅ-;

Collection c = Collections.synchronizedCollection(myCollection);

결론적으로, Enumeration과 Iterator 사이에서 뚜렷하게 차이를 보이는 점은
 "스냅샷을 지원하느냐 하지 않느냐" 라는 것 같습니다.
아티클에서는 "Fail-fast Iterator에 대한 멀티쓰레드 무결성 해결방법" 이라는 제목아래 글을 써내려가고 있는데요. 스냅샷과 동기화가 Iterator의 멀티쓰레드 무결성 해결방법으로 제시되었고, 그와 함께 예시 소스코드도 보여주고 있습니다. 그러니까 Iterator가 스냅샷을 지원하지 않아도, 스냅샷을 명시적으로 생성할 수 있다는 것입니다. 이런건 한번 소스코드를 작성해봐야 확실히 알 수 있겠네요. 일단 이번 포스팅은 여기서 마치고, 소스코딩을 야심차게 시작해보도록 하겠습니다.-ㅛ-; 아자 +ㅅ+ ㅋ
< 출처: http://happystory.tistory.com >
반응형
Vector or ArrayList -- which is better and why?

Sometimes Vector is better; sometimes ArrayList is better; sometimes you don't want to use either. I hope you weren't looking for an easy answer because the answer depends upon what you are doing. There are four factors to consider:

Vector가 더 나을 때도 있고, ArrayList가 더 나을 때도 있다. 또는, 둘 다 사용하길 원하지 않을 수도 있다. 나는 당신이 쉬운 답을 찾으려 하지 않기를 바란다. 왜냐하면 답은 당신이 하는 방식에 따라 달라질 테니까 말이다. 고려해야 할 다음 4가지 요인이 있다.

API
In The Java Programming Language (Addison-Wesley, June 2000) Ken Arnold, James Gosling, and David Holmes describe the Vector as an analog to the ArrayList. So, from an API perspective, the two classes are very similar. However, there are still some major differences between the two classes.

The Java Programming Language 에 Ken Arnold, James Gosling, and David Holmes는 Vector를 ArrayList의 상사형(서로 형이 같은)이라고 묘사한다. 그래서 API를 보면 두 개의 클래스는 매우 유사하다. 하지만 여전히 두 클래스 사이에는 중요한 차이점이 존재한다.

Synchronization
Vectors are synchronized. Any method that touches the Vector's contents is thread safe. ArrayList, on the other hand, is unsynchronized, making them, therefore, not thread safe. With that difference in mind, using synchronization will incur a performance hit. So if you don't need a thread-safe collection, use the ArrayList. Why pay the price of synchronization unnecessarily?

Vector는 동기화 되어있다. Vector의 내용에 접근하는 어떤 메소드는 thread safe1 하다. 반면에ArrayList는 동기화 되어있지 않다. 그러므로 ArrayList로 만들어진 것은 thread safe하지 않다. thread safe가 필요하지 않는데도 동기화를 사용하면, performance hit2 을 초래할 것이다. 만약 당신이 thread-safe 컬렉션을 필요로 하지 않는다면, ArrayList를 사용하라. 왜 불필요하게 synchronization의 비용을 지불하려 하는가?

Data growth
Internally, both the ArrayList and Vector hold onto their contents using an Array. You need to keep this fact in mind while using either in your programs. When you insert an element into an ArrayList or a Vector, the object will need to expand its internal array if it runs out of room. A Vector defaults to doubling the size of its array, while the ArrayList increases its array size by 50 percent. Depending on how you use these classes, you could end up taking a large performance hit while adding new elements. It's always best to set the object's initial capacity to the largest capacity that your program will need. By carefully setting the capacity, you can avoid paying the penalty needed to resize the internal array later. If you don't know how much data you'll have, but you do know the rate at which it grows, Vector does possess a slight advantage since you can set the increment value.

내부적으로 ArrayList와 Vector둘 다 Array를 사용하면서 그들의 컨텐츠를 유지한다. 당신은 당신의 프로그램에서 둘 중에 하나를 사용하는 동안에 이 사실을 명심할 필요가 있다. ArrayList 또는 Vector의 요소를 삽입할 때, 만약 그 용량을 다 써버린다면, object는 자신의 내부array를 확장시킬 필요가 있다. Vector는 자신의 array 크기의 배수만큼 초기화 한다. 반면에 ArrayList는 자신의 array 크기의 50%만큼 씩 증가시킨다. 당신이 이러한 클래스들을 사용하는데 의존한다면, 당신은 새로운 요소들을 추가하는 동안에 결국 대량의 performance hit을 초래할 것이다. 당신의 프로그램이 필요로 하는 최대의 용량만큼 객체의 초기화 용량을 정하는 것이 최선이다. 신중하게 용량을 셋팅해라. 그러면, 당신은 후에 내부 array를 resize하는데 필요한 요금 지불을 피할 있다. 만약 당신이 얼마나 데이터를 가지게 될지는 모른지만, 그것이 증가하는 비율을 안다면 , Vector는 당신이 증가값을 정할 수 있기 때문에 근소한 이점을 가진다.(아래 글 참조)

Usage patterns
Both the ArrayList and Vector are good for retrieving elements from a specific position in the container or for adding and removing elements from the end of the container. All of these operations can be performed in constant time -- O(1). However, adding and removing elements from any other position proves more expensive -- linear to be exact: O(n-i), where n is the number of elements and i is the index of the element added or removed. These operations are more expensive because you have to shift all elements at index i and higher over by one element. So what does this all mean?

ArrayList와 Vector 둘 다 컨테이너의 특정 위치에서 요소들을 되찾거나, 컨테이너의 끝에서 요소들을 더하거나, 삭제할때 유용하다. 이러한 모든 기능은 constant time-- O(1)(맨 아래 글을 참조하세요.)에서 수행될 수 있다. 하지만, 어떤 다른 자리에서 요소들을 더하거나 제거하는 것은 더 많은 비용을 발생시킨다. 정확하게 직선위에서..-ㅅ-?? n은 요소들의 수이고, i는 더해지거나 제거되는 요소의 인덱스다. 당신이 하나의 요소에 때문에 모든 요소들을 index i에 그 이상의 값으로 옮겨야만 하기때문에 이러한 operation들은 더 많은 비용이 든다. 그렇다면 이러한 것은 무엇을 의미하는가?

It means that if you want to index elements or add and remove elements at the end of the array, use either a Vector or an ArrayList. If you want to do anything else to the contents, go find yourself another container class. For example, the LinkedList can add or remove an element at any position in constant time -- O(1). However, indexing an element is a bit slower -- O(i) where i is the index of the element. Traversing an ArrayList is also easier since you can simply use an index instead of having to create an iterator. The LinkedList also creates an internal object for each element inserted. So you have to be aware of the extra garbage being created.

그것은 만약 당신이 요소들을 검색하거나, Array의 끝에서 요소들을 더하거나 제거하기를 원한다면 Vector나 ArrayList 둘 중에 하나를 사용하라는 것을 의미한다. 만약 당신이 그 밖의 무엇인가를 하길 원한다면, 스스로 또다른 콘테이너 클래스를 찾아라. 예를들어, LikedList는 constant time -- O(1) 에서 어디서든지 요소를 더하거나 제거할 수 있다. 하지만, 요소를 찾는 것은 조금 느리다. ArrayList을 왔다갔다 하는 것은 또한 당신이 iterator을 만들어야만 하는 대신에 인덱스를 더욱 간편하게 사용할 수 있기 때문에 더 쉽다. LinkedList는 또한 삽인된 각각의 요소에 대한 내부 객체를 만든다. 그래서 당신은 만들어지는 여분의 garbage에 대해서 알아야만 한다.

Finally, in "PRAXIS 41" from Practical Java (Addison-Wesley, Feb. 2000) Peter Haggar suggests that you use a plain old array in place of either Vector or ArrayList -- especially for performance-critical code. By using an array you can avoid synchronization, extra method calls, and suboptimal resizing. You just pay the cost of extra development time.

마지막으로 Practical Java "PRAXIS 41"에 Peter Haggar은 당신이 Vector 나 ArrayList 둘 중의 하나 대신에 명백한 old array를 사용할 것을 제안한다. -특히 비판적인 코드에서??? array를 사용함으로써, 당신은 동기화, 여분의 메소드를 부르는 것. 그리고 차선의 resizing을 피할 수 있다. 당신은 단지 여분의 개발시간비용만을 지불할 것이다.

어허...ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 해석하는데 무지 오래걸렸습니다..-ㅅ-
하지만 해놓고보니.. 뿌듯하군요.
그러니까, ArrayList와 Vector의 가장 큰 차이점으로 꼽을 수 있는 것은 "동기화"입니다.
하지만, 여러 문서를 찾아보니 ArrayList가 동기화되는 Vector보다 퍼포먼스가 좋다는
이야기가 많더라구요. 즉, 적절한 곳에 ArrayList를 동기화 해서 사용하는게 더 좋다는 의견이
많았습니다. 저는 아직 Vector를 한번 더 사용해 본적도 없고, 개발 경험이 거의 전무해서
잘 와닿지는 않고, 공감할래야 공감할 수도 없지만... 이해했다는 것에 만족해야겠습니다.
-ㅅ-... 화잇힝...ㅋㅋ

Vector - Data Growth 추가사항(API문서)
The Vector class implements a growable array of objects. Like an array, it contains components that can be accessed using an integer index. However, the size of a Vector can grow or shrink as needed to accommodate adding and removing items after the Vector has been created.

Vector 클래스는, 오브젝트의 가변장 배열을 구현합니다. 여기에는 배열과 같이, 정수 인덱스를 사용해 액세스 할 수 있는 요소가 격납되고 있습니다. 그러나, Vector 의 사이즈는 작성 후에 추가 및 삭제된 객체를 격납할 수 있듯이 필요에 따라서 늘리거나 줄이거나 할 수가 있습니다.

Each vector tries to optimize storage management by maintaining a capacity and a capacityIncrement. The capacity is always at least as large as the vector size; it is usually larger because as components are added to the vector, the vector's storage increases in chunks the size of capacityIncrement. An application can increase the capacity of a vector before inserting a large number of components; this reduces the amount of incremental reallocation.

각 Vector 는,capacity (용량)와 capacityIncrement (증가량)를 관리하는 것으로써 기억 관리를 최적화하려고 합니다.capacity 는 항상 Vector 의 요소수에 가까운 값이며, 통상은 요소수부터 커집니다. 이것은 Vector 에 요소가 더해질 때, Vector 의 기억 영역은 capacityIncrement 만 늘려지기 때문입니다. 많은 요소를 삽입하기 전에 애플리케이션으로 용량을 필요한 값으로 설정해 두면, 메모리의 재배분의 회수를 줄일 수가 있습니다.


Constant Time : TimeComplexity 단위의 하나로, 입력 n의 크기의 증감과 상관 없이 주어진 문제를 푸는데에 걸리는 시간(See DiscreteTime)이 동일한 것을 말하며, BigONotation으로는 O(1)이라고 표시합니다. 예를 들어 배열의 i번째 원소를 가져오는 연산은 배열의 크기 n과 무관하게 동일한 속도로 수행됩니다.
TimeComplexity : 시간 복잡도. Automaton으로 어떠한 문제를 풀기 위해 소모되는 이산시간(See DiscreteTime)의 크기를 말합니다.
Automaton : 물리적/논리적인 자동기계-ㅅ-.. 라는데... 그냥 컴퓨터로 이해하겠습니다.
BigONotation : 아.. 이건 진짜 모르겠습니다-ㅅ-;;;;;;;;;;;;;;;;;;;;;;; 살려주세요 ㅠ
출처 : http://jania.pe.kr/wiki/jwiki/moin.cgi
반응형
일단, LikedList에 대하여 살펴봐야겠군요.
1학년때 교재로 썼던 <<Absolute Java>>를 보면,
Liked Data Strutures라는 챕터가 있습니다.
아래는 Introduction에 있는 내용인데요. 야심차게 해석을..-ㅅ-;

A linked data structure consists of capsules of data, known as nodes, that are connected via things called links. These links can be viewed as arrows and thought of as ane-way passages from one node to another. The simplest kind of liked data structure consists of a single chain of nodes, each connected to the next by a link; this is known as a linked list.

linked data structure은 node라고 알려져 있는 data의 캡슐로 구성되어 있다.
이는 link라 불리우는 것을 매개로 하여 연결된다. 이러한 link는 하나의 node에서 다른 node로 가는 한방향의 통로로 간주 될 수 있다. (화살표로 표시된다.)
linked data structure의 가장 간단한 종류는 일련의 단일 node로 구성되어 있다.
각각은 다음 link까지 연결된다. 이러한 것을 linked list라 한다.

워... 큰일났다... 무지하게.. 난해하네요..-ㅅ-;
이걸 이해하려면.. 자료구조를 공부해야겠는데요...-_ㅠ

그러는 와중에.. 저의 구세주 zerry82님께서 등장하셨습니다.!!!
친절한 설명 감사드립니다 ^ ^
그림으로 표현해보면,

사용자 삽입 이미지

멋진 것이었군요+ㅁ+
그렇다면, ArrayList와 LinkedList의 차이점 쯤은 금방 알 수 있겠군요.

여러가지 문서가 있었는데.. '속도'부분에서 많은 논란이 있더라구요.
일단은 괜찮은 영어문서가 있길래.. 무턱대고 해석 들어갔습니다-ㅅ-;

There are two general-purpose List implementations in the Collection Framework, ArrayList and LinkedList, which of the two List implementations you use depends on your specific needs. If you need to support random access, without inserting or removing elements from any place to other than the end, then ArrayList offers you the optimal collection, the LinkedList class provides uniformly named methods to get, remove and insert an element at the beginning and end of the list.

콜렉션 프레임워크에는 ArrayListLinkedList를 병용하는 List implementations1 이 있다.
이것은 당신이 특정한 필요에 의해서 사용하는 두가지 List implementations이다.
만약 당신이 끝 이외에 어디에서든지 요소를 제거하거나 삽입하는 것 없이 자유롭게 접근하는 것이 필요하다면, 그땐, ArrayList가 당신에게 최적의 콜렉션을 제공한다.
LinkedList
클래스는 리스트의 처음과 끝에 요소을 제거하거나 삽입하는 메소드를 제공한다.

Each ArrayList instance has a capacity. The capacity is the size of the array used to store the elements in the list. It is always at least as large as the list size. As elements are added an ArrayList, its capacity grows automatically. The details of the growth policy are not specified beyond the fact that adding an element has constant amortized time cost.

각각의 ArrayList 인스턴스는 용량을 가지고 있다. 용량은 리스트에서 요소들을 저장하기 위해 사용되는 array 크기이고, 언제나 적어도 리스트의 크기만큼 커야한다. 요소들이 ArrayList 추가될 , 그것의 용량은 자동으로 증가한다. 증가방법에 대한 자세한 사항은 지속적으로 상환되는 시간비용을 가진 요소를 더하는 사실 이외에는 기술되어지지 않는다.

An application can increase the capacity of an ArrayList instance before adding a large number of elements using the ensureCapacity operation. This may reduce the amount of incremental reallocation.

에플리케이션은 ensureCapacity 기능을 사용하여 많은 수의 요소들을 추가하기 전에ArrayList 인스턴스의 용량을 증가시킬 있다.
이러한 것은 많은량의 재배치 증가를 감소시킨다.

그렇지요!! 정리를 해보자면,

more..


출력은 다음과 같습니다.
                    [뎅뎅이, 붉은돼지, zerry, Real Study, java, jsp, html/css]
                    첫번째 : 뎅뎅이
                    [Real Study, 뎅뎅이, 붉은돼지, zerry, java, jsp, html/css]
                    [Real Study, 뎅뎅이, 붉은돼지, zerry, java, jsp]
                    첫번째 : Real Study

소스코드를 보면 아시겠지만, addFirst()라던지.. removeLast()라던지
LinkedList에는 ArrayList의 메소드 이외에 추가, 삭제하는 메소드가 추가되어있습니다.
(API 참조 !!)
제가 작성한 코드는 간단한 코드라 아직까지는 그 중요성이 와닿지 않지만,
이러한 것들이 모이다보면..-ㅅ-;
LinkedList가 ArrayList보다 훨씬 빠른성능을 보여주겠지요??
(스택,큐,양방향큐 를 만들 때 편리하게 사용된다는데요. 이건 뭔지 잘 몰라서-ㅅ-;)

< 출처: http://happystory.tistory.com >
반응형
Enum..일단 에자일 책에서 처음 본 신개념입니다.
일단 정의부터 살펴보도록 하지요.

Enum : 정해진 리스트에서만 값을 선택할 수 있다.
            컴파일러가 지원하는 primitive타입이 아니라
            컴퍼일러가 클래스로 변환시킨다.
            불필요한 상수값 부여를 막을 수 있다.
            코드가 간결해진다.

책에 나와있는 소스코드를 보면,
               
enum Grade { A, B, C, D, F };

요런식으로 선언해줍니다.
Grade enum은 다섯 개의 가능한 값을 가지고, 각각은 객체 인스턴스를 표현합니다.

               public static void main(String[] args){

                     System.out.println(Grade.A);

               }

이런식으로 써줄 수 있지요.

이렇듯, Enum은 기본적으로 static입니다.
Enum클래스를 상속받은 static final class로서,
내부요소들은 해당클래스의 인스턴스를 가지게 되지요.

이걸 증명해줄만한, 재밌는 내용이 있어서 가져와봤습니다.1

enum으로 작성한 것을 jad라는 역컴파일러를 통해서 풀어준 코드입니다.

사용자 삽입 이미지

↓ jad로 변환

사용자 삽입 이미지

신기하지요?+ㅅ+ 스터디시간에 발표했던 내용인데, 복습할 겸.. 다시한번 올려봅니다.ㅎ

그때 알고 넘어간 Enum에 관한 사항은 여기까지입니다.
정말 더 깊이 공부하고 싶은 마음은 굴뚝 같으나..-ㅅ-;
아직까지는 너무 어렵네요.
너무 신개념이라 API를봐도 메소드의 정확한 사용법을
알아내기가 쉽지가 않네요.

일단 저 멀리 안드로메다 수준까지 정리를 해 놓으신 zerry님의 글을
트랙백으로 남겨놓겠습니다.-ㅅ-;
두고두고,, 천천히 봐야지.. 한번에 보려니까 머리가 너무 아프네여 ㅠ ㅎ
반응형
아래 그림에서 볼 수 있듯이 Collection과 Map은 따로 존재합니다.
저는 지금까지 Collection 인터페이스를 Map, Set, List가 구현하는 줄 알았는데,
아니더라구요. 이러한 사실은 API를 봐도 알 수 있습니다.

사용자 삽입 이미지

                        List : 순서(index)를 알고 있는 컬렉션.
                        Set : 중복된 것을 허용하지 않는 컬렉션.
                        Map : 키-값 기능 제공.

이러한 개념은 실제 코드를 작성해봐야 알 수 있지요?
에자일 자바책 9장에 이러한 내용이 깊~~게 언급되므로
그때 좀 더 자세히 알아보도록 하겠습니다.-ㅅ-ㅎ


에자일자바 5장을 보면 Collections.sort()가 등장합니다.
Collection이라는 인터페이스 외에, Collections라는 유틸리스 클래스가 또 존재하는데요.
API를 살펴보니, Collection에는 메소드가 거의 없드라구요.
sort()또한 보이지 않았습니다.
sort()는 Collections에 static으로 선언이 되어있습니다.
아래 Collections API를 보면,
사용자 삽입 이미지
-ㅅ-;;;
난해합니다. -_ㅠ
일단 필드와 메소드를 보고 유추해본 결과,
Collections클래스는 List, Set, Map에서 공통으로 사용되는 메소드를 모은
유틸리티 클래스라고 볼 수 있지 않을까 생각됩니다.

static으로 선언되어 있기 때문에
Collections.sort()로 편하게 사용할 수 있습니다.

다음으로 정말 중요한 iterator()입니다.
iterator()는 Iterator타입의 객체를 리턴하는데요.
이 객체를 사용해서 콜렉션 안에 있는 모든 엘리먼트를 하나하나 검색할 수 있습니다.
참 바보스럽게도, 전 여지껏 Iterator라는 인터페이스 없이
iterator()메소드만 Collection, Set, List에 각각 존재하는 줄 알았습니다.-ㅅ-;
하지만, 아니더라구요 ㅎ API를 보면 알 수 있습니다.
Collection, Set, List 인터페이스는 Iterable인터페이스를 구현합니다.
Iterable인터페이스를 보면,
사용자 삽입 이미지
iterator()메소드가 있는것을 볼 수 있습니다.
보면, 메소드가 Iterator형인 것이 확인되지요??
짧은 영어로 해석을 해보면,
<T>타입 요소의 집합을 건네주는 iterator을 리턴한다.-ㅅ-;

그니까.. 제너릭으로 한정한 타입의 요소의 집합으로 iterator을 리턴한다는 뜻이겠지요?

그렇다면.. 이번에는 Iterator인터페이스를 뒤져보도록 하겠습니다.
다음과 같은 메소드들이 존재합니다.

사용자 삽입 이미지

boolean hasNext()
Returns true if the iteration has more elements. (In other words, returns true if next would return an element rather than throwing an exception.)
Returns:
true if the iterator has more elements.
=> 만약 다음에 불러올 요소들이 더 있다면 true을 리턴한다.
    다시말해서, 만약 next가 엘리먼트를 리턴하면 true를 리턴한다.

직역을 하면 도저히 이해하기가 힘들 것 같아서, 엄청난 의역을 해야만 했습니다.ㅎ
제가 이해한 바로는 next()메소드를 통해서 콜렉션에 저장된 다음 요소가 리턴되면
hasNext()는 true를 리턴하고, 리턴될 다음 요소가 없어도
exception은 발생하지 않는다는 것이지요..?ㅋㅋㅋ

그렇다면, 다음으로 next()메소드를 살펴봅시다. 이것을 보면 더욱 이해가 잘됩니다.
E next()
Returns the next element in the iteration. Calling this method repeatedly until the hasNext() method returns false will return each element in the underlying collection exactly once.
Returns:
the next element in the iteration.
Throws:
NoSuchElementException - iteration has no more elements.
=> 반복하면서 다음 요소를 리턴한다.
    이 메소드는 hasNext()메소드가 false를 리턴할 때까지
    기초가되는 콜렉션에 있는 각각의 요소를 정확하게 한번씩
    반복적으로 리턴할 것이다.

영어는 역시 어렵습니다..-_ㅠ
위에 hasNext()메소드에 대한 설명을 보충해주고 있지요??
이래서 API는 보고나면 재밌습니다... 볼때는 괴롭지만 ㅎㅎ

마지막으로 remove()가 있는데, 이것은 Collection안에 있는 모든 요소를 검색하여
어떤 조건을 만족하지 않는 요소를 제거할 때 사용하는데요.
좋은 소스코드가 있습니다.

                        static void filter(Collection c) {
                            Iterator i = c.iterator();
                            while (i.hasNext()) {
                            if (!cond(i.next())) i.remove();
                            }
                        }

                    hasNext()가 true일 경우에만 if문을 반복하는 것입니다.
                    제시하는 조건을 만족하지 않을경우,
                    remove()메소드를 사용해서 해당하는 요소를 제거해줍니다.

iterator() 이전에 Enumeration이 쓰였다고 하는데요.
iterator()와 Enumeration의 차이점은 다음에 포스팅 하도록 하겠습니다.
차이점을 공부하다보면, iterator()에 대해서도 더 자세히 알 수 있겠지요?
출처 : http://happystory.tistory.com
반응형
향상된 for 루프는 J2SE 5.0에서 새로운 언어기능으로 소개된 것으로,
Iterator를 생성하거나, 카운트 변수의 시작과 끝을 계산할 필요 없이
콜렉션을 반복 할 수 있게 해줍니다.

일단, 기본적인 Array에서 향상된 for loop의 사용을 비교해도록 하겠습니다.

more..


이전에 제가 작성한 코드입니다. 이것을 향상된 for문으로 더 간단하게 만들어보겠습니다.

more..



무지 신기하지요??+ㅛ+
자 이제, 이게 어떻게 변하는 가를 설명하도록 하겠습니다.
기존에 for문은 카운트 변수의 시작과 끝을 계산해야했습니다.
즉, row = 0 이고, row가 rowLand(지뢰의 세로 길이 - scanner을 통해서 받은 값)보다
작을 때까지만.. row을 계속 늘려줘라-ㅅ-; 뭐 요런 것인데요.
향상된 for문을 보면
for(String[] row : landMine)

이런식으로 간단하게 줄여졌습니다.
이는, "landMine이라는 이름의 배열의 엘리멘트(row) 반복"으로 읽을 수 있습니다.
자동으로 엘리멘트의 길이를 측정한다고 볼 수 있겠네요..

향상된 for loop의 기본 syntax을 다음과 같이 나타낼 수 있습니다.
   	EnhancedForStatement:
for ( Type Identifier : Expression )
Statement   
Expression must be an instance of a new interface
    called java.lang.Iterable, or an array.

Expression java.lang.Iterable 또는 array라 일컬어지는
새로운 인터페이스의 인스턴스야만 한다.


향상된 for loop에도 단점이 있다고 합니다.

So when should you use the for-each loop? Any time you can. It really beautifies your code. Unfortunately, you cannot use it everywhere. Consider, for example, the expurgate method. The program needs access to the iterator in order to remove the current element. The for-each loop hides the iterator, so you cannot call remove. Therefore, the for-each loop is not usable for filtering. Similarly it is not usable for loops where you need to replace elements in a list or array as you traverse it. Finally, it is not usable for loops that must iterate over multiple collections in parallel.

당신은 for-each(향상된 for) 루프를 언제 사용하여야 하는가?
언제든지 가능하다. 그러면 당신의 코드를 정말 아름답게 할 것 이다..-ㅅ-;;
허나.. 불행하게도, 당신은 for-each를 어디에서든지 사용할 수 없다.
예들들어, 삭제 메소드를 고려해보라. 프로그램은 현재 element을 제거하기 위해
iterator에 접근하는 것을 필요로 한다. for-each loop는 iterator를 숨긴다.
그래서 당신은 remove을 부를 수 없다.
그러므로, for-each loop는 필터링에 쓰기에는 편리하지 않다.
유사하게, 당신은 콜렉션을 순회하면서,
list나 array에 element들을 바꾸는 것을 필요로 할 때, 적합하지 않다.
마지막으로, 동시에 복합적인 콜렉션을 되풀이하는 것은 적합하지 않다.

아 ! 그리고,
지뢰찾기 자료 입력 부분도 for-each문으로 바꿔보고자 했는데,
잘되지가 않아서.. 많은 블로그를 돌아다니면서 예제를 찾아보았는데요.
자료를 입력해주는 부분에는
향상된 for 루프를 사용하지 않고, 일반 for loop를 사용하드라구요.
좀 더 조사해봐야 할 것 같네요.-ㅛ-

자자자자자 그렇다면,
ArrayList와 같은 콜렉션에서 향상된 for loop(for-each)는
어떻게 쓰여질까요??(제너릭과 iterator(), hasNext()등등의 사용..-ㅅ-)
그것은...............
콜렉션에 대해 좀 더 공부하는 시간을 가진 후에,
그때 다시 포스팅하도록 하겠습니다. +ㅅ+ ㅋㅋ
위에 해석한 단점도.. 그때서야 비로소 이해할 수 있을 것 같네요-ㅛ-;

< 출처: http://happystory.tistory.com >
반응형

(1) [2006-12-27] 특집5부_ARM과 파워PC에 기반한 임베디드 프로그래밍 최적화 기법
(2) [2006-12-27] 특집4부_성능 이슈 해결을 위한 닷넷 프로그래밍 최적화 기법
(3) [2006-12-27] 특집3부_리팩토링을 이용한 자바 성능 최적화 기법
(4) [2006-12-26] 특집2부_OOP적 개발을 위한 C++ 프로그래밍 최적화 기법
(5) [2006-12-26] 특집1부_개발 환경의 변화와 대응하는 프로그래밍 최적화의 재발견

출처: http://www.dbguide.net/know/know101003.jsp?IDX=1164&K=TITLE&K2=REGID&V=¨?¨????%20?????¡?&catenum=14

분야별 특성에 맞춘 Programming Optimization



프로그래밍 최적화. 코드 몇 줄을 줄이고 실행 속도를 높이기 위해 머리를 쥐어짜던 시절이 있었다. 이미 추억 저편으로 멀어진 그 기억 속에서는 그런 것이 바로 프로그래밍 최적화였다. 그럼 하드웨어의 성능이 예전의 슈퍼컴퓨터와 맞먹을 정도로 높아진 지금, 프로그래밍 최적화가 대체 무슨 의미를 가질 수 있을까? 프로그램의 속도가 조금 빠르거나 느린 정도라면 이제 거의 체감할 수 없는 상황이 되지 않았는가. 성능이 아주 떨어지지만 않는다면 이제 약간의 실행 속도의 차이는 무의미해진 지 오래다. 이처럼 시대가 변했다고는 하지만 분명히 프로그래밍을 위해 갖춰야 할 최적화 항목들은 여전히 존재한다. 코드의 수를 줄이는 것이 아니더라도 OOP적 개발을 위한 기법들이 필요하고, 보다 개선된 프로그램을 위한 리팩토링도 필요하다. 심지어 예전처럼 속도를 따져야 하는 분야도 있다. 바로 임베디드 분야이다. 이번 특집에서는 각 개발 분야에서 중요하게 다뤄져야할 최적화 기법들에 대해 알아본다.


기획·정리
| 정희용 기자 flytgr@imaso.co.kr


리팩토링을 이용한 자바 성능 최적화 기법


허광남 | GS홈쇼핑 EC정보팀 과장


리팩토링, 복잡다단해지는 현대의 소프트웨어 개발에서 이 단어는 점점 중요한 위치를 차지해 가고 있다. 이제 리팩토링은 진정한 개발자의 덕목 중에 하나라고 단언할 수 있을 정도다. 리팩토링을 한다는 것은 개선에 대한 의지가 있음을 뜻하고, 좀 더 나은 코드, 구조, 프로세스를 지향한다는 의미가 된다. 리팩토링으로 소프트웨어의 성능을 직접적으로 높이지는 못 한다. 하지만 코드의 가독성을 증대시켜, 생각하는 프로그래머들의 머릿속 성능을 높여준다. 3부에서는 리팩토링 방법들에 대해 알아본다.


햄버거나 커피 등을 살 때, 또는 백화점이나 편의점에서 물건을 살 때, 우리는 1회용 물건을 쓰는 것에 대한 세금을 낸다. 1회용 물건을 쓰면 환경이 그만큼 빨리 피폐해지기 때문이란다. 그게 사실인지 아닌지 모르겠지만, 내 돈이 나가는 것은 용납이 안 된다. 1회용품의 편리함. 그 반대급부로 만들어지는 쓰레기 처리에 따른 비용을 지불한다고 하는데, 영 맘에 안 든다.

혹시 프로그램을 짤 때도 1회용 프로그램을 짠다는 생각을 해본 적이 있는가? 그런데 우리는 1회용 프로그램을 짜도 세금을 내지 않는다. 다행일까? 1회용 프로그램이 환경 자원을 소모시키지는 않는다. 다만 1회용 프로그램은 쓰레기를 양산한다. 그때 그때 필요한대로 찍어낸 프로그램은 수많은 중복코드를 양산해낸다. 재활용하지 않는 습관 탓에 시스템이라는 환경이 무거워지고 손이 많이 가도록 바뀌는 것이다.

재활용성은 객체지향 프로그램의 핵심원리 중의 하나이다. 재활용성을 높인다는 것은 찍어낼 때 사용하는 템플릿을 얘기하는 것이 아니다. 오히려 업무나 기능을 제어 가능한 곳에 집약시켜서 관리할 수 있도록 시스템 전체의 청결한 상태를 유지하는 것이다. 이리저리 산재된 중복 코드를 정리하는 것이 핵심이다. 이 때 필요한 기술이 리팩토링이다. 이쯤 얘기하면 리팩토링은 정리 정돈에 비견된다. 군대에서 총기수입을 하는 것과도 같고 집에서 설거지를 하는 것과도 같다.




리팩토링이란



Refactoring (Re + Factor + ing) 영어 단어를 요소별로 나눠보면 요소들을 재구성한다는 뉘앙스를 받을 수 있다. 이는 마틴 파울러의 책에서 비롯된 단어인데, 책에 있는 리팩토링의 정의를 보면 다음과 같다.

“리팩토링은 외부 동작을 바꾸지 않으면서 내부 구조를 개선하는 방법으로, 소프트웨어 시스템을 변경하는 프로세스이다.” 마틴 파울러, 리팩토링, P10. 대청출판사 책에 이어서 나오는 내용은 버그가 끼어들지 않도록 주의하면서 코드를 작성한 후에 더 나은 디자인으로 개선하는 방법이라고 한다. 디자인을 먼저 한 후 코드를 만드는 것이 아니라 일단 돌아가는 코드를 작성하고, 그 후에 그 코드가 더 좋은 구성을 갖도록 바꾼다는 것이다. 우리들의 코딩 관행을 돌아보면, 일단 돌아가는 프로그램을 짠다. 그리고? 끝이다. 그 다음으로 넘어간다. 정리? 남은 사람이 알아서 할 것이다. 남은 사람이 자기 밖에 없다면? 날 잡아서 정리하거나, 회사 옮긴다.




리팩토링을 하는 이유



야심찬 초급 개발자가 자주하는 것 중에 하나가 이전 소스에 대한 비평이다. “도대체 어떻게 이렇게 소스를 짤 수 있지. 발로 짜도 이것보다는 낫겠네. 왜 이렇게 if else가 많은 거야. 이 소스 이해할 시간 있으면 차라리 다시 짜고 만다.” 그래서, 다시 짠다. 그리고 오픈하면 이것 저것 버그 리포트와 요구사항이 들어온다. 이것 저것 예외 처리를 해주다 보면 내가 짠 코드지만 보기 싫어진다. 어느 정도 서비스가 안정적으로 돌아가도록 소스를 수정해 놓으니, 이런, 전에 내가 막 뭐라고 했던 이전 개발자의 소스와 별반 차이가 없다.

“제길, 다음 후임이 누가 될지는 몰라도 내 욕 무진장 하겠군.” 문서라도 잘 주면 모르겠지만, 처음 개발할 때 보고했던 문서 그대로다. 요구사항과 수정을 통해서 변경된 내용을 문서에 업데이트하질 못했다. “할 시간이 있어야지.” SM(System Maint enance)분야에서는 거의 이렇게 사는 것이 보통이다.

이전 사람이 만든 소스에서 버릴 것은 거의 없다. 정리가 안 되서 몇 달간 목욕 못한 모습일 뿐이지, 처리할 수 있는 모든 경우의 수는 그 안에 다 가지고 있다. 이런 코드를 새로 짠다는 것은 그 모든 경우의 수를 처음부터 다시 감수하겠다는 의미가 된다.
이전 소스를 씻기고 다듬는 것이 소스 수정을 위한 필수 과정이다. 정리하지 않고 계속해서 소스를 추가해 가는 일은 운동하지 않고 계속해서 먹어대는 것과 같이 시스템을 비만상태로 만들어간다. 움직임이 점차 둔해질 것이다. 정리 안 된 방처럼 발 디딜 팀이 없는 소스가 될 것이다.

무엇인가 소스의 변경이 필요할 때, 기능 추가나 삭제, 수정 작업이 일어날 때 소스의 리팩토링은 포장이사처럼 편하게 작업하도록 도와준다. 리팩토링은 소스의 중복된 부분을 모듈화 시켜준다. 모듈화는 입출력이 명확하기 때문에 이식성을 높여준다. 중복을 제거한다는 것은 시스템의 칼로리를 빼는 것과 같다. 시스템의 복잡도, 즉 코드를 읽는 사람의 머리가 열받는 정도를 낮춰준다. 물론 그렇다 해도 이사 자체는 귀찮은 일이다.




리팩토링을 위한 도구



리팩토링은 그로 인해 영향 받는 프로그램의 수가 적을 대에만 수작업으로 작업해야 한다. 사실 리팩토링을 수작업으로 한다는 것은 추천하지 않는다. 좋은 개발 환경이 있는데 사서 고생할 필요가 없는 탓이다. 리팩토링을 위한 좋은 툴이 많이 나왔다. 일단 통합개발환경(IDE, Integrated Development Environment)을 준비한다. 요즘의 자바 개발 시 많이 사용되는 IDE는 기본적으로 리팩토링을 지원한다.

리팩토링과 함께 진행되어야 할 JUnit 테스트케이스 자동 생성도 같이 지원되고 있다. 리팩토링 작업을 할 경우 여러 줄의 코드들이 수정된다. 이때 영향을 받는 프로그램들을 모두 불러내서 수작업으로 수정할 경우 리팩토링에 대한 공수가 많이 필요한 탓에 감히 리팩토링에 대한 엄두를 낼 수 없다. 하지만 요즘 통합 개발 환경을 지원하는 개발 도구들은 변경 받는 파일들의 목록과 변경 전 후의 코드 비교, 자동 변경 기능을 지원한다. 덕분에 리팩토링에 드는 수고가 전혀 수고로 생각되지 않을 정도다.




리팩토링 진행 방법



리팩토링하는 이유와 리팩토링 도구까지 알아보았으니 이제 리팩토링 방법에 대해 알아볼 차례다. 주저리 주저리 방법들을 늘어놓을 수 도 있겠지만 개발자는 코드로 얘기한다. 바로 이클립스에서 리팩토링을 사용하는 방법을 설명하도록 하자.


<리스트 1> 리팩토링 샘플
1 public void deleteArticle(Connection conn, int seq) throws SQLException {
2 if (conn == null)
3 return;
4
5 // db에서 삭제 - 삭제 테이블로 이동
6 PreparedStatement pstmt = null;
7 pstmt = conn.prepareStatement(QUERY_MOVE);
8 pstmt.setInt(1, seq);
9 pstmt.executeUpdate();
10
11 pstmt.close();
12
13 pstmt = conn.prepareStatement(QUERY_DELETE);
14 pstmt.setInt(1, seq);
15 pstmt.executeUpdate();
16
17 pstmt.close();
18
19 // memo 삭제 생략
20
21 }


리팩토링에 대한 간단한 예를 들기 위해서 <리스트 1>을 보며 설명하겠다. 7~11번 줄의 코드가 13~17번 줄의 코드와 유사한 것을 알 수 있다. 중복이 계속되는 것은 일정한 패턴을 갖고 있는데 중복이 심해지면 패턴 변경에 따른 공수가 많이 필요하므로 소스의 유연성이 떨어지게 된다. 때문에 반복되는 패턴을 메소드화 시켜서 쉽게 코드를 읽을 수 있도록 한다.

<화면 1> 반복되는 부분, 메소드 추출의 대상


<화면 2> 메소드 추출(Extract Method)

이클립스에서 패턴부분을 선택하고, 오른쪽 버튼을 눌러 콘텍스트 메뉴를 열면 중간 위치에 [Refactor…]라는 메뉴가 보인다. 확장 메뉴에서 [Extract Method…]를 선택하면 <화면 2>와 같은 다이얼로그 창이 뜬다. ‘doQuery’라고 메소드명을 입력한 뒤에 파라미터들을 확인한다.

화면 아래쪽의 버튼 중 [Preview]를 클릭하면 <화면 3>과 같이 미리보기 창으로 바뀐다. 이때 화면에 표시되는 정보들이 기가 막힌다. 리팩토링을 통해서 변경되는 소스의 비교와 상단에는 이 리팩토링에 영향을 받는 소스들과 메소드명까지 친절하게 알려준다. 게다가 이클립스가 모두 다 자동으로 바꿔준다.


<화면 3> 리팩토링 결과 미리보기


<화면 4>에서는 다이얼로그에서 만든 doQuery() 메소드의 내용을 볼 수 있다. 소스 비교란의 맨 오른쪽에 있는 네모는 소스 전체에서 변경이 일어난 부분을 표시한 것이다.


<화면 4> 리팩토링으로만들어진 메소드

비교가 끝났다면 [OK] 버튼을 클릭해서 리팩토링을 실행한다. 소스 리팩토링을 마친 뒤에 doQuery() 메소드를 보면, <화면 5>처럼 파라미터가 Connection conn, int seq 두 개임을 알 수 있는데, 여기에 하나가 더 필요하다. 바로 쿼리 부분인데, 이것을 파라미터로 받아야 비로로 doQuery()가 공용으로 쓰일 수 있게 된다.

<화면 5> 리팩토링으로 만들어진 약간 아쉬운 메소드

QUERY_MOVE라는 상수를 파라미터로 대치한다. 이 상수에 마우스 오른쪽 버튼을 클릭한 뒤에 [Refactor]-[Introduce Para meter] 메뉴를 실행시키면 <화면 6>과 같은 다이얼로그 창을 볼 수 있다. 새로운 파라미터 이름을 ‘query’로 정하고 우측의 [up] 버튼을 클릭해서 파라미터의 위치를 조정한다. 파라미터의 변경은 메소드의 모습인 시그니처(signature)를 변경하는 것이다.


마찬가지로 [Preview] 버튼을 클릭하면 <화면 7>과 같이 리팩토링 전후의 소스를 비교할 수 있다.

<그림 7>에서 [OK] 버튼을 클릭해서 만들어진 doQuery() 메소드는 반복되는 쿼리 실행 부분을 메소드 추출(Extract Met hod)과 파라미터로 빼기(Introduce Parameter) 리팩토링을 이용해서 만든 것이다. <리스트 2>는 그것을 이용해서 바뀐 소스의 모습이다.

<그림 7> 파라미터로 만들기 적용하기 전 미리보기


<리스트 2> QUERY_DELETE 부분 리팩토링 과정
1 public void deleteArticle(Connection conn, int seq) throws SQLException {
2 if (conn == null)
3 return;
4
5 // db에서 삭제 - 삭제 테이블로 이동
6 PreparedStatement pstmt;
7 doQuery(conn, QUERY_MOVE, seq);
8 doQuery(conn, QUERY_DELETE, seq);
9
10 pstmt = conn.prepareStatement(QUERY_DELETE);
11 pstmt.setInt(1, seq);
12 pstmt.executeUpdate();
13
14 pstmt.close();
15
16 // memo 삭제 생략
17
18 }

19 private void doQuery(Connection conn, String query, int seq) throws SQLException {
20 PreparedStatement pstmt = null;
21 pstmt = conn.prepareStatement(query);
22 pstmt.setInt(1, seq);
23 pstmt.executeUpdate();
24
25 pstmt.close();
26 }


<리스트 2>는 아직 변경 중인 샘플코드이다. 앞서 만든 doQuery() 메소드를 이용해서 쿼리만 다른 것을 보내면 된다. 필자가 추가한 8번 줄은 10~14번 줄과 동일한 기능을 수행하게 된다. 코드를 정리하면 다음과 같이 된다.


<리스트 3> QUERY_DELETE 부분 리팩토링 후
1 public void deleteArticle(Connection conn, int seq) throws SQLException {
2 if (conn == null)
3 return;
4
5 // db에서 삭제 - 삭제 테이블로 이동
6 doQuery(conn, QUERY_MOVE, seq);
7 doQuery(conn, QUERY_DELETE, seq);
8 // memo 삭제 생략
9
10 }


하단의 구문이 지워지면서 이 deleteArticle() 메소드 내의 PreparedStatement pstmt 선언은 불필요하기 때문에 삭제했다. 처음 보았던 소스에서 많이 정리되었다. 정리를 하고 보니 deleteArticle() 메소드를 호출하는 곳에서 비슷한 기능을 하는 부분을 볼 수 있다.


<리스트 4> 리팩토링 적용 범위 확대
1 // password 확인
2 if (confirmPassword.equals(MASTER_PASSWORD)
3 || confirmPassword.equals(article.getPassword())) {
4 deleteArticle(conn, seq);
5 deleteFiles(conn, seq);
6 } else {
7 resourceName = "/jsp/error.jsp";
8 throw new Exception(CommonUtil.k2a("잘못된 비밀번호"))
9 }

10 public void deleteFiles(Connection conn, int seq) throws SQLException {
11 if (seq == 0){
12 return;
13 }
14
15 // file db에서 삭제 - sts 값 0 로 변경
16 PreparedStatement pstmt = conn.prepareStat ement(QUERY_DEL_SEQ_FILE);
17 pstmt.setInt(1, seq)
18 pstmt.executeUpdate();
19
20 pstmt.close();
21
22 // file 삭제 생략
23 }


<리스트 4>의 16~20번 줄을 보면 앞서 추출한 메소드 doQuery()로 변경할 수 있을 것 같다. 그럼 코드는 <리스트 5>와 같이 수정될 것이다.


<리스트 5> QUERY_DEL_SEQ_FILE 부분 리팩토링 과정

10 public void deleteFiles(Connection conn, int seq) throws SQLException {
11 if (seq == 0){
12 return;
13 }
14
15 // file db에서 삭제 - sts 값 0 로 변경
16 doQuery(conn, QUERY_DEL_SEQ_FILE, seq);
17
18 // file 삭제 생략
19 }


이렇게 정리하고 난 후에 다시 전체적인 코드를 생각해보면 두 개의 메소드가 불필요하다 생각이 든다. 즉 <리스트 6>과 같이 deleteArticles()와 deleteFiles() 메소드를 지우고 바로 doQuery() 를 호출하도록 바꿀 수 있을 것이다. <리스트 6>은 리팩토링을 통해 최종적으로 정리된 소스이다.


<리스트 6> 리팩토링 적용으로 개선된 코드
1 // password 확인
2 if (confirmPassword.equals(MASTER_PASSWORD)
3 || confirmPassword.equals(article.getPassword())) {
4 // db에서 삭제 - 삭제 테이블로 이동
5 doQuery(conn, QUERY_MOVE, seq);
6 doQuery(conn, QUERY_DELETE, seq);
7 // memo 삭제 생략
8 // file db에서 삭제 - sts 값 0 로 변경
9 doQuery(conn, QUERY_DEL_SEQ_FILE, seq);
10 // file 삭제 생략
11 } else {
12 resourceName = "/jsp/error.jsp";
13 throw new Exception(CommonUtil.k2a("잘못된 비밀번호"));
14 }


앞에서 보았던 소스의 if else 구문과 비교해보면 doQuery() 라는 공통으로 사용할 수 있는 메소드와 5줄이 늘어났지만 deleteArticle(), deleteFiles() 두 개의 메소드가 사라졌다. 이전 소스와 비교해보면 메소드 구성은 <화면 8>과 같이 변경되는 것을 알 수 있다.

추가된 메소드는 +화살표, 제거된 메소드는 화살표로 표시되고 변경된 메소드는 그냥 검은 화살표로 표시된다. 화면 아래쪽에 표시되는 소스 비교하는 곳을 보면 더욱 명확하게 알 수 있다.

지금까지 샘플 소스의 구조를 개선하면서 두 가지 리팩토링 기법에 대해 알아보았다. 이 외에도 많은 기법들이 리팩토링 책에 소개되어있고, 이클립스에도 더 많은 리팩토링 기능이 지원된다.


<그림 8> 리팩토링 전 후 메소드 비교



리팩토링 경험담



필자는 이 글을 쓰고 있는 지금 큰 프로젝트를 진행하고 있다. 6년간 하나도 버려지지 않고 운영되면서 그때그때 패치된 페이지를 스프링 프레임워크에 맞춰서 바꾸는 작업이다. 그런데, 작업을 하는 동안 필자가 간과한 것이 있었다. 그렇게 복잡하게 얽히고설킨 페이지를 스프링 프레임워크의 새로운 바닥부터 하나씩 쌓아 올린 것이다. 기존에 운영하고 있는 소스에서 하나씩 뜯어서 새로운 토양으로 옮겨심기를 한 것이다. 이것은 재개발에 가까운 것이었고, 굉장히 많은 시간이 필요했다. 만약 옮겨야 할 소스를 기존의 토양 위에서 조금씩 리팩토링한 후에 옮겼다면 오히려 많은 시간을 절약할 수 있었을 것이다.

실수했다고 생각하는 부분은 다음과 같다. 우선적으로 모든 기능을 다 옮겨올 때까지 신규 페이지는 아직 미완성이다. 하지만 기존의 페이지 내에서 리팩토링을 한다고 하면 이미 모든 기능과 데이터를 다 갖고 있는 상태이다. 다른 파트에서 데이터가 필요하다고 할 때에도 현재 갖고 있는 데이터에서 데이터를 뽑아내서 보다 빨리 전달할 수 있을 것이다.

두 번째로 시간의 압박이다. 기존의 페이지는 언제든지 답이 나온다. 하지만 신규페이지는 모든 테스트를 마칠 때까지 계속 기다리라고 얘기해야만 한다. 바닥부터 모든 것들에 대해서 테스트를 만들어야 하는 탓에 더 많은 테스트 코드들이 필요하다. 여기에도 만만치 않은 시간이 투입된다.

세 번째는 애플리케이션에 대한 자신감이 떨어진다는데 있다. 맥가이버도 아닌데 시간에 쫓기면서 개발할 경우 만들어진 소스는 분명히 수많은 버그를 품고 있을 가능성이 높다. 그 값이 절대 정확하다고 이야기하기 힘들다. 하지만 리팩토링을 통해서 내부로부터 개혁해 나갈 경우 빠진 것 없이 소스를 재구성할 수 있기 때문에 안정된 기반에서 작업할 수 있을 것이다.

전산의 불문율 가운데 유명한 것이 하나 있다. ‘잘 돌아가는 것은 손대지 마라.’ 칼퇴근을 위해서 절대 절명으로 필요한 말이다. 이렇게 관리되는 소프트웨어의 품질은 논하기 힘들다. 그냥 먹고 살기 위한 프로그램과 그것을 관리하는 직장인이 되어버리게 된다. 반면에 리팩토링의 기본 사상은 개선을 위한 노력이다. 막무가내 개선이 아니라 현명한 개선을 위한 방법을 제시하고 있고, 친구격인 테스트 케이스가 그 안전장치가 되어 준다. 한 순간의 품질이 아닌 지속적인 소프트웨어의 건강을 생각한다면 꾸준히 리팩토링으로 손질할 필요가 있다. 그것이 끝없이 변하는 웹 애플리케이션과 같은 소프트웨어일 경우는 더욱 그렇다.
개선을 위한 작은 몸짓에 진정한 프로그래머가 되고 싶은 독자들을 초대한다.


참고 자료
1. 리팩토링, 마틴파울러, 윤성준,조재박 역, 대청, 2002년3월
2. 패턴을 활용한 리팩터링, 죠슈아 케리에브스키, 윤성준,조상민 역, 인사이트,
2006년7월


리팩토링 관련 사이트

1. http://www.refactoring.com/
Refactoring Home Page

2. http://xper.org/wiki/xp/ReFactoring
김창준 님의 Refactoring에 관한 정보

3. http://c2.com/cgi/wiki?CodeSmell
Code Smell

4. http://xper.org/wiki/xp/CodeSmell
Code Smell 번역

5. http://www.okjsp.pe.kr/lecture/ide/eclipse/refactor/eclipse_refactoring.html
Eclipse의 refactoring기능

6. http://www.okjsp.pe.kr/lecture/ide/eclipse/eclipse_install.html
Eclipse 시작하기


 



제공 : DB포탈사이트 DBguide.net

반응형

자바 SE 6의 성능 모니터링 및 진단 (한글)

자바 최신 버전에서 향상된 성능과 모니터링의 장점을 활용하기

 

 

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

 




난이도 : 중급

Cathy Kegley, 소프트웨어 엔지니어, IBM
Greg Roberts, 스태프 소프트웨어 엔지니어, IBM

2008 년 3 월 11 일

자바 SE 6(Java™ Platform, Standard Edition 6)은 성능에 초점을 맞춰 응용 프로그램을 모니터링, 감시하고 공통적인 문제를 진단하기 위해 확장된 도구들을 제공합니다. 이 기사에서는 자바 SE 플랫폼의 모니터링과 관리에 대한 기본적인 사항들을 소개하고, 자바 SE 6에서 보강된 사항들에 대한 상세한 정보를 제공합니다.

자바 SE 6은 응용 프로그램을 관리, 모니터링하고 일반적인 문제점을 진단하는 확장된 도구들을
제공한다. 개선된 사항은 다음과 같다.

  • 모니터링 및 관리(Monitoring and management) API 향상
  • 개선된 그래픽 모니터링 도구인 JConsole에 대한 공식 지원
  • 강화된 자바 가상 머신(JVM) 도구

이 기사는 자바 SE 플랫폼의 모니터링과 관리에 대한 기본적인 사항들을 소개하고, 최신 버전에서
보강된 사항들에 대해 상세한 정보를 제공한다. 또한 자바 SE 6 플랫폼에서 사용할 수 있는 진단
및 문제 해결 도구들에 대해 설명한다.

이 기사를 통해 이전 버전의 자바 SE에서 소개된 모니터링 및 관리 기능을 확실히 이해하게
 될 것이다.  참고자료를 통해 자세한 배경 정보를 볼 수 있다.

모니터링 및 관리 API

자바 SE 5는 java.lang.management 패키지에 플랫폼 MBean 또는 MXBean이라는 9개의
MBean을 정의한다(참고자료). 각 MXBean은 JVM의 단일 기능 영역을 감싸고 있다.
자바 SE 5부터 JVM은 플랫폼 MBean 서버라고 부르는 MBean 서버를 내장하고 있다.
MBean은 이 저장소 안에 존재하고 관리된다. 표 1에 자바 플랫폼에 포함된 9개의
MXBean을 요약했다.


표 1. 플랫폼 MBeans
관리 인터페이스 관리되는 자원
ClassLoadingMXBean 클래스 로더
CompilationMXBean 컴파일러
MemoryMXBean 메모리
ThreadMXBean 스레드
RuntimeMXBean 런타임
OperatingSystemMXBean 운영체제
GarbageCollectorMXBean 가비지 컬렉터
MemoryManagerMXBean 메모리 관리자
MemoryPoolMXBean 메모리 풀

어떤 응용 프로그램이라도 원하는 빈의 인스턴스를 얻고 절적한 메서드를 호출해 JVM이 제공하는
 플랫폼 MBean을 사용할 수 있다. 로컬 또는 원격 JVM의 동작을 모니터링하고 정보를 얻기 위해
MXBean을 사용할 수 있다.

플랫폼 MBean은 적재된 클래스 개수, JVM 가동시간, 메모리 소모량, 실행 중인 스레드 개수,
스레드 경쟁에 대한 통계 등의 정보에 대해 접근을 제공한다.

JVM 리소스를 모니터링하고 관리하는 두 가지 방법이 있다.

  • MXBean 인터페이스를 통한 직접 접근
  • MBeanServer 인터페이스를 통한 간접 접근

MXBean 인터페이스를 통한 직접 접근

정적인 팩토리 메서드를 통해 MXBean 인스턴스를 얻어 로컬에서 실행 중인 JVM의
MXBean 인터페이스에 직접 접근할 수 있다. ManagementFactory 클래스는 MXBean을 얻을 수
있는 정적인 팩토리 메서드를 제공한다. Listing 1은 이 팩토리를 통해 RuntimeMXBean을 얻어
표준 속성 중 하나인 VmVendor의 값을 얻는 방법을 보여준다.


Listing 1. MXBean에 직접 접근하기
                
RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();

// Get the standard attribute "VmVendor"
String vendor = mxbean.getVmVendor();

MBeanServer 인터페이스를 통한 간접 접근

플랫폼 MBeanServer 인터페이스는 원격 JVM에 연결하고 그 플랫폼에서 실행 중인 MXBean에
접근하기 위해 MXBeanServerConnection을 사용한다. ManagementFactory 클래스의 getPlatformMBeanServer 메서드를 사용해 플랫폼 MBean 서버에 접근할 수 있다. Listing 2는
원격에서 실행 중인 JVM에서 RuntimeMXBean을 얻고, VmVendor 속성의 값을 얻는 방법을
보여준다.


Listing 2. MXBean에 간접 접근하기
                
MBeanServerConnection serverConn;

try {
   //connect to a remote VM using JMX RMI
JMXServiceURL url = new JMXServiceURL( "service:jmx:rmi:///jndi/rmi://<addr>");

   JMXConnector jmxConnector = JMXConnectorFactory.connect(url);

   serverConn = jmxConnector.getMBeanServerConnection();

   ObjectName objName = new 
   ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME);

   // Get standard attribute "VmVendor"
   String vendor = 
   (String) serverConn.getAttribute(objName, "VmVendor");

} catch (...) { }

참고자료에서 MXBean과 the java.lang.management API에 대한 더 자세한 정보를 볼 수 있다.

자바 SE 6에서 보강된 API

자바 SE 5는 락(lock)과 대기 조건(wait codition)에 대한 프레임워크를 제공하는 java.util.concurrent.locks 패키지를 도입했다. 이 프레임워크는 자바의 내장된 동기화
지원과는 별도로 더욱 융통성 있는 락 사용을 가능케 해 주었다.

자바 SE 6은 java.lang.management 패키지에 java.util.concurrent.locks에 대한 지원을
추가했다. 여기에는 락에 대한 정보를 제공하는 새로운 클래스와 ThreadInfo, ThreadMXBean, OperatingSystemMXBean에 대한 보강이 포함되었다.

자바 SE 6은 새로운 클래스 두 개를 도입했다.

  • LockInfo는 락에 대한 정보를 갖고 있다.
  • MonitorInfoLockInfo를 상속해 객체 모니터 락에 대한 정보를 갖고 있다.

ThreadInfo 클래스는 새로운 객체들을 사용하기 위해 새 메서드 세 가지를 도입했다.

  • getLockInfo()는 주어진 스레드가 차단 대기중인 LockInfo 객체를 반환한다.

  • getLockedMonitors()는 주어진 스레드에 의해 현재 락된 MonitorInfo 객체를 반환한다.

  • getLockedSynchronizers()는 주어진 스레드에 의해 현재 락된 소유 가능한 동기화

객체(ownable synchronizer)를 표현하는 LockInfo 객체를 반환한다.

ThreadMXBean.getThreadInfo 메서드는 자바 SE 5에서는 스레드가 획득 대기중이거나
진입 차단된 차단된 객체 모니터에 대해서만 보고했지만, 자바 SE 6부터는 획득 대기중인 스레드의 AbstractOwnableSynchronizer를 보고하도록 보강되었다.

ThreadMXBean 인터페이스도 메서드 네 개를 추가했다.

  • isObjectMonitorUsageSupported()는 가상 머신이 객체 모니터에 대한 모니터링을

지원하는지 여부를 검사한다.

  • isSynchronizerUsageSupported()는 소유 가능한 동기화 객체의 사용 모니터링을

지원하는지 여부를 검사한다.

  • findDeadlockedThreads()는 데드락된 스레드 ID의 배열을 반환한다. 데드락된 스레드는 다른 스레드가 객체 모니터나 동기화 객체에 진입하는 것을 차단하고 있는 스레드다.

  • dumpAllThreads()는 모든 살아있는 스레드에 대한 스택 트레이스와 동기화 정보를 반환한다.

마지막으로, OperatingSystemMXBean 인터페이스에 최근 수 분 동안 시스템의 평균 부하를 반환하는 getSystemLoadAverage()가 추가되었다.

프로그래밍 수준 지원과 더불어, 자바 SE 6은 문제를 감지하고 JVM의 자원 사용량을 모니터링하기
위한 몇 가지 진단 및 문제 해결 도구를 제공한다. 다음 두 절에서는 사용할 수 있는 진단 도구들에
설명하고 사용 예를 보여준다.






자바 모니터링 및 관리 콘솔(JConsole)

자바 SE 6은 자바 SE 5부터 도입된 모니터링 및 관리 콘솔인 JConsole에 대한 공식 지원을 포함한다. JConsole을 통해 실행 중에 다양한 JVM 통계를 모티터링할 수 있다. 이 도구는 데드락, 락 경합,
메모리 누수, 순환 스레드 등의 증상을 감지하는 데 유용하다. 이 도구는 로컬이나 원격 JVM에
연결하여 다음과 같은 정보를 모니터링할 수 있다.

  • 스레드 상태(연관된 락을 포함한)
  • 메모리 사용량
  • 가비지 컬렉션
  • 런타임 정보
  • JVM 정보

다음 절에서 자바 SE 6에서 JConsole의 보강된 부분들을 설명한다. 참고자료에서 JConsole을
시작하고 사용하는 방법에 대한 추가 정보를 볼 수 있다.

Attach API 지원

자바 SE 6부터 JConsole은 새로운 Attach API를 구현하고 있다. 이 com.sun.tools.attachcom.sun.tools.attach.spi 두 개의 패키지로 구성되어 있으며, 대상 가상 머신에 동적으로
부착되어 대상 JVM에서 에이전트를 실행하는 응용 프로그램을 구현할 수 있다.

예전에는 JConsole로 모니터링하려면 응용 프로그램을 -Dcom.sun.management.jmxremote
옵션을 사용하여 시작해야 했지만, 더 이상 그 옵션을 사용할 필요가 없다. 동적인 부착을 통해 JConsole은 Attach API를 지원하는 어떤 응용 프로그램이라도 지원할 수 있게 되었다. JConsole은
 시작하면서 호환 응용 프로그램을 자동으로 감지한다.

보강된 UI와 MBean 표현

자바 SE 6에서는 JConsole이 Windows® 운영체제나 GNOME 데스크톱 등의 실행 중인 플랫폼과
 비슷한 룩앤필을 갖도록 수정했다. 이 기사에서 이후부터 볼 스크린샷은 윈도우 XP에서 찍은
것으로 이전 버전과 달라진 UI 기능들을 보여준다.

일단 시작해 응용 프로그램과 연결되면, JConsole은 각각 다른 JVM 자원을 표현하는 6개의
 탭으로 구성된 화면을 제공한다.

  • Overview
  • Memory
  • Threads
  • Classes
  • VM Summary
  • MBeans

Overview 탭은 상호 연관된 메모리 사용량, 스레드, 클래스, CPU 사용량에 대한 정보를 그래프
 형식으로 표시한다. Overview 탭은 예전에는 여러 개의 탭을 전환해야 했던 관련된 정보들을
한 페이지에 표시한다. 그림 1은 예제 응용 프로그램의 Overview 탭을 보여준다.


그림 1. JConsole의 Overview 탭
JConsole의 Overview 탭

Overview 탭은 VM 자원 사용 정보에 대한 4개의 그래프와 결과를 보고 싶은 시간 범위를 바꾸기
위한 선택 목록을 표시한다. 첫 번째 힙 메모리 사용량 그래프는 시간대별로 사용된 힙 메모리의
양을 메가바이트 단위로 표시한다. 이 그래프는 메모리 누수를 감지하는 데 유용하다.
응용 프로그램에 메모리 누수가 있다면 힙 메모리 사용량은 시간이 지남에 따라 꾸준히 늘어난다.

스레드 그래프는 시간대 별로 살아있는 스레드 개수를, 클래스 그래프는 적재된 클래스 개수를
표시한다. CPU 사용량 차트는 응용 프로그램 생명 주기의 다양한 지점에서의 CPU 사용량을
표시한다.

그림 2의 VM Summary 탭은 자바 SE 6에 새로 추가된 것이다. 여기에는 총 가동시간, 스레드 정보,
적재된 클래스 개수, 메모리 통계, 가비지 컬렉션, 운영체제 정보를 포함한 JVM에 대한 자세한
정보를 제공한다.


그림 2. JConsole의 VM Summary 탭
JConsole의 VM Summary 탭

MBeans 탭은 MBean들의 동작과 속성에 더 쉽게 접근할 수 있도록 개선되었다. 플랫폼에 등록된
모든 MBean의 정보를 표시한다. 모든 플랫폼 MBean은 이 탭을 통해 접근할 수 있다. 왼쪽에 현재
실행중인 모든 MBean을 트리 구조로 표시한다. MBean을 선택하면 MBeanInfo와 설명자가
오른쪽에 표시된다(그림 3).


그림 3. JConsole의 MBean 탭
JConsole의 MBean 탭

Attributes 노드를 선택하면 MBean의 모든 속성을 표시한다(그림 4).


그림 4. MBean Attributes
MBean 속성

오른쪽에 표시된 속성과 값은 앞에서 설명한 java.lang.management 패키지의 ThreadMXBean
API를 통해 접근할 수 있다는 점을 주목하자. 나열된 속성 값을 더블클릭하면 추가 정보를 얻을 수
있다. 굵게 표시된 속성값은 확장할 수 있다. 예를 들어, AllThreadIds 값을 더블클릭하면 22개
스레드의 스레드 ID를 모두 표시한다(그림 5).


그림 5. 확장된 속성 값
확장된 속성 값

쓰기 가능한 속성은 파란색으로 표시되는데, 클릭한 다음 새 값을 입력해 편집할 수 있다. 예를 들어,
그림 5의 JConsole 화면에서 ThreadContentionMonitoringAvailable 속성을 이 방식으로
편집할 수 있다.

왼쪽 트리 구조에서 Operations 노드를 선택하면 MBean과 연관된 동작들을 표시한다. MBean
동작은 오른쪽에 버튼으로 표시되는데, 클릭하면 지정한 메서드를 호출한다. 그림 6은
ThreadMXBean의 가용한 동작들을 보여준다.


그림 6. MBean Operations
MBean 동작

핫스팟 진단 MBean

자바 SE 6부터 JConsole은 핫스팟 진단(HotSpot Diagnostic) MBean을 지원한다. 이 MBean은
즉석(on-the-spot) 동작을 수행하기 위해 이번 버전에 도입되었다. 이 API를 통해 사용자는 실행
중에 힙을 덤프하거나 다른 VM 옵션을 설정할 수 있다. MBean 탭에서 com.sun.management
노드를 확장하고 HotSpotDiagnostic을 선택하면 핫스팟 진단 MBean에 접근할 수 있다.
그림 7에서 핫스팟 진단 MBean에서 가용한 메서드들을 볼 수 있다.


Figure 7. 핫스팟 진단 MBean
핫스팟 진단 MBean

JConsole 플러그인 지원

자바 SE 6부터 JConsole은 플러그인을 지원한다. JConsole과 함께 실행할 수 있는 고유한
플러그인을 만들 수 있다. 예를 들어, 응용 프로그램에 특화된 MBean에 접근해 고유의 모니터링
 동작을 수행하는 탭을 JConsole의 메인 화면에 추가할 수 있다.

커스텀 JConsole 플러그인을 만들려면 com.sun.tools.jconsole.JConsolePlugin 추상
클래스를 상속해야 한다. JConsole 화면에 제대로 나타나게 하려면 메서드 두 개를 구현해야 한다.

  • newSwingWorker()는 플러그인의 GUI를 갱신하는 SwingWorker 객체를 반환한다.
  • getTabs()는 JConsole 창에 추가될 탭의 맵을 반환한다.

JConsole은 서비스-공급자 메커니즘을 사용해 모든 플러그인 클래스를 감지하고 적재한다.
 이 때문에, 플러그인 클래스는 META-INF/services/com.sun.tools.jconsole.JConsolePlugin
파일을 포함하는 JAR 파일로 제공해야 한다. 이 파일은 한 줄에 하나씩 나열된 플러그인 클래스의
완전한 이름 목록을 포함해야 한다. 아래 명령으로 JConsole을 실행하면 새로운 플러그인
클래스를 JConsole 화면에 불러온다.

jconsole -pluginpath plugin_path
            

이 명령에서, plugin_path는 JConsole 플러그인들이 들어있는 디렉터리나 JAR 파일의 위치다.
여러 개의 경로를 지정할 수 있다.

자바 SE 6에는 JTop이라는 JConsole 플러그인이 예제로 포함되어 있다. JTop은 현재
 응용 프로그램 내에서 실행중인 스레드의 CPU 사용량을 보여준다. JConsole을 JTop과
 함께 실행하려면 아래 명령을 실행하면 된다.

jconsole -pluginpath JAVA_HOME/demo/management/JTop/JTop.jar

그림 8은 JTop 탭이 선택된 JConsole을 보여준다. 왼쪽 컬럼은 실행 중인 스레드의 이름을
표시한다. 각 스레드에 대해 CPU 사용량과 스레드 상태가 표시된다. 통계가 변하면 화면이
자동으로 갱신된다. JTop 플러그인은 CPU 점유율이 높은 스레드를 식별하는 데 유용하다.


그림 8. JConsole의 JTop 플러그인
JConsole의 JTop 플러그인





모니터링 및 문제 해결 도구

자바 SE 6은 JConsole 외에도 몇 가지 다른 명령행 도구를 포함한다. 이 진단 도구들은 응용
프로그램을 특수한 모드로 실행할 필요 없이 아무 응용 프로그램에나 부착할 수 있다.
응용 프로그램이 의도한 대로 동작하는지를 확인하기 위한 정보를 얻을 수 있다. 이 도구들은
실험적이며 이후에 나올 자바 SE 버전에서는 지원되지 않을 수 있음을 주의하자.

모니터링 도구

자바 SE 6은 JVM의 성능 통계를 모니터링하는 데 유용한 명령행 유틸리티들을 포함하고 있다(표 2).


표 2. 모니터링 도구
도구 설명
jps JVM 프로세스 상태 도구
jstat JVM 통계 모니터링 도구
jstatd JVM jstat 데몬

jps 유틸리티는 대상 시스템의 현재 사용자에 대한 가상 머신을 나열한다. 이 유틸리티는
일반적인 자바 실행기 대신, JNI Invocation API를 통해 시작한 VM 환경에 유용하다.
이러한 환경에서는 프로세스 목록에서 자바 프로세스를 식별하는 것이 쉽지만은 않다.
jps 도구는 이 문제를 완화한다.

다음 예제는 jps 사용 예를 보여준다. 단순히 명령행에서 jps를 입력하면, 사용자가 접근
권한을 가진 가상 머신 목록과 프로세스 ID를 나열한다(Listing 3).


Listing 3. jps 유틸리티 사용하기
                
$ jps
16217 MyApplication
16342 jps

jstat 유틸리티는 JVM에 내장된 도구를 사용해 실행 중인 응용 프로그램의 성능과 자원 소모에
 대한 정보를 제공한다. 이 툴은 힙 크기 및 가비지 컬렉션과 연관된 성능 이슈를 진단하는 데
유용하다.

jstatd 데몬은 JVM 생성과 종료를 모니터링하고 원격 모니터링 도구가 로컬 호스트에 실행 중인
 JVM에 부착할 수 있도록 해주는 원격 메서드 호출(Remote Method Invocation, RMI) 서버
애플리케이션이다. 예를 들어, 이 데몬을 통해 jps 유틸리티는 원격 시스템의 프로세스 목록도
나열할 수 있다.

참고자료에서 이 도구들 각각에 대한 추가 문서와 사용 예를 볼 수 있다.

문제 해결 도구

자바 SE 6은 응용 프로그램이 예상치 못한 동작을 할 때 정밀한 처방을 내릴 수 있도록 도와주는
몇 가지 문제 해결 도구들도 포함하고 있다(표 3).


표 3. 문제 해결 도구
도구 설명
jinfo 설정 정보
jhat 힙 덤프 열람기
jmap 메모리 맵
jsadebugd 서비스 가능 여부 진단 에이전트 디버그 데몬
jstack 스택 트레이스

jinfo 명령행 유틸리티는 실행 중인 자바 프로세스의 설정 정보나 충돌 덤프를 추출하고, 가상
머신을 시작할 때 사용한 시스템 프로퍼티나 명령행 플래그를 출력한다.

jhat 도구는 힙의 스냅샷에서 객체 위상을 열람할 수 있는 편리한 수단을 제공한다. 힙 분석 도구
(Heap Analysis Tool, HAT)를 대체하기 위해 자바 SE 6에서 도입된 이 도구는 메모리 누수를
감지하는 데 유용하다.

jmap 명령행 유틸리는 실행 중인 VM이나 코어(core) 파일에 대한 메모리 관련 통계를 출력한다. jsadebugd 데몬을 사용하면 원격 기계의 프로세스나 코어 파일을 조회할 수 있다. jmap 도구는 OutOfMemoryError를 야기할 수 있는 finalizer의 과도한 사용을 진단하는 데 유용하다.

jsadebugd(Serviceability Agent Debug Daemon)은 자바 프로세스나 코어 파일에 부착되어
디버그 서버처럼 동작한다. 이 유틸리티는 현재 솔라리스 OS와 Linux®에서만 사용할 수 있다.
자바 RMI를 사용해 jstack, jmap, jinfo 같은 원격 클라이언트를 이 서버에 부착할 수 있다.

jstack 명령행 유틸리티는 지정한 프로세스나 코어 파일에 부착되어 VM의 내부 스레드를
포함한 가상 머신에 부착된 모든 스레드의 스택 트레이스를 출력하며, 선택적으로 네이티브
스택 프레임도 출력할 수 있다. 이 도구는 jsadebugd 데몬을 통해 원격 기계의 프로세스나
코어 파일을 조회할 수 있다. jstack 도구는 데드락을 진단하는 데 유용하다.

참고자료에서 이 도구들 각각에 대한 추가 문서와 사용 예를 볼 수 있다.






결론

자바 6 플랫폼은 자바 응용 프로그램의 성능과 메모리 문제를 효과적으로 식별하고 진단하는 것을
 돕기 위해 몇 가지 VM 기구, 관리 API, 그리고 JDK 도구들을 강화했다. 이 기사는 자바 SE의
모니터링 및 관리 프레임워크에서 향상된 부분들을 설명하고, 개발자들이 사용할 수 있는 명령행
 진단 유틸리티에 대해 다루었다.

자바 응용 프로그램의 평균 속도는 시간이 지남에 따라 꾸준히 증가하고 있다. 자바 SE 6이
나오면서 자바의 성능은 C나 C++에 비견할 정도가 되었다. 많은 경우에 자바 코드는 눈에
띌 정도로 빨라졌다. 그리고 여기에서 소개한 도구들을 사용해 더 나은 성능 최적화를 이룰 수
있을 것이다. 시도해보자. 예전에는 결코 알 수 없었던, 응용 프로그램의 성능을 향상시킬 수
있는 지점을 발견하게 될 것이라고 장담한다.



참고자료

교육

Caroline Gough, developerWorks, 2006년 4월): MXBean에 대한 더 자세한 정보

썬 개발자 네트워크, 2006년 8월): 자바 SE에서 공통적인 성능 문제 진단하기

이 API를 먼저 살펴보라.

소개한 모니터링 및 문제 해결 도구들에 대한 문서와 예제


토론


필자소개

Cathy Kegley는 IBM의 Lotus Expeditor 클라이언트 팀의 소프트웨어 엔지니어다.


Greg Roberts는 IBM의 Lotus Expeditor 클라이언트 개발 팀의 스태프 소프트웨어 엔지니어다.

반응형
Generic (제너릭)
 
 
What ?
 
제너릭은 컬렉션(자료구조),
 
즉 쉽게 말해서 객체들을 저장(수집)하는
 
구조적인 성격을 보강하기 위해 제공되는 것.
 
 

0. GENERIC

다들 아시겠지만, Generic의 사전적인 의미는 다음과 같습니다.


 1.     (생물) 속의 , (성질) 속에 특이한

 2.     일반적인, 포괄적인

 3.     (수,인칭,시제) 총칭적인



1. generic 필요성

Java 에서는 객체들을 담아 편하게 관리하기 위해  Collection 을 제공한다. 이 Collection 의 대부분이

어떤 객체를 담을지 모르기 때문에 모든 자바 객체들의 base 객체 (최상위 객체)인 Object로

저장되어 설계하도록 설계되어 있습니다.

문제는 이 Collection 에 Element로 어떤 Type을 받아 들임이 좋을수도 있지만

서로 다른 Type이 하나의 Collection 에 섞여 들어 가는것이 문제 JDK 5.0 에 와서 제너릭이 포함되면서

이제는 실행하기 전에 컴파일 단계에서 특정 Collection 에 객체 타입을 명시하여

지정된 객체가 아니면 절대 저장이 불가능하게 할수 있습니다.

2. 제너릭의 타입

제너릭 타입은 < > 사이에 컴파일할 당시 사용될 객체자료형만 선언 해주면 객체를 저장할 때

선언된 제너릭 타입으로만 저장된다.

API 에서는 전달되는 객체가 현 객체 내에서 하나의 자료형(Type)으로 쓰일 때 <T> 로 유도하고 있으며

전달되는 객체가 현 객체 내에서 하나의 요소(Element)로 자리를 잡을때는 <E> 로 , 그리고 전달되는 객체가

현 객체 내에서 Key 값으로 사용될 때는 <K>로,

만약 전달되는 객체가 현 객체 내에서 Value 값으로 사용될 때는 <V> 로 표현된다.

3. 사용자 정의 제네릭 클래스  


앞에서 언급한 자료형을 제너릭으로 set 하고 print 해주는 간단한 사용자 정의 클래스를 만들어 보겠습니다.

/*
* FILE NAME  : Generic.java
* DATE   : 2007. 8. 5
* Written By  : yangck20@naver.com
*
*/
package ckbin.array;
public class Generic<T> {
 T[] v;
 
// Generic 타입으로 Set
 public void set(T[] n) {
  v = n;
 }
 
// T[] 원소 출력
 public void print() {
  for(T s : v)
   System.out.println(s);
 }

}




 

 
사용자_Class명 <적용할_제너릭 타입> 변수명;   // 선언
 
 
변수명 = new 사용자_Class명<적용할_제너릭 타입>( ) ; // 생성
 



4. 제너릭 타입 사용하기


사용자 정의형으로 만든 제너릭 클래스의 사용

 




위에서 생성한 사용자_Class를 사용하여 생성할때는 다음과 같이 생성자를 만들수 있습니다.

Generic<String> Gen = new Generic<String> ();

 위에서 생성한 사용자_Class 를 사용하는 Main Class 를 만들어 보겠습니다.

 /*
* FILE NAME  : GenericMain.java
* DATE   : 2007. 8. 5
* Written By  : yangck20@naver.com
*
*/
package ckbin.array;
import ckbin.array.Generic;
public class GenericMain {
 
 public static void main(String ar[]) {
 
  Generic<String> Gen = new Generic<String> ();
 
  String[] StrArr = {"디워","정말","보고파"};
 
  Gen.set(StrArr);
  Gen.print();

 
  Generic<Integer> Gen2 = new Generic<Integer> ();
 
  Integer[] IntArr = {2,0,0};
 
  Gen2.set(IntArr);
  Gen2.print();

 
 }
}
 
== 출력 결과 =============================================================================
디워
정말
보고파
2
0
0
 

 



















위 GenericMain 소스를 보면 Generic 클래스는 String 배열과

Integer 배열을 받아 출력해주는 아주 간단한 소스지만 타입을

달리주어 객체에 담은 배열들을 출력해주는것이 이상적입니다.

HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("1", new Integer(1));
map.put("2", 2);            // auto boxing
 
Integer i = map.get("1");   // 따로 캐스팅이 필요 없다.
int j = map.get("2");       // auto unboxing


이렇게 별도의 캐스팅 없이 처리할 수 있다는 점에서 좋아졌네요..

+ Recent posts