2012년 3월 26일 월요일

[DeveloperWorks] BM 스타일의 자바: 모니터링 및 문제 해결 (한글)


여러 해 동안, 자바 런타임의 IBM 구현을 위한 모니터링 및 문제해결 도구 호스트를 개발했다. 이 도구를 가지고 IBM 지원 팀, 자바 애플리케이션 개발자 및 생산 운영진은 자바 배포 시 발생하는 문제를 진단하고 해결한다.
이 글에서는 최신 IBM Java에 구현된 세 가지 주요 장치들인 트레이스 엔진, 덤프 엔진 및 DTFJ 툴링 API에 대해 논하기로 한다. 이 세 가지 장치들은 문제의 근본 요인을 해결하는 데에 유용하게 쓰인다.
트레이스 정보는 소프트웨어 문제 해결 시 강력한 도구이다. 기능 에러, 트레이스 상태, 성능 문제와 같은 문제들을 효과적으로 조사하는 데 이 정보를 사용한다. 교육적인 관점에서는 프로그램 흐름을 이해하는 데 상당히 유용하다.
IBM 개발 팀이 자바 가상 머신 (JVM) 결함을 진단하는 데 도움이 되도록, SDK 1.2.2에서 자바 런타임의 IBM구현에 있어 IBM 트레이스 엔진 개념을 도입했다. 이 트레이스 엔진은 낮은 오버헤드, 높은 성능 및 가상 머신 자체에 대한 구성 가능 한 추적 메커니즘을 제공하기 위해 고안되었다. SDK 1.2.2에 이은 후속 릴리스에 관해서는 이전 릴리스에 비해 기능이 상당히 향상되었고 IBM SDK의 현재 버전은 부가적으로 필요한 지침 없이 런타임 상에서 배포되는 JVM, 자바 클래스 라이브러리(JCL) 및 임의의 자바 애플리케이션 코드에 관한 트레이스 데이터를 포착하는 고성능 엔진 기능을 가진다.

본 시리즈에 대하여

IBM 스타일의 자바 시리즈에서는 최신 IBM의 자바 플랫폼 구현에 대해 알아보기로 한다. 자바 플랫폼 5.0에 어떤 부분이 향상되었는지를 살펴보고 IBM의 새로운 릴리스에 구현된 중요한 기능들을 사용하는 방법을 알아본다.
본 글의 저자와 개인적으로도 연락할 수 있다. 시리즈에 대한 의견이 있을 경우 시리즈 책임자인 Chris Bailey에게 연락하기 바란다. 최신 IBM 릴리스도 다운로드 할 수 있다. (참고자료)
다음과 같은 다양한 메커니즘을 통해 트레이스 엔진을 설정, 제어한다.
  • -Xtrace 명령행 옵션 사용
  • 트레이스 속성 파일을 사용
  • com.ibm.jvm.Trace API를 통한 자바 코드를 동적으로 사용
  • 트레이스 트리거 이벤트를 사용
  • C-기반 JVM RAS 인터페이스 (JVMRI)를 이용한 외부 에이전트 사용
트레이스를 제어하는 일차적인 방법은 명령행 상에 -Xtrace 옵션을 사용하는 것이다. 또한 옵션 자료가 길거나 복잡할 경우, 옵션 트레이스 속성 파일을 사용한다.
-Xtrace 옵션은 트레이스가 stderr, 내부 버퍼 또는 2진 파일 가운데 한 곳에 기록되는 여부, 메소드 트레이스, JVM 트레이스, 또는 두 트레이스의 설정 여부, 추적되는 트레이스 포인트 및 트리거 이벤트 시 트레이스 포인트 선택 또는 덤프에 관련된 변환이 필요한지 여부를 결정하는 데 사용되는 토큰 시리즈 또는 토큰 값 쌍으로 이루어져 있다.
IBM 트레이스 도구를 사용할 경우, 우선 트레이스 출력 정보가 나올 수신지를 결정해야 한다. 표 1은 각 키워드에 대한 간단한 설명과 함께 각 트레이스 포인트가 각 수신지에 보내는 양에 대해 나와 있다. 예를 들어, print는 전 트레이스 데이터를 stderr minimaloutput 옵션을 이용해 파일에 포착된 인-메모리 버퍼로 각 트레이스포인트에 관한 하위 데이터를 보낸다.

