'Java SE 6 플랫폼 응용 프로그램 모니터링 및 관리'에 해당되는 글 1건

  1. 2012.05.08 Java SE 6 플랫폼 응용 프로그램 모니터링 및 관리
01.JAVA/Java2012. 5. 8. 14:59
반응형

BigAdmin System Administration Portal
Java SE 6 플랫폼 응용 프로그램 모니터링 및 관리
Print-friendly VersionPrint-friendly Version

번역 책임의 한계: 
본 기사는 어느 정도의 인간의 간섭과 사후 편집을 포함하여 컴퓨터 소프트웨어 프로그램으로 번역되었습니다. 번역은 독자의 편리함을 위해 “있는 그대로” 제공되며, Sun은 번역된 문장 또는 페이지 전체로서의 정확성 또는 완전성에 대한 아무런 대표성이 없으며 어떠한 책임도 지지 않습니다. 사이트와 툴의 사용에 관한 추가 고지 사항에 대해서는 본 사이트의 이용 약관을 참조하십시오.

 
 

기사 색인

응용 프로그램이 원래 속도보다 느리게 실행되거나 이전보다 느리게 실행되는 듯 하거나 또는 응용 프로그램이 응답이 없거나 중단되었습니다. 프로덕션 환경 또는 개발 중에 이러한 상황에 처할 수 있습니다. 이러한 문제의 원인은 무엇일까요? 흔히 메모리 누출, 교착 상태 및 동기화 문제와 같은 원인은 진단하기 어렵습니다. Java Platform Standard Edition(Java SE) 버전 6은 즉시 사용할 수 있는 모니터링 및 관리 기능을 제공하여 여러 일반적인 Java SE 문제를 진단할 수 있도록 도와줍니다.

이 기사는 Java SE 6 응용 프로그램을 모니터링 및 관리하는 단기 과정입니다. 첫 번째, Java SE 응용 프로그램의 일반적인 문제와 증상에 대해 설명합니다. 두 번째, Java SE 6의 모니터링 및 관리 기능의 개요에 대해 설명합니다. 세 번째, 다양한 JDK(Java Development Kit) 도구를 사용하여 이러한 문제를 진단하는 방법에 대해 설명합니다.

주: Java SE 플랫폼 사양에 대한 API 추가 또는 기타 개선 사항은 JSR 270 전문가 그룹에 의해 검토 및 승인됩니다.

Java SE 응용 프로그램의 일반적인 문제

일반적으로 Java SE 응용 프로그램의 문제는 메모리, 스레드, 클래스 및 잠금과 같은 중요 자원과 관련되어 있습니다. 자원 경합 또는 누수는 성능 문제나 예상치 않은 오류로 이어질 수 있습니다. 표 1은 Java SE 응용 프로그램의 몇 가지 일반적인 문제와 증상을 요약하고 개발자가 각 문제의 원인을 진단하는 데 도움을 줄 수 있는 도구를 나열합니다.

표 1. 일반적인 문제 진단 도구
 
 
문제
증상
진단 도구
OutOfMemoryError
메모리 사용 증가
빈번한 가비지 컬렉션
 
성장률이 높은 클래스
예상치 않은 수의 인스턴스가 있는 클래스
메모리 맵(jmap)
jmap -histo 옵션 참조
 
객체가 의도하지 않게 참조되고 있음
jconsole 또는 jmap(jhat 포함)
jmap -dump 옵션 참조
객체가 종료 대기 중임
jconsole
jmap -dump(jhat 포함)
객체 모니터의 스레드 블록 또는 java.util.concurrent 잠금
스레드 CPU 시간이 지속적으로 증가
jconsole(JTop 포함)
치열한 경합 통계가 있는 스레드
jconsole

메모리 부족

JVM(Java Virtual Machine)*에는 비힙 및 기본 유형의 메모리가 있습니다.

