스레드를 잘못 사용하면 다양한 종류의 문제가 발생할 수 있습니다. 너무 많은 활성 스레드는 스레드 고갈(thread starvation)을 초래할 수 있고, 스레드들이 서로를 블록하여 애플리케이션의 활성도(liveness)에 영향을 주거나, 잘못된 순서로 lock을 획득하면 데드락(deadlock)이 발생할 수 있습니다. 또한, 스레드에 대한 정보는 디버깅 목적으로도 매우 중요합니다.
JProfiler에서 스레드 프로파일링은 두 개의 뷰 섹션으로 나뉩니다. "Threads" 섹션은 스레드의 라이프사이클과 스레드 덤프 캡처를 다루며, "Monitors & locks" 섹션은 여러 스레드 간의 상호작용을 분석하는 기능을 제공합니다.
스레드 검사하기
스레드 히스토리 뷰는 각 스레드를 타임라인의 색상 행으로 표시하며, 색상은 기록된 스레드 상태를 나타냅니다. 스레드는 생성 시점, 이름 또는 스레드 그룹별로 정렬할 수 있고, 이름으로 필터링할 수도 있습니다. 또한 드래그 앤 드롭으로 스레드의 순서를 직접 변경할 수 있습니다. monitor 이벤트가 기록된 경우, 스레드가 "Waiting" 또는 "Blocked" 상태였던 부분에 마우스를 올리면 해당 스택 트레이스와 monitor 히스토리 뷰로 이동하는 링크를 볼 수 있습니다.
모든 스레드의 표 형식 뷰는 thread monitor 뷰에서 확인할 수 있습니다. 스레드가 생성되는 동안 CPU 녹화가 활성화되어 있으면, JProfiler는 생성한 스레드의 이름을 저장하고 테이블에 표시합니다. 하단에는 생성한 스레드의 스택 트레이스가 표시됩니다. 성능상의 이유로 JVM에서 실제 스택 트레이스를 요청하지 않고, CPU 녹화의 현재 정보를 사용합니다. 즉, 스택 트레이스에는 호출 트리 수집의 필터 설정을 만족하는 클래스만 표시됩니다.
프로파일링 설정에서 추정 CPU 시간 기록을 활성화하면, 테이블에 CPU Time 컬럼이 추가됩니다. CPU 시간은 CPU 데이터를 기록할 때만 측정됩니다.
대부분의 디버거와 마찬가지로 JProfiler도 스레드 덤프를 생성할 수 있습니다. 스레드 덤프의 스택 트레이스는 JVM에서 제공하는 전체 스택 트레이스이며, CPU 녹화와는 무관합니다. 서로 다른 스레드 덤프는 두 개를 선택한 후 Show Difference 버튼을 클릭하면 diff 뷰어에서 비교할 수 있습니다. 또한, 하나의 스레드 덤프에서 두 개의 스레드를 선택한 후 컨텍스트 메뉴에서 Show Difference를 선택하여 비교할 수도 있습니다.
동일한 스택 트레이스를 가진 스레드 덤프는 그룹화됩니다. 플랫폼 스레드 덤프에서 이러한 노드에 대해 유사한 모든 스레드를 표시하는 컨텍스트 메뉴 액션이 제공됩니다.
Java 21 이상을 프로파일링할 때, thread dumps 뷰에서 "Take thread dump with virtual threads"라는 별도의 액션을 사용할 수 있습니다. 플랫폼 스레드도 virtual thread dump에 포함됩니다.
스레드 덤프는 "Trigger thread dump" 트리거 액션이나 API를 통해서도 생성할 수 있습니다.
락킹 상황 분석하기
모든 Java 객체에는 두 가지 동기화 작업에 사용할 수 있는 monitor가 연결되어 있습니다. 스레드는 monitor에서 대기(wait)할 수 있고, 다른 스레드가 해당 monitor에서
notify를 호출하면 깨어납니다. 또는 monitor에 lock을 획득(acquire)할 수 있으며, 이때 다른 스레드가 lock을 소유하고 있으면 블록될 수 있습니다. 또한, Java는
java.util.concurrent.locks 패키지에서 더 고급 락킹 전략을 위한 클래스를 제공합니다. 이 패키지의 lock은 객체의 monitor를 사용하지 않고 별도의
네이티브 구현을 사용합니다.
JProfiler는 위 두 가지 메커니즘 모두에 대해 락킹 상황을 기록할 수 있습니다. 락킹 상황에는 하나 이상의 스레드, monitor 또는
java.util.concurrent.locks.Lock의 인스턴스, 그리고 일정 시간 동안 대기 또는 블록되는 작업이 포함됩니다. 이러한 락킹 상황은 monitor 히스토리
뷰에서는 표 형식으로, 락킹 히스토리 그래프에서는 시각적으로 표시됩니다.
락킹 히스토리 그래프는 개별 monitor 이벤트의 지속 시간보다는 관련된 모든 monitor와 스레드의 전체 관계에 초점을 맞춥니다. 락킹 상황에 참여하는 스레드와 monitor는 각각 파란색과 회색 사각형으로 표시되며, 데드락에 포함된 경우 빨간색으로 표시됩니다. 검은색 화살표는 monitor의 소유권을 나타내고, 노란색 화살표는 대기 중인 스레드에서 관련 monitor로 이어집니다. 점선 빨간색 화살표는 스레드가 monitor를 획득하려고 시도 중이며 현재 블록되고 있음을 나타냅니다. CPU 데이터가 기록된 경우, 블록 또는 대기 화살표에 마우스를 올리면 스택 트레이스를 볼 수 있습니다. 해당 툴팁에는 monitor 히스토리 뷰의 해당 행으로 이동하는 하이퍼링크가 포함되어 있습니다.
표 형식 monitor 히스토리 뷰는 monitor 이벤트를 보여줍니다. 각 이벤트는 컬럼으로 표시되는 지속 시간을 가지므로, 테이블을 정렬하여 가장 중요한 이벤트를 쉽게 찾을 수 있습니다. 표 뷰에서 행을 선택하면 Show in Graph 액션으로 그래프로 바로 이동할 수 있습니다.
각 monitor 이벤트에는 연결된 monitor가 있습니다. Monitor Class 컬럼은 monitor로 사용된 인스턴스의 클래스 이름을 표시하며, Java 객체와 연결되지 않은 경우 "[raw monitor]"로 표시됩니다. monitor는 고유 ID를 가지며, 별도의 컬럼에 표시되어 여러 이벤트에서 동일 monitor의 사용을 연관시킬 수 있습니다. 각 monitor 이벤트에는 작업을 수행하는 대기 스레드와(필요시) 작업을 블록하는 소유 스레드가 있습니다. 해당 정보가 있으면, 스택 트레이스가 뷰 하단에 표시됩니다.
monitor 인스턴스에 대해 추가로 궁금한 점이 있다면, monitor 히스토리 뷰와 락킹 히스토리 그래프 모두에서 Show in Heap Walker 액션을 통해 heap walker로 이동하여 monitor 인스턴스를 새로운 객체 집합으로 선택할 수 있습니다.
관심 이벤트 제한하기
monitor 이벤트를 분석할 때의 근본적인 문제는 애플리케이션이 monitor 이벤트를 매우 빠른 속도로 생성할 수 있다는 점입니다. 그래서 JProfiler는 기본적으로 대기 및 블록 이벤트에 임계값(threshold)을 두어, 그 이하의 이벤트는 즉시 폐기합니다. 이 임계값은 뷰 설정에서 정의되어 있으며, 더 긴 이벤트에 집중하고 싶을 때 값을 높일 수 있습니다.
기록된 이벤트에 추가로 필터를 적용할 수 있습니다. monitor 히스토리 뷰 상단에는 임계값, 이벤트 타입, 텍스트 필터가 제공됩니다. 락킹 히스토리 그래프에서는 관심 있는 스레드나 monitor를 선택하여 해당 엔티티가 포함된 락킹 상황만 표시할 수 있습니다. 관심 이벤트는 타임라인에서 다른 색상으로 표시되며, 해당 이벤트만 탐색할 수 있는 보조 네비게이션 바가 있습니다. 현재 이벤트가 관심 이벤트가 아닌 경우, 현재 이벤트와 다음 관심 이벤트 사이에 몇 개의 이벤트가 있는지 양 방향 모두에서 확인할 수 있습니다.
선택한 스레드나 monitor가 포함된 락킹 상황뿐만 아니라, 그래프에서 제거된 경우의 락킹 상황도 표시됩니다. 이는 각 monitor 이벤트가 두 개의 락킹 상황(작업이 시작되는 시점과 끝나는 시점)으로 정의되기 때문입니다. 완전히 비어 있는 그래프도 JVM에 더 이상 lock이 없음을 나타내는 유효한 락킹 상황입니다.
관심이 필요한 이벤트 수를 줄이는 또 다른 전략은 락킹 상황을 누적(cumulate)하는 것입니다. 락킹 히스토리 그래프 하단에는 모든 기록된 이벤트를 보여주는 타임라인이 있습니다. 이 타임라인에서 클릭 후 드래그하면 시간 범위가 선택되고, 해당 범위에 포함된 모든 이벤트의 데이터가 위의 락킹 그래프에 표시됩니다. 누적 그래프에서는 각 화살표가 동일 타입의 여러 이벤트를 포함할 수 있습니다. 이 경우, 툴팁 창에는 이벤트 수와 전체 소요 시간이 표시됩니다. 드롭다운 목록에서 타임스탬프를 선택해 각 이벤트로 전환할 수 있습니다.
데드락 감지
"Current locking graph"와 "Current monitors" 뷰는 JProfiler UI에서 액션으로 트리거되는 "monitor dump"를 기반으로 동작합니다. monitor dump를 통해 아직 진행 중인 이벤트를 검사할 수 있습니다. 여기에는 히스토리 뷰에서 표시할 수 없는, 끝나지 않는 이벤트인 데드락도 포함됩니다.
블록 작업은 일반적으로 짧게 끝나지만, 데드락이 발생하면 두 뷰 모두 해당 문제를 영구적으로 표시합니다. 또한, 현재 락킹 그래프에서는 데드락을 일으키는 스레드와 monitor를 빨간색으로 표시하여 즉시 문제를 파악할 수 있습니다.
새로운 monitor dump를 수행하면 두 뷰의 데이터가 교체됩니다. "Trigger monitor dump" 트리거 액션이나 API를 통해서도 monitor dump를 트리거할 수 있습니다.
Monitor 사용 통계
블록 및 대기 작업을 더 높은 관점에서 조사하기 위해 monitor 통계 뷰는 monitor 기록 데이터로부터 리포트를 생성합니다. monitor 이벤트를 monitor, 스레드 이름, 또는 monitor의 클래스별로 그룹화하여 각 행에 대해 누적된 횟수와 지속 시간을 분석할 수 있습니다.