표 1. 트레이스 수신지
키워드기능
minimal선택된 트레이스포인트(식별자 및 타임스탬프만)를 인-코어 버퍼까지 추적한다. 관련 트레이스 데이터는 기록되지 않는다.
maximal선택된 트레이스포인트(식별자, 타임스탬프 및 관련 데이터)를 인-코어 버퍼까지 추적한다.
countJVM 주기에서 선택된 트레이스포인트를 호출한 회수를 센다.
print선택된 트레이스포인트를 공백이 없는 stderr까지 추적한다.
iprint선택된 트레이스포인트를 공백이 있는 stderr까지 추적한다.
external선택된 트레이스포인트를 JVMRI 리스너까지 추적한다.
exception선택된 트레이스포인트를 예외로 남겨둔 인-코어 버퍼까지 추적한다.

여기서 각 키워드의 값을 필요한 트레이스 포인트에 설정해야 한다. 예를 들어:
  • -Xtrace:maximal=all은 JVM 트레이스 포인트에서 유용한 정보를 내부 래핑 버퍼까지 추적한다.
  • -Xtrace:iprint=awt은 시작과 종료에 공백이 있는 상태에서 모든 JVM 내부 AWT 트레이스 포인트를 stderr까지 추적한다.
  • -Xtrace:iprint=awt는 메소드 트레이스를 설정하고 출력 장소를 공백이 있는 stderr로 설정한다.
표 1에서 나온 옵션을 이용하면 자체적으로 출력이 생성되지 않기 때문에 각각 추적하기 위해선 메소드 명칭을 제공해야 한다. 32장의 관련 IBM 진단 가이드의 "자바 애플리케이션 및 JVM"에서는 모든 트레이스 옵션에 관해 자세하게 설명할 것이다. (참고자료)
트레이스에 관한 인-스토리지 버퍼를 사용하는 것은 상당히 효율적이다. 문제를 발견하거나 버퍼에 있는 자료를 파일로 재빨리 이동시키기 위해 API를 사용할 때까지 명시된 데이터 입/출력 기능이 수행되지 않기 때문이다. 쓰레드 당 원리로 버퍼를 할당한다. 이는 쓰레드 간의 충돌을 배제하고 다른 쓰레드로 각각의 쓰레드에 대한 트레이스 데이터를 제거하는 현상을 막기 위함이다. 예를 들어 하나의 특정 쓰레드를 전달하지 못할 경우, 버퍼에 있는 자료를 덤핑하거나 재빨리 이동할 때 특정 쓰레드에 대한 트레이스 정보는 여전히 유용하다.
트레이스 데이터를 조사하려면, 버퍼 자료를 덤핑하거나 이동시킨 다음 포맷시켜야 한다. 다음과 같은 경우에 버퍼 자료의 이동은 자동적으로 발생한다.
  • 자바에서 처리되지 않은 예외가 발생할 시
  • 운영 체계 신호가 울리거나 예외가 발생될 때
  • com.ibm.jvm.Trace.snap() Java API를 호출할 경우
  • JVMRI TraceSnap 함수를 호출할 경우