힙 메모리는 모든 클래스 인스턴스 및 배열이 할당되는 메모리의 런타임 데이터 영역입니다. 비힙 메모리에는 JVM의 최적화 또는 내부 처리에 필요한 메소드 영역과 메모리가 포함되어 있습니다. 이는 런타임 상수 풀, 필드 및 메소드 데이터, 메소드 및 구성자의 코드와 같은 클래스당 구조를 저장합니다. 기본 메모리는 운영 체제에 의해 관리되는 가상 메모리입니다. 응용 프로그램을 할당하기에 메모리가 부족한 경우 java.lang.OutOfMemoryError가 발생합니다.

다음은 각 메모리 유형에서의 OutOfMemoryError에 대한 가능한 오류 메시지입니다.

  • 힙 메모리 오류. 응용 프로그램이 새 객체를 만들지만 힙이 충분한 공간을 가지지 않고 더 이상 확장할 수 없을 경우 다음 오류 메시지와 함께 OutOfMemoryError가 발생합니다.

    java.lang.OutOfMemoryError: Java heap space
     
  • 비힙 메모리 오류. 영구 생성은 인턴된 문자열뿐만 아니라 클래스당 구조를 저장하는 핫스폿 VM 구현에 있는 비힙 메모리 영역입니다. 영구 생성이 가득 차면 응용 프로그램이 클래스 로드나 인턴된 문자열 할당에 실패하여 다음 오류 메시지와 함께 OutOfMemoryError가 발생합니다.

    java.lang.OutOfMemoryError: PermGen space
    
     
  • 기본 메모리 오류. 응용 프로그램의 JNI(Java Native Interface) 코드 또는 기본 라이브러리 및 JVM 구현은 기본 힙으로부터 메모리를 할당합니다. OutOfMemoryError는 기본 힙에서 할당이 실패할 경우 발생합니다. 예를 들어, 다음 오류 메시지는 스왑 공간이 부족함을 나타냅니다. 이 오류는 운영 체제의 구성 문제 또는 시스템의 다른 프로세스에 의해 발생할 수 있으며 많은 메모리를 소비합니다.

    java.lang.OutOfMemoryError: request <size> bytes for <reason>. 
    Out of swap space?
    
     

메모리 부족 문제는 구성(해당 응용 프로그램이 실제로 그렇게 많은 메모리를 필요로 함) 문제 또는 메모리 사용을 줄이기 위한 프로파일링 및 최적화를 필요로 하는 응용 프로그램의 성능 문제 때문일 수 있습니다. 메모리 사용을 줄이기 위한 메모리 설정 구성 및 응용 프로그램 프로파일링은 이 기사의 범위를 넘어서는 것이지만 관련 정보에 대해 핫스폿 VM 메모리 관리 백서(PDF)를 참조하거나 NetBeans IDE Profiler와 같은 프로파일링 도구를 사용할 수 있습니다.

메모리 누출

JVM은 자동 메모리 관리를 책임지고 있으며 이는 응용 프로그램에 사용되지 않은 메모리를 재생 이용합니다. 그러나 응용 프로그램이 더 이상 필요하지 않은 객체에 대한 참조를 유지하는 경우 객체는 가비지 컬렉션될 수 없으며 객체가 제거될 때까지 힙에서 공간을 차지합니다. 이러한 의도하지 않은 객체 유지를 메모리 누출이라고 합니다. 응용 프로그램이 많은 양의 메모리를 누출하면 결국에는 메모리를 다 사용하여 OutOfMemoryError가 발생합니다. 또한 가비지 컬렉션은 응용 프로그램이 여유 공간을 만들려고 함에 따라 더욱 더 빈번히 발생하므로 응용 프로그램의 실행 속도가 느려집니다.

종료자

OutOfMemoryError의 또 다른 가능한 원인은 종료자의 과도한 사용입니다. java.lang.Object 클래스에는 finalize라는 보호된 메소드가 있습니다. 클래스는 이 finalize메소드를 대체하여 해당 클래스의 객체가 가비지 컬렉션에 의해 재생 이용되기 전에 시스템 자원을 지우거나 정리를 수행할 수 있습니다. 객체에 대해 호출될 수 있는 finalize메소드는 해당 객체의 finalizer입니다. 종료자가 실행되는 시기 또는 적어도 실행은 될지 여부가 보장되지 않습니다. 종료자가 있는 객체는 종료자가 실행될 때까지 가비지 컬렉션되지 않습니다. 따라서 종료 대기 중인 객체는 응용 프로그램에 의해 더 이상 참조되지 않더라도 메모리를 유지하여 메모리 누출과 유사한 문제가 발생할 수 있습니다.

교착 상태

교착 상태는 둘 이상의 스레드가 각기 다른 스레드가 잠금을 해제하도록 기다릴 때 발생합니다. Java 프로그래밍 언어는 모니터를 사용하여 스레드를 동기화합니다. 각 객체는 모니터와 연관되어 있어 객체 모니터라고도 할 수 있습니다. 스레드가 객체에서 synchronized 메소드를 호출하는 경우 해당 객체는 잠깁니다. 동일한 객체에서synchronized 메소드를 호출하는 또 다른 스레드는 잠금이 해제될 때까지 차단됩니다. 내장 동기화 지원 외에도 J2SE 5.0에 도입된 java.util.concurrent.locks 패키지는 조건을 잠금고 기다리는 프레임워크를 제공합니다. 교착 상태는 java.util.concurrent 잠금뿐만 아니라 객체 모니터와도 연관될 수 있습니다.

일반적으로 교착 상태는 응용 프로그램 또는 응용 프로그램의 일부가 응답하지 않도록 만듭니다. 예를 들어, GUI(그래픽 사용자 인터페이스) 업데이트를 책임지고 있는 스레드가 교착 상태에 빠지면 GUI 응용 프로그램이 중단되고 어떠한 사용자 동작에도 응답하지 않습니다.

루핑 스레드

루핑 스레드는 또한 응용 프로그램이 중단되도록 만들 수 있습니다. 하나 이상의 스레드가 무한 루프에서 실행되고 있는 경우 해당 루프는 사용 가능한 모든 CPU 주기를 다 소비하고 응용 프로그램의 나머지 부분이 응답하지 않도록 만들 수 있습니다.

치열한 잠금 경합

동기화는 다중 스레드 응용 프로그램에 많이 사용되어 공유 자원에 대한 액세스를 상호 배제하고 다중 스레드 간의 작업을 조정 및 완료합니다. 예를 들어, 응용 프로그램이 객체 모니터를 사용하여 데이터 구조에서 업데이트를 동기화합니다. 두 스레드가 동시에 데이터 구조를 업데이트하려고 하면 한 스레드만이 객체 모니터를 획득하여 데이터 구조 업데이트를 진행할 수 있습니다. 한편, 다른 스레드는 첫 번째 스레드가 업데이트를 완료하고 객체 모니터를 해제할 때까지 synchronized 블록에 들어가기 위해 기다리므로 차단됩니다. 동기화 경합은 응용 프로그램 성능 및 확장성에 영향을 줍니다.

Java SE 6 플랫폼의 모니터링 및 관리 기능

Java SE 6의 모니터링 및 관리 지원에는 다양한 VM(가상 머신) 자원을 검사하기 위한 유용한 진단 도구와 프로그램 인터페이스가 포함되어 있습니다. 프로그램 인터페이스에 대한 자세한 내용은 API 사양을 참조하십시오.

JConsole은 Java 모니터링 및 관리 콘솔로서 이를 사용하여 런타임에 다양한 VM 자원 사용을 모니터링할 수 있습니다. 또한 응용 프로그램 실행 동안 이전 섹션에서 설명한 증상을 감시할 수 있습니다. JConsole을 사용하여 동일한 시스템에서 로컬로 실행되거나 다른 시스템에서 원격으로 실행되는 응용 프로그램에 연결하여 다음과 같은 정보를 모니터링할 수 있습니다.

  • 메모리 사용 및 가비지 컬렉션 작업
  • 스레드 상태, 스레드 스택 추적 및 잠금
  • 종료 대기 중인 객체의 수
  • 가동 시간 및 프로세스가 소비하는 CPU 시간과 같은 런타임 정보
  • JVM에 대한 입력 인수 및 응용 프로그램 클래스 경로와 같은 VM 정보