인-스토리지 트레이스에 대한 확장 기능으로 트레이스 데이터를 연속적으로 파일에 기록할 수 있다. 하지만 쓰레드 당 하나의 버퍼 대신 적어도 두 개 이상의 버퍼가 할당된다. 이와 같은 할당 과정으로 전 쓰레드 버퍼를 파일시스템에 기록하는 동안 쓰레드는 계속 실행된다. 트레이스 볼륨, 버퍼 크기 및 출력장치의 대역폭에 따라서 주어진 쓰레드에 다중 버퍼가 할당되어 생성되는 트레이스 데이터와 보조를 맞추게 된다.
minimal 또는 maximal 트레이스 옵션의 출력 정보가 파일에 기록되었는지 여부를 지정하려면 output(출력) 키워드나 예외 옵션에 관한 exception.output 키워드를 반드시 사용한다.
  • -Xtrace:maximal=all,output=trace.out은 trace.out이라는 이름의 파일을 추적한다.
  • -Xtrace:maximal=all,output={trace.out,5m}은 trace.out이라는 이름의 파일을 추적하고 파일 크기가 5MB까지 증가하면 파일 내부를 감싸는 역할을 한다.
  • -Xtrace:maximal=all,output={trace#.out,5m,5}는 다섯 개의 파일을 순차적으로 추적한다. 각각의 파일 용량은 5MB이고#은 파일 반복 번호로 교체된다. 이에 관한 예를 들면 trace0.trc서부터 trace4.trc에 이르는 명칭의 파일들이 생성된다, 각 파일은 5MB용량의 최신 트레이스 데이터가 포함되어 있다. 모든 5개의 파일을 채우고 나면 JVM은 trace0.trc를 덧쓰게 되고, 이런 과정은 trace4.trc까지 이어진다. 이 옵션을 통해 생성되는 최대 파일 수는 36개고, 이 경우 #은 0~9까지 바뀌고 이후 A~Z까지 바뀐다.
파일 이름을 다음과 같이 바꾸는 것 또한 가능하다.
  • %p: 자바 프로세스에 관한 ID
  • %d: 현재 날짜, (년/월/일 순)
  • %t: 현재 시간, (시/분/초 순)
트레이스 포매터는 임의의 플랫폼에서 작동하는 자바 프로그램이며, 임의의 플랫폼에서 트레이스 파일을 포맷한다. code.jar에서 IBM SDK와 함께 전송되는 이 포매터는 포맷팅 템플릿을 포함한 TraceFormat.dat 이라는 명칭의 파일을 필요로 한다. 이 파일은 jre/lib에서 전송된다. 다음과 같은 명령행을 사용해 트레이스 포매터를 시작한다.
java com.ibm.jvm.format.TraceFormat input_file [output_file]


여기서 com.ibm.jvm.format.TraceFormat은 트레이스 포매터 클래스고, input_file은 포맷되는 이진 트레이스 파일의 명칭이며,output_file은 옵션 출력 파일 이름이다. 출력 파일이 지정되지 않은 경우, 기본 출력 파일의 이름은 .fmt. 이 추가된 입력 파일 이름이다.
IBM VM 트레이싱 도구는 주요 트레이스포인트의 하위 자료에서 메모리 안에 있는 버퍼까지 데이터를 연속적으로 포착하는 flight recorder를 포함한다. 메모리 버퍼들은 런타임에 문제가 발생할 경우 포착되며, VM 히스토리에 대한 문제 진단 및 분석에 도움이 되고자 사용한다. VM 초기화는 인-스토리지 버퍼 주위를 감싸기 위해 포착되는 소형 트레이스포인트 자료로 트레이스를 시작한다. 트레이스포인트를 활용해 자바 런타임 시 발생되는 임의의 문제를 진단하는 1단계 작업을 수행하고 -verbose:gc 옵션으로 제공되는 데이터의 하위 자료가 항상 유용한지 확인한다. 그와 같은 쓰레기 수집 데이터는 임의의 요구 자바 덤프 파일에도 존재한다.
내부 플라이트 레코더는 이와 같은 명령행 옵션을 사용한다.
-Xtrace:maximal=all{level1},exception=j9mm{gclogger}


명령행 상의 -Xtrace를 지정하거나, 속성 파일로부터 -Xtrace를 들여오는 경우, 활성 트레이스포인트 자료는 소거된다.
자바 메소드 트레이스로 쓰레드 기반에 따라 자바 런타임의 IBM 구현 프로세스 상에서 실행되는 임의의 코드에 대한 메소드 시작과 종료를 통한 메소드 호출 과정을 추적한다. 자바 코드를 수동 계측 하는 과정 없이 추적 과정을 행하며 자바 메소드 트레이스를 이용해 JCL, 제3의 패키지 또는 애플리케이션 코드를 추적한다.
특히 트레이스 조건이 발생하는 디버깅 상황이나 메소드 간에 얘기치 않은 매개변수가 지나가 예외 상황으로 가는 경우, 메소드 트레이스 기능을 사용한다. 트레이스 타임스탬프 내에 마이크로 초의 정밀도 기능이 존재하기 때문에 이 기능은 성능 문제를 디버깅할 시 매우 유용하게 쓰인다.
mt값을 수신지 키워드 (maximal, minimal, print) 중 하나로 설정할 뿐만 아니라 메소드 키워드 토큰을 추가하면서 명령 라인 상에서 메소드 트레이스를 호출한다. 메소드 키워드로 클래스, 메소드 명칭 또는 둘 다에 의한 메소드 트레이스를 선택한다. not 연산자 및 !와 함께 와일드 카드를 사용해 복잡한 선택 기준을 허용한다. 예를 들어,
  • -Xtrace:print=mt,methods={*.*,!java/lang/*.*}: java.lang 패키지의 경우를 제외한 모든 메소드 및 클래스에 대해 메소드 트레이스를 stderr에 기록한다.
  • -Xtrace:maximal=mt,output=trace.out,methods={tests/mytest/*.*}: tests.mytest 패키지에 있는 모든 메소드에 대해 메소드 트레이스를 파일에 기록한다.
IBM의 트레이스 엔진의 가장 강력한 기능 중 하나는 트레이스 이벤트 상에 트리거하는 기능으로 대상 트레이스 출력을 생성하고 생성되는 트레이스 데이터 양을 줄이는 데 중요한 역할을 한다. 이렇게 되면 디버그 되는 애플리케이션의 성능 및 (즉 오버헤드가 상당히 감소함) 데이터 해석 속도가 증가한다. (필요 이상의 정보가 줄어듦)
트레이스 엔진은 VM 내부 또는 자바 메소드로 임의의 주어진 트레이스 포인트에 대한 트리거링 기능 및 표 2에 요약된 대로, 이벤트에서 수행되는 여러 가지 기능이 있다.

표 2. 트레이스 엔진 기능
키워드기능
suspend모든 트레이싱 기능을 일시 중지함. (특정 트레이스 포인트는 제외함).
resume모든 트레이싱 기능을 계속 진행함 (resumecount 속성 기능 및 Trace.suspendThis() 호출 기능에 의해 일시 정지된 쓰레드들을 제외함).
suspendthis본 쓰레드에 대한 일시 중지 카운트를 증가시킴. 0이 아닌 일시 중지 카운트는 쓰레드에 관한 모든 트레이싱 기능을 중지시킨다.
resumethis일시 중지 카운트가 0보다 큰 경우, 본 쓰레드에 대한 이 카운트를 감소시킴. 일시 중지 카운트가 0인 경우, 본 쓰레드에 관한 트레이싱 기능은 계속된다.
sysdump비파괴 시스템 덤프를 생성시킴.
javadump자바 덤프를 생성시킴.
heapdump힙 덤프를 생성시킴.
snap능동적 트레이스 버퍼에 있는 자료를 현재 작동 중인 디렉토리에 있는 파일에 옮김.

트리거 트레이스 이벤트가 일어나는 경우, 선택할 표 2의 기능을 결정하는 트리거(trigger)명령행을 이용해 트리거 트레이스를 설정한다. 이 경우, 트리거 옵션은 기타 트레이스 속성으로 인해 선택된 것을 정상 상태로 생성 또는 차단하는 여부를 관리한다는 점을 알아둔다.
메소드 이벤트에서의 트리거를 지정하기 위해 다음의 포맷을 이용한다.
-Xtrace:trigger=method{method spec, entry action, exit action, delay count, match count}


표시된 메소드 spec에 맞는 임의의 메소드를 입력하자마자 엔트리 기능(entry action)을 수행한다. 메소드를 빠져나갈 경우, 종료 기능(exit action)을 수행한다. 지연 카운트를 지정할 경우 지연 카운트 기능보다 시작과 종료 기능이 더 많이 발생할 때만 시작과 종료 기능을 수행한다. 매치 카운트를 지정할 경우 최대한 많이 매치 카운트 기능보다 시작과 종료 기능이 더 많이 발생할 때만 시작과 종료 기능을 수행한다. 이 예를 생각해 보자.
-Xtrace:trigger=method{java/lang/StackOverflowError*, sysdump}

이렇게 하면 이른바 <clinit> 메소드라 불리는 StackOverflowError 메소드의 첫 번째(오로지 첫 번째) 인스턴스 상에서 비파괴 시스템 덤프를 생성한다.
resumecount 또는 suspendcount 키워드와 함께 일시 정지(suspend) 재시작(resume) 옵션을 사용해 각각의 쓰레드 또는 모든 쓰레드를 일시 정지 하거나 계속 실행한다.
-Xtrace:resumecount=1

-Xtrace:trigger=method{HelloWorld.main,resume,suspend}


이 옵션들은 HelloWorld.main()을 호출하는 경우 모든 쓰레드에 대해 트레이싱 기능을 시작하며, HelloWorld.main() 호출이 정지되는 경우, 트레이싱 기능을 중지시킨다. 이렇게 되면 트레이싱 기능은 자바 런타임 시작 시간 동안 발생하지 않으며, 이 기능을 통해 Helloworld 애플리케이션이 작동하고 있는 동안 트레이스 데이터만을 생성한다.
트레이스 엔진을 사용해 자바 런타임 자체적으로 또는 자바 런타임 상에서 작동 중인 애플리케이션 코드에서, 발생되는 임의의 문제 형태에 관한 데이터 흐름 및 기록 데이터를 생성한다. 덤프 엔진에서 생성되고 덤프 파일 안에 있는 상태 데이터와 함께 이러한 기록 데이터는 많은 문제를 인지하고 디버깅하는 강력한 수단을 제공한다.
자바 런타임의 IBM구현을 통해 만든 덤프 엔진은 IBM 지원 팀이 자바 런타임 자체 또는 IBM SDK와 함께 제공되는 JCL에서 발생하는 문제를 진단하는 데 사용되는 대부분의 필요한 데이터를 제공한다. 많은 문제들이 나오는 원인을 해결하기 위해 후-처리되는 특수 이벤트 상의 여러 가지 다른 덤프 형태를 트리거하는 기본 설정 기능을 덤프 엔진이 가지고 있다.
한편 자바 애플리케이션에서 나오는 문제를 진단하는 데 도움이 되는 수많은 덤프 및 유용한 이벤트를 사용한다. JCL에서 문제를 진단하는 과정은 기타 자바 클래스에서 발생하는 문제를 진단하는 과정과 똑같기 때문이다.
IBM 덤프 엔진은 4가지 다른 덤프 형태(z/OS®상에서 5개의 덤프 형태)를 생성한다. 뿐만 아니라 필요한 경우 각각의 과정에서 도구를 실행하는 기능을 가지고 있다. 각각의 덤프 형태는 그 자체로 비파괴적이지만 SIGSEGV/GPF와 같은 오류 이벤트에 의해 발생될 경우에는 파괴적으로 된다. 표3에는 유용한 덤프 형태에 대해 요약되어 있다.

표3. 덤프 형태
KeywordDump typeDescription
javaJava dump (자바 덤프)환경. 쓰레드 스택 및 클래스 정보를 포함한 상태 리포트
heapHeap dump (힙 덤프)자바 힙 상의 각 객체에 대한 크기 및 레퍼런스 사항을 포함한 덤프
snapSnap dump(스냅 덤프)트레이스 버퍼에 있는 컨텐츠를 파일에 기록함.
systemSystem dump(시스템 덤프)운영 체제(코어 파일, 미니 덤프 또는 트랜잭션 덤프)의 정상 포맷에 있는 프로세스 이미지
ceedumpCEEDUMPz/OS-특정 쓰레드 스택 및 레지스터 요약 파일
toolTool agent (도구 에이전트)제공된 명령행을 이용해 미리 정의된 도구를 수행한다.

IBM 덤프 엔진은 표 4에 나와 있는 각각의 이벤트 형태에 대해 유용한 임의의 모든 덤프 형태를 생성하는 기능을 가지고 있다.

표 4. 이벤트 형태
이벤트설명
gpfSIGSEGV 또는 SIGILL과 같이 얘기치 않은 충돌 현상이 발생함
userA SIGQUIT 신호가 (Window상의 Control+Break, Linux 상의Control+\, z/OS상의 Control+V) 발생한다.
vmstartVM 에서 초기화 종료.
vmstopVM이 셧다운됨.
load새로운 클래스를 로드함.
unload클래스 로더를 언로드함.
throw자바 예외를 없앰.
catch자바 예외를 포착함.
uncaught애플리케이션에서 자바 예외를 취급하지 않음.
thrstart새로운 쓰레드 시작
thrstop구 쓰레드 중지
blocked모니터에 들어가면서 쓰레드 중지.
fullgc쓰레기 수집이 시작됨

이런 이벤트 들은 그 자체로 각각의 덤프 형태를 생성할 시 상당히 뛰어난 가변성을 제공한다. 하지만 덤프 필터가 추가되면 그와 같은 가변성을 증가시킨다. 필터를 각각의 이벤트에 추가해 덤프를 생성할 시 더 많은 세분성을 제공한다. 예를 들어, 예외 및 에러 명칭을throw, catch uncaught 이벤트에 추가할 수 있다.
토큰 시리즈와 함께 -Xdump 명령행 옵션을 사용해 모든 덤프 옵션을 설정해 다양한 옵션을 설정한다. Listing 1에 나온 -Xdump:what을 이용해 기본 덤프 옵션을 볼 수 있다.

Listing 1. -Xdump:what 출력
    

C:\home> java -Xdump:what



Registered dump agents

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

dumpFn=doSystemDump                             // Generate a system dump

events=gpf+abort                                // on SIGSEGV and SIGABRT events

filter=          

label=C:\home\core.%Y%m%d.%H%M%S.%pid.dmp       // location and name of file

range=1..0                                      // write on every event occurrence

priority=999                                    // write this dump first

request=serial                                  // write in serial

opts=

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

dumpFn=doSnapDump                               // Generate trace snap file

events=gpf+abort                                // on SIGSEGV and SIGABRT events

filter=

label=C:\home\Snap%seq.%Y%m%d.%H%M%S.%pid.trc   // location and name of file

range=1..0                                      // write on every event occurrence

priority=500                                    // write after higher priority dumps

request=serial                                  // write in serial

opts=

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

dumpFn=doSnapDump                               // Generate trace snap file

events=uncaught                                 // on uncaught exceptions

filter=java/lang/OutOfMemoryError               // that match OutOfMemoryError

label=C:\home\Snap%seq.%Y%m%d.%H%M%S.%pid.trc   // location and name of file

range=1..4                                      // write only on the first four events

priority=500                                    // write after higher priority dumps

request=serial                                  // write in serial

opts=

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

dumpFn=doHeapDump                               // Generate heap dump file

events=uncaught                                 // on uncaught exceptions

filter=java/lang/OutOfMemoryError               // that match OutOfMemoryError

label=C:\home\heapdump.%Y%m%d.%H%M%S.%pid.phd   // location and name of file

range=1..4                                      // write only on the first four events

priority=40                                     // write after higher priority dumps

request=exclusive+prepwalk                      // make sure the heap is walkable

opts=PHD                                        // write in "PHD" format

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

dumpFn=doJavaDump                               // Generate java dump file

events=gpf+user+abort                           // on SIGSEGV, SIGABRT and SIGQUIT events

filter=

label=C:\home\javacore.%Y%m%d.%H%M%S.%pid.txt   // location and name of file

range=1..0                                      // write on every event occurrence

priority=10                                     // write after higher priority dumps

request=exclusive                               // obtain exclusive access to walk the VM

opts=

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

dumpFn=doJavaDump                               // Generate java dump file

events=uncaught                                 // on uncaught exceptions

filter=java/lang/OutOfMemoryError               // that match OutOfMemoryError

label=C:\home\javacore.%Y%m%d.%H%M%S.%pid.txt   // location and name of file

range=1..4                                      // write only on the first four events

priority=10                                     // write after higher priority dumps

request=exclusive                               // obtain exclusive access to walk the VM

opts=

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


한편 구문을 변경해 부가적으로 덤프를 추가할 수 있다. 포착되지 않은 소켓 예외 상에 자바 덤프를 생성하려면, 다음과 같은 구문을 사용한다.
-Xdump:java:events=uncaught,filter=java/net/SocketException


모든 힙 덤프를 제거하려면 다음 구문을 사용한다.
-Xdump:heap:none


향상된 덤프 엔진 도구를 이용해 IBM SDK 자체의 문제를 해결한다. 여기서 이 도구들을 이용해 자바 애플리케이션에서 나오는 문제를 해결하는 게 더 중요하다. OutOfMemoryErrors 상에서 자바 덤프 파일 및 힙 덤프를 생성하는 기능으로 메모리 누수 현상을 진단하고 임의의 대형 객체에 대해 할당된 스택을 결정할 수 있게 된다. 기타 자바 예외에서 자바 덤프 파일을 생성하는 기능으로, 덤프에서 쓰레드 스택 데이터를 사용, 잠재적인 트레이스 상태를 디버깅 한다.
게다가, 다양한 이벤트 상에서 비파괴 시스템 덤프를 생성하는 기능으로 인해 이벤트 포인트에서의 임의의 자바 애플리케이션 부분에 관한 상태를 조사하는 데 DTFJ API를 사용한다.
DTFJ API는 다양한 덤프 포맷 또는 자바 객체 및 기타 자바 구조를 메모리 안에 설치하는 과정에 관한 지식 없이 프로세스 이미지(예를 들면 시스템 덤프)의 스냅샷으로부터 자바 프로세스에 관한 정보를 이용하는 자바-기반 API다.
이미 논의되었듯이, 트레이스 또는 덤프 엔진을 이용해 자바 런타임의 IBM구현으로 비파괴 시스템 덤프를 생성한다. 게다가,com.ibm.jvm.Dump.SystemDump()정적 메소드를 사용해 비파괴 시스템을 생성한다. 유용한 운영 체계 도구인 -- AIX®상의 gencore 또는 리눅스 상의 gcore 등을 이용해 역시 비파괴 시스템을 생성한다.
비파괴 시스템 덤프를 생성하면서 DTFJ API를 이용하는 도구는 라이브 시스템에서부터 나온 정보를 얻을 뿐만 아니라 고장이 나서 셧다운 된 시스템을 후-처리한다.
DTFJ API는 런타임 구현과는 무관한 계층 인터페이스다. 다중 운영 체계 및 하드웨어 플랫폼, 다중 가상 머신 구현 프로세스 및 다중 언어를 다루기 위해 API 자체를 조사한다. 자바 런타임을 중점적으로 고려해 DTFJ API에 포함된 확장자의 기본 자료를 만들고 이를 통해 도구 작성자는 JVM 데이터 구조를 인식하고 조사한다. 자바 런타임의 IBM구현 프로세스와 같이 전송되는 DTFH 구현 프로세스는 자바 런타임 내의 데이터 구조에 관한 정보를 제공한다.
API는 자체적으로 리플렉션 API의 상당한 영향을 받고 Iterator를 사용하는 자바 프로세스의 계층형 뷰와 결합해 고급 객체에서 점점 특정화되는 객체에 이르는 접근 기능을 제공한다. 이렇게 되면 프로세스 Image에서 각각의 JavaField JavaMethod 객체까지 다양한 데이터 객체가 나오고, 객체들이 차례로 탐색되면서 데이터를 얻게 된다. 객체는 시스템 덤프를 얻는 지점에서 이 데이터를 포함한다. 그림 1에서는 DTFJ API가 인지하고 탐색하는 몇 가지 데이터 객체에 대해 나와 있다.

그림 1. DTFJ 데이터 객체 개요
DTFJ data objects overview
JExtract 유틸리티는 시스템 덤프 포맷 및 자바 런타임의 내부 데이터 구조를 인지한다. 이 유틸리티는 인지 기능을 이용해 다양한 데이터 구조의 위치를 나타내는 시스템 덤프 파일 내에서 인덱스를 제공하는 XML 설명 파일을 생성한다. 그러면 DTFJ는 시스템 덤프 및 JExtract-생성 XML 파일을 조합해 DTFJ API를 사용하는 도구에서 요구되는 정보를 제공한다.
The JExtract utility understands both the format of the system dump and the Java runtime's internal data structures. It uses that knowledge to create an XML description file that provides indexes into the system dump file that indicate the location of various data structures. DTFJ then uses the combination of the system dump and the JExtract-produced XML file to provide the information requested by the tools that use the DTFJ API.
JExtract가 시스템 덤프용 포스트 프로세서지만, 덤프 엔진 도구를 사용할 수 있다. 그 결과 시스템 덤프를 생성한 이후 JExtract를 자동적으로 호출한다. 예를 들어, OutOfMemoryErrors상에 요구되는 시스템 덤프를 얻으려면 다음과 같은 구문을 사용한다.
-Xdump:tool:events=uncaught,filter=

  OutOfMemoryError,exec="jextract .\core.%Y%m%d.*.%pid.dmp"


DTFJ API를 이용해 시스템 덤프 내에 존재하는 대형 정보를 처리한다. 이 정보는 프로세스에서 실행 중인 플랫폼: 물리적 메모리, CPU 번호 및 형태, 라이브러리, 명령 라인, 쓰레드 스택 및 레지스터 등에 관한 정보를 포함한다. 또한 클래스로더, 쓰레드, 모니터, 힙, 객체, 자바 쓰레드, 메소드, 컴파일 코드, 필드 및 필드 값을 포함해 프로세스에서 실행 중인 자바 런타임 및 자바 애플리케이션의 상태에 관한 정보를 제공한다.
이와 같이 다양한 데이터 생성물을 이용할 수 있기에, DTFJ API는 임의의 개수로 도구를 생성할 수 있는 가변성을 제공한다. 예를 들어, 더 단순한 레벨에서, DTFJ API를 통해 도구를 생성해 다양한 캐시의 크기 및 컨텐츠를 탐색해 캐시를 수용하는 데 필요한 실제 자바 힙 메모리 양을 조절한다.
DTFJ-기반 도구를 사용하는 첫 번째 단계는 시스템 덤프와 연관된 Image 객체를 얻은 다음, ImageAddressSpace로부터ImageProcess객체를 얻는 것이다. Listing 2에 잘 나와 있다.

Listing 2. DTFJ를 사용, 현 프로세스 얻기
    

Image theImage =  new ImageFactory().getImage(new File(fileName));

ImageAddressSpace currentAddressSpace = 

  (ImageAddressSpace) theImage.getAddressSpaces().next();

ImageProcess currentProcess = currentAddressSpace.getCurrentProcess();


대부분의 플랫폼에서는, 이미지에 단일 ImageAddressSpace ImageProcess 객체만이 있다. 하지만 메인프레임 운영 체계에는 각각의 다중 인스턴스를 포함하고 있다.
일단 ImageProcess 객체를 얻고 나면, Listing 3에 나온 바와 같이, 고유 쓰레드를 처리할 수 있다.

Listing 3. 쓰레드 및 스택 프레임 얻기
    

Iterator vmThreads = process.getThreads();

ImageThread vmThread = (ImageThread) vmThreads.next();

Iterator vmStackFrames = vmThread.getStackFrames();


Listing 4에서처럼, 다양한 ImageModule(라이브러리) 객체를 처리할 수 있다.

Listing 4. 적재 라이브러리 얻기
    

Iterator loadedModules = process.getLibraries()


ImageProcess 객체에서, Listing 5에서처럼, JavaRuntime을 생성해 얻는다.

Listing 5. 자바 런타임 얻기
    

JavaRuntime runtime = (JavaRuntime) process.getRuntimes().next();


이렇게 되면, 모든 자바 구조에 접근하게 된다.
JavaRuntime 객체에서 작성 도구의 사용을 시작해 작동 중인 임의의 자바 애플리케이션을 탐색하는 과정이 가능하다. Listing 6에서의 단순한 예에서는 자바 힙 상에서의 모든 객체를 반복한 다음 각 형태의 객체 수를 계산하는 방법에 대해 나와 있다.

Listing 6. 덤프 내의 각 형태의 객체 수 계산하기
    

..

    Map<String,Long> objectCountMap = new HashMap<String,Long>();



    Iterator allHeaps = currentRuntime.getHeaps();



    /* Iterate over each of the Java heaps and call countObjects on them to */

    /* populate the object type count HashMap                               */

    while(allHeaps.hasNext()) {

         countObjects((JavaHeap)allHeaps.next(),objectCountMap);

    }



    /* print out each of the entries in the HashMap of object types        */

    for (String objectClassName : objectCountMap.keySet()) {

         System.out.println(objectClassName + 

           " occurs " + objectCountMap.get(objectClassName));

    }



    private static void countObjects(JavaHeap currentHeap, 

      Map<String, Long> objectCountMap)

    throws Exception{



        /* Iterate over each of the Objects on the supplied Java heap       */

        Iterator currentHeapObjects = currentHeap.getObjects();

        while(currentHeapObjects.hasNext()) {

            JavaObject currentObject = (JavaObject)currentHeapObjects.next();



            /* Get the name of the class from the object                    */

            String objectClassName = currentObject.getJavaClass().getName();

            long objectCount = 0;



            /* Add the class name to the HashMap, or increase the count if it */

            /* already exists                                                 */

            if (objectCountMap.containsKey(objectClassName)) {

                 objectCount = objectCountMap.get(objectClassName);

            }

            objectCountMap.put(objectClassName, objectCount + 1);

        }

   }

  


우리가 논의한 모든 기능들은 자바 배치 시 개발 및 생성 문제를 진단, 해결하려고 할 경우에 도움이 될 것이다. 상세한 상태 데이터 처리용으로 사용이 쉬운 API와 함께 기록 트레이스 데이터 및 상세 상태 데이터를 생성하는 세 가지 주요 도구들을 사용하면 자바 애플리케이션을 탐색하는 강력하고도 융통성 있는 방법을 제공하게 되어 문제 상황을 해결하는 데 유용하다.
이 글에서는 IBM이 자바 가상 머신의 구현 시 개선점 및 변화를 설명했다. 메모리 관리, 클래스 공유 및 애플리케이션 모니터링에 대한 영역을 다루고, 위의 기능들을 이용해 자바 애플리케이션의 기능 및 유용성을 향상시키는 방법에 대해서도 배웠다. 자바 가상 머신 구현 시 향상된 점과 그 밖의 많은 사항에 대한 자세한 정보는 IBM 진단 가이드에서 이용할 수 있고, IBM 런타임 및 SDK 포럼에 참여하기 바란다. (참고자료)
본 시리즈의 마지막 글에서 자바 보안 개발 팀은 자바 플랫폼의 IBM 보안 향상에 관해 다룰 것이다. 그 글에서는 각각의 보안 업체에 대해 소개하고, 이들이 제공하는 기능에 대해 검토할 것이다.

교육
제품 및 기술 얻기
토론
Chris Bailey
Chris Bailey는 2000년 Southampton University의 대학원생 시절에 IBM Java Technology Centre에 입사했다. 자바 기술과 자바 플랫폼 기반 제품의 IBM 포트와 관련된 문제를 해결하는 일을 했다. 그는 "IBM Java Runtimes and SDKs" 이라는 developerWorks 포럼을 운영하고 있고 현재에는 자바 플랫폼의 IBM 포트의 사용자들이 사용할 수 있는 정보 및 툴링을 향상시키는데 주력하고 있다.
Simon Rowland는 2001년 Leeds 대학을 졸업한 뒤, IBM 자바 기술 센터에 입사했다. 그는 철학을 전공했지만, 철학으로 시간을 보내는 것보다는 프로그래머의 섬세한 삶이 마음에 들어 프로그래머로 진로를 바꾸기로 결심했다. Simon은 IBM에서 다양한 프로젝트 및 플랫폼을 연구하는 데 시간을 보냈고 현재는 개발 팀에서 근무하면서 자바 기술의 IBM 구현에 관한 트레이스 및 덤프 기능에 대해 중점적으로 연구하고 있다. 여가 시간에는 달리기와 자전거를 즐기며 쉬는 동안에 가장 좋은 아이디어를 만들어 낸다.

댓글 없음:

댓글 쓰기