또한 Java SE 6에는 기타 명령줄 유틸리티가 포함되어 있습니다. jstat 명령은 메모리 사용, 가비지 컬렉션 시간, 클래스 로딩 및 JIT(Just-In-Time) 컴파일러를 비롯한 다양한 VM 통계를 인쇄합니다. jmap 명령를 사용하면 런타임에 힙 막대 그래프 및 힙 덤프를 얻을 수 있습니다. jhat 명령을 사용하면 힙 덤프를 분석할 수 있습니다. 또한 jstack 명령을 사용하면 스레드 스택을 추적할 수 있습니다. 이러한 진단 도구들은 특수 모드에서 시작할 필요 없이 모든 응용 프로그램에 연결할 수 있습니다.

JDK 도구를 사용한 진단

이 섹션에서는 JDK 도구를 사용하여 일반적인 Java SE 문제를 진단하는 방법에 대해 설명합니다. JDK 도구를 사용하면 응용 프로그램에 대한 보다 정확한 진단 정보를 얻을 수 있으며 응용 프로그램이 원래대로 작동하고 있는지 알 수 있습니다. 일부 경우에 진단 정보는 문제를 진단하고 근본 원인을 식별하기에 충분할 수 있습니다. 다른 경우에는 프로파일링 도구나 디버거를 사용하여 문제를 디버깅해야 할 수 있습니다.

각 도구에 대한 자세한 내용은 Java SE 6 도구 설명서를 참조하십시오.

메모리 누출 진단 방법

메모리 누출은 재현에 매우 긴 시간이 소요될 수 있으며 매우 드물거나 모호한 조건에서 발생하는 경우 특히 그렇습니다. 이상적인 경우는 개발자가 OutOfMemoryError가 발생하기 전에 메모리 누출을 진단하는 것입니다.

먼저 JConsole을 사용하여 메모리 누출이 계속해서 증가하는지 모니터링합니다. 이는 가능한 메모리 누출을 나타냅니다. 그림 1은 메모리 사용이 증가하고 있는 MemLeak라는 응용 프로그램에 연결된 JConsole의 메모리 탭을 보여줍니다. 메모리 탭 내에 삽입된 상자에서 GC(가비지 컬렉션) 작업을 관찰할 수 있습니다.

그림 1: 메모리 탭은 증가하는 메모리 사용을 보여주고 있으며 이는 가능한 메모리 누출을 나타냅니다.
그림 1: 메모리 탭은 증가하는 메모리 사용을 보여주고 있으며 이는 가능한 메모리 누출을 나타냅니다.

또는 다음과 같이 jstat 명령을 사용하여 메모리 사용 및 가비지 컬렉션 통계를 모니터링할 수 있습니다.

  $ <JDK>/bin/jstat -gcutil <pid> <interval> <count>

jstat -gcutil 옵션은 <count> 횟수 동안 지정된 표본 추출 <interval>의 각 표본에서 프로세스 ID <pid>의 실행 응용 프로그램의 힙 사용 및 가비지 컬렉션의 요약을 인쇄합니다. 이는 다음과 같은 표본 출력을 생성합니다.

  S0     S1     E      O      P     YGC   YGCT    FGC    FGCT     GCT
0.00 0.00 24.48 46.60 90.24 142 0.530 104 28.739 29.269
0.00 0.00 2.38 51.08 90.24 144 0.536 106 29.280 29.816
0.00 0.00 36.52 51.08 90.24 144 0.536 106 29.280 29.816
0.00 26.62 36.12 51.12 90.24 145 0.538 107 29.552 30.090

jstat 출력 및 다양한 VM 통계를 얻기 위한 기타 옵션에 대한 자세한 내용은 jstat 설명서 페이지를 참조하십시오.

힙 막대 그래프

응용 프로그램에 메모리 누출이 있는 것으로 의심되는 경우 jmap 명령을 사용하면 총 인스턴스 수 및 각 클래스의 인스턴스가 사용한 총 바이트 수를 비롯한 클래스당 통계를 보여주는 힙 막대 그래프를 얻을 수 있습니다. 다음 명령줄을 사용합니다.

$ <JDK>/bin/jmap -histo:live <pid>
 

힙 막대 그래프 출력은 다음과 유사합니다.

num   #instances    #bytes  class name
--------------------------------------
  1:    100000    41600000  [LMemLeak$LeakingClass;
  2:    100000     2400000  MemLeak$LeakingClass
  3:     12726     1337184  <constMethodKlass>
  4:     12726     1021872  <methodKlass>
  5:       694      915336  [Ljava.lang.Object;
  6:     19443      781536  <symbolKlass>
  7:      1177      591128  <constantPoolKlass>
  8:      1177      456152  <instanceKlassKlass>
  9:      1117      393744  <constantPoolCacheKlass>
 10:      1360      246632  [B
 11:      3799      238040  [C
 12:     10042      160672  MemLeak$FinalizableObject
 13:      1321      126816  java.lang.Class
 14:      1740       98832  [S
 15:      4004       96096  java.lang.String
 < more .....>
 

jmap -histo 옵션은 프로세스 ID <pid>의 실행 응용 프로그램의 힙 막대 그래프를 요청합니다. jmap이 힙의 라이브 객체만 카운트하도록 live 하위 옵션을 지정할 수 있습니다. 연결할 수 없는 객체를 비롯한 모든 객체를 카운트하려면 다음 명령줄을 사용합니다.

$ <JDK>/bin/jmap -histo <pid>
 

두 힙 막대 그래프(하나는 연결할 수 없는 객체를 비롯한 모든 객체를 카운트 하는 그래프, 다른 하나는 라이브 객체만 카운트하는 그래프)를 비교하여 어떤 객체가 가비지 컬렉션될 것인지 확인하는 것이 유용한 경우가 있을 수 있습니다. 하나 이상의 힙 막대 그래프 스냅샷에서 일반적으로 다음과 같은 특성을 지니는 메모리 누출이 있을 수 있는 클래스를 식별할 수 있습니다.

  • 인스턴스가 예상치 않게 많은 양의 메모리를 사용함
  • 클래스의 인스턴스 수가 시간이 지남에 따라 높은 속도로 증가함
  • 가비지 컬렉션될 것으로 예상한 클래스 인스턴스가 가비지 컬렉션되지 않음

jmap 유틸리티를 사용하여 얻은 이전의 힙 막대 그래프가 LeakingClass 및 그 배열이 가장 많은 인스턴스 수를 가지는 것으로 나타나 메모리 누출이 의심됩니다.

힙 막대 그래프는 일부 경우 메모리 누출을 진단하는 데 필요한 정보를 제공합니다. 예를 들어, 응용 프로그램이 소수의 몇 군데에서만 누출 클래스를 사용하는 경우 소스 코드에서 쉽게 누출을 찾을 수 있습니다. 반면에 java.lang.String 클래스와 같이 응용 프로그램에서 누출 클래스가 널리 사용되는 경우 객체에 대한 참조를 추적하고 힙 덤프를 분석하여 심도 있게 진단해야 합니다.

힙 덤프

다음 방법을 통해 힙 덤프를 얻을 수 있습니다. 첫 번째 방법은 다음 명령줄을 통해 jmap 명령을 사용하여 힙 덤프를 얻는 것입니다.

$ <JDK>/bin/jmap -dump:live,file=heap.dump.out,format=b <pid>
 

이는 다음과 같은 표본 출력을 생성합니다.

Dumping heap to d:\demo\heap.dump.out ...
Heap dump file created
 

jmap -dump 옵션은 프로세스 ID <pid>의 실행 응용 프로그램의 힙 덤프가 지정된 파일 이름 heap.dump.out에 작성되도록 요청합니다. -histo 옵션과 유사하게 live 하위 옵션은 선택 사항이며 라이브 객체만이 덤프되도록 지정합니다.

두 번째 방법은 그림 2에 나타난 것처럼 HotSpotDiagnostic MBean의 dumpHeap 연산을 호출하여 JConsole에서 힙 덤프를 얻는 것입니다.

그림 2: HotSpotDiagnostic MBean의 <code>dumpHeap</code> 연산을 호출하여 JConsole에서 힙 덤프를 얻습니다.
그림 2: HotSpotDiagnostic MBean의 dumpHeap 연산을 호출하여 JConsole에서 힙 덤프를 얻습니다.
 

이는 JConsole을 사용하여 응용 프로그램을 모니터링할 때 특히 유용하고 편리한데 그 이유는 하나의 도구를 사용하여 모니터링 및 문제 해결을 수행할 수 있기 때문입니다. 또한 JConsole을 사용하여 응용 프로그램에 원격으로 연결할 수 있으므로 또 다른 시스템에서 힙 덤프를 요청할 수 있습니다.

지금까지 런타임에 힙 덤프를 얻을 수 있는 두 가지 방법에 대해 살펴보았습니다. HeapDumpOnOutOfMemoryError HotSpot VM 옵션을 설정하여 OutOfMemoryError가 처음 발생할 때 힙 덤프를 작성하도록 요청할 수도 있습니다. 응용 프로그램을 시작할 때 명령줄에 이 옵션을 설정할 수 있습니다.

$ <JDK>/bin/java -XX:+HeapDumpOnOutOfMemoryError ... 
 

다음과 같이 jinfo 명령을 사용하여 응용 프로그램이 실행되고 있는 동안 이 옵션을 설정할 수도 있습니다.

$ <JDK>/bin/jinfo -flag +HeapDumpOnOutMemoryError <pid>
 

마지막으로, 그림 3에 나타난 것처럼 HotSpotDiagnostic MBean의 setVMOption 연산을 호출하여 JConsole에서 HeapDumpOnOutOfMemoryError 옵션을 설정할 수 있습니다.

그림 3: HotSpotDiagnostic MBean의 <code>setVMOption</code> 연산을 호출하여 VM 옵션을 설정합니다.
그림 3: HotSpotDiagnostic MBean의 setVMOption 연산을 호출하여 VM 옵션을 설정합니다.
 

OutOfMemoryError가 발생하면 java_pid<pid>.hprof라는 이름의 힙 덤프 파일이 자동으로 작성됩니다.

java.lang.OutOfMemoryError: Java heap space
Dump heap to java_pid1412.hprof ...
Heap dump file created [68354173 bytes in 4.416 secs ]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at MemLeak.consumeMemory(MemLeak.java:25)
at MemLeak.main(MemLeak.java:6)
 

힙 분석

힙 덤프 파일이 작성되면 jhat 명령을 사용하여 힙 분석을 수행하고 어떤 참조가 누출이 의심되는 객체를 활성 상태로 유지하는지 확인할 수 있습니다.

$ <JDK>/bin/jhat heap.dump.out
 

이는 다음과 같은 표본 출력을 생성합니다.

Reading from heap.dump.out...
Dump file created Tue Jul 20 12:05:59 PDT 2006
Snapshot read, resolving...
Resolving 283482 objects...
Chasing references, expect 32 dots..........................................
Eliminating duplicate references............................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
 

jhat 유틸리티(이전의 HAT 힙 분석 도구)는 힙 덤프를 읽고 지정된 포트에서 HTTP 서버를 시작합니다. 그런 다음 브라우저를 사용하여 서버에 연결하고 지정된 힙 덤프에 대해 쿼리를 실행합니다. 그림 4는 jhat가 분석하는 힙 덤프의 모든 클래스(java.* 및 javax.* 제외)를 보여줍니다. 이 도구는 다음을 비롯한 여러 쿼리를 지원합니다.

  • 주어진 객체에 설정된 루트의 모든 참조 경로 표시. 이 쿼리는 메모리 누출을 찾는데 특히 유용합니다.
  • 모든 클래스에 대한 인스턴스 카운트 표시
  • 모든 클래스에 대한 인스턴스 카운트 및 크기를 비롯한 힙 막대 그래프 표시
  • 종료자 요약 표시
그림 4: 힙 덤프는 <code>java.*</code> 및 <code>javax.*</code>를 제외한 모든 클래스를 보여줍니다.
그림 4: 힙 덤프는 java.* 및 javax.*를 제외한 모든 클래스를 보여줍니다.
 

내장 OQL(Object Query Language) 인터페이스를 통해 사용자 정의 쿼리를 개발하여 특정 문제를 드릴 다운할 수도 있습니다. 예를 들어, 문자열 길이가 100 이상인 모든java.lang.String 객체를 찾으려면 OQL query 페이지에서 다음 쿼리를 입력합니다.

select s from java.lang.String s where s.count >= 100
 

과도한 종료자 사용 진단 방법

과도한 종료자 사용은 메모리를 유지하고 응용 프로그램이 해당 메모리를 신속하게 재생 이용하는 것을 방해합니다. 이러한 과도한 사용은 OutOfMemoryError를 유발할 수 있습니다. 그림 5가 나타낸 것처럼 JConsole을 사용하여 종료 대기 중인 객체 수를 모니터링할 수 있습니다.

그림 5: JConsole의 VM 탭은 종료 대기 중인 객체 수를 보여줍니다.
그림 5: JConsole의 VM 탭은 종료 대기 중인 객체 수를 보여줍니다.
 

앞에서 설명한 것처럼 jhat를 사용하여 힙 덤프에 어떤 종료 가능한 객체가 있는지도 확인할 수 있습니다.

또한 Solaris 및 Linux 운영 체제에서 jmap 유틸리티를 사용하여 종료 가능한 객체의 클래스를 확인할 수 있습니다.

$ <JDK>/bin/jmap -finalizerinfo <pid>
 

교착 상태 진단 방법

Java SE 6은 응용 프로그램에 교착 상태가 발생했는지 확인할 수 있는 매우 편리한 두 가지 방법을 제공하며 java.util.concurrent 잠금을 지원하도록 교착 상태 감지 기능이 향상되었습니다. JConsole 및 jstack 명령은 객체 모니터에 관련된 교착 상태 즉, synchronized 키워드 또는 java.util.concurrent 소유 가능한 동기화를 사용하여 얻은 잠금을 찾을 수 있습니다.

그림 6은 Deadlock 응용 프로그램에 2개의 교착 상태가 있음을 보여주고, Deadlock 2 탭은 객체 모니터에 차단된 3개의 교착 상태 스레드를 보여줍니다. 각 교착 상태 탭은 교착 상태에 관련된 스레드 목록을 보여주고 스레드가 차단된 잠금을 식별하며 해당 잠금을 소유한 스레드를 가리킵니다.

그림 6: JConsole이 2개의 교착 상태를 감지하고 세부 내용을 제공합니다.
그림 6: JConsole이 2개의 교착 상태를 감지하고 세부 내용을 제공합니다.
 

jstack 유틸리티를 사용하여 스레드 덤프를 얻고 교착 상태를 감지할 수도 있습니다.

$ <JDK>/bin/jstack <pid>
 

다음은 java.util.concurrent 소유 가능한 동기화에 관련된 교착 상태 1개를 감지하는 표본 jstack 출력의 아래 부분입니다.

Sample jstack output
더 큰 표본을 보려면 여기를 누르십시오.

루핑 스레드 진단 방법

CPU 사용 증가는 루핑 스레드의 징조 중 하나입니다. JTop은 응용 프로그램의 스레드당 CPU 사용 시간을 보여주는 JDK 데모입니다. JTop은 CPU 사용량별로 스레드를 정렬하여 CPU 시간을 과도하게 사용하고 있는 스레드를 쉽게 감지할 수 있게 해줍니다. 높은 스레드 CPU 소비가 예상된 동작이 아닌 경우 해당 스레드는 루핑일 수 있습니다.

JTop을 독립 실행형 GUI로 실행할 수 있습니다.

$ <JDK>/bin/java -jar <JDK>/demo/management/JTop/JTop.jar
 

또는 JConsole 플러그인으로 실행할 수 있습니다.

$ <JDK>/bin/jconsole -pluginpath <JDK>/demo/management/JTop/JTop.jar 
 

그림 7에 나타난 것처럼 응용 프로그램의 각 스레드가 사용하고 있는 CPU 시간을 보여주는 추가 JTop 탭을 통해 JConsole 도구를 시작합니다. JTop 탭은 LoopingThread가 많은 양의 CPU 시간을 사용하고 있고 사용량이 점점 증가하고 있음을 보여주며 따라서 루핑 스레드가 의심됩니다. 개발자는 이 스레드의 소스 코드를 검토하여 무한 루프가 포함되어 있는지 확인해야 합니다.

그림 7: JTop 탭은 응용 프로그램의 각 스레드가 얼마의 CPU 시간을 사용하는지 보여줍니다.
그림 7: JTop 탭은 응용 프로그램의 각 스레드가 얼마의 CPU 시간을 사용하는지 보여줍니다.

치열한 잠금 경합 진단 방법

어떤 잠금이 병목 현상인지 판단하는 것은 꽤 어려울 수 있습니다. JDK는 잠금 경합에 소비된 총 누적 시간뿐만 아니라 스레드가 객체 모니터에서 차단되거나 대기한 횟수와 같은 스레드당 경합 통계를 제공합니다. 그림 8에 나타난 것처럼 스레드가 객체 모니터에서 차단되거나 대기한 횟수에 대한 정보는 JConsole의 스레드 탭에 표시된 스레드 정보에서 항상 확인할 수 있습니다.

그림 8: 스레드 탭은 스레드가 객체 모니터에서 차단되거나 대기한 횟수를 보여줍니다.
그림 8: 스레드 탭은 스레드가 객체 모니터에서 차단되거나 대기한 횟수를 보여줍니다.
 

그러나 경합에 소비된 총 누적 시간을 추적하는 기능은 기본적으로 비활성화되어 있습니다. 그림 9에 나타나 것처럼 Threading MBean의ThreadContentionMonitoringEnabled 속성을 true로 설정하여 스레드 경합 시간의 모니터링을 활성화할 수 있습니다.

그림 9: Threading MBean의 <code>ThreadContentionMonitoringEnabled</code> 속성을 설정하여 스레드 경합의 모니터링을 활성화합니다.
그림 9: Threading MBean의 ThreadContentionMonitoringEnabled 속성을 설정하여 스레드 경합의 모니터링을 활성화합니다.
 

스레드 경합 통계를 확인하여 스레드에서 기대보다 치열한 잠금 경합이 발생했는지 판단할 수 있습니다. 그림 10에 나타난 것처럼 스레드 ID를 입력 인수로 사용하여 Threading MBean의 getThreadInfo 연산을 호출하여 스레드가 차단된 총 누적 시간을 얻을 수 있습니다.

그림 10: Threading MBean의 <code>getThreadInfo</code> 연산의 반환값입니다.
그림 10: Threading MBean의 getThreadInfo 연산의 반환값입니다.
 
요약

Java SE 6 플랫폼은 프로덕션 및 개발 환경에서 Java SE 응용 프로그램의 일반적인 문제를 진단할 수 있는 몇 가지 모니터링 및 관리 도구를 제공합니다. JConsole을 사용하면 응용 프로그램을 관찰하여 증상을 확인할 수 있습니다. 또한 JDK 6에는 몇 가지 기타 명령줄 도구도 포함되어 있습니다. jstat 명령은 메모리 사용 및 가비지 컬렉션 시간과 같은 다양한 VM 통계를 인쇄합니다. jmap 명령을 사용하면 런타임에 힙 막대 그래프 및 힙 덤프를 얻을 수 있습니다. jhat 명령을 사용하면 힙 덤프를 분석할 수 있습니다. 또한jstack 명령을 사용하면 스레드 스택을 추적할 수 있습니다. 이러한 진단 도구들은 특수 모드에서 시작할 필요 없이 모든 응용 프로그램에 연결할 수 있습니다. 이러한 도구들을 사용하여 응용 프로그램의 문제를 보다 효율적으로 진단할 수 있습니다.

추가 정보
저자 정보

Mandy Chung은 Sun Microsystems의 Java SE 모니터링 및 관리 팀장이며, java.lang.management API, 즉시 사용할 수 있는 원격 관리, JConsole 및기타 HotSpot VM 서비스가용성 기술을 개발하고 있습니다. 저자의 블로그를 방문하십시오.


* "Java Virtual Machine" 및 "JVM"이라는 용어는 Java 플랫폼용 가상 머신을 의미합니다.

 Java SE 6 빌드 95 이하 버전을 사용하는 경우 dumpHeap 연산은 첫 번째 인수만을 사용하며 라이브 객체만을 덤프합니다.

Posted by 1010