JProfiler 도움말

CPU 프로파일링

JProfiler가 메서드 호출의 실행 시간과 호출 스택을 함께 측정할 때 이를 "CPU 프로파일링"이라고 합니다. 이 데이터는 다양한 방식으로 제공됩니다. 해결하려는 문제에 따라 각각의 표현 방식이 가장 도움이 될 수 있습니다. CPU 데이터는 기본적으로 기록되지 않으므로, CPU 녹화를 켜서 흥미로운 use case를 캡처해야 합니다.

호출 트리

모든 메서드 호출과 그 호출 스택을 추적하면 상당한 양의 메모리가 소모되며, 모든 메모리가 소진될 때까지 짧은 시간만 유지할 수 있습니다. 또한, 바쁜 JVM에서 메서드 호출의 수를 직관적으로 파악하기도 쉽지 않습니다. 일반적으로 그 수가 너무 많아 추적하고 따라가는 것이 불가능합니다.

또 다른 측면은 많은 성능 문제가 수집된 데이터를 집계해야만 명확해진다는 점입니다. 이렇게 하면 특정 시간 동안 전체 활동에서 메서드 호출이 얼마나 중요한지 알 수 있습니다. 단일 추적만으로는 데이터의 상대적 중요성을 파악할 수 없습니다.

이러한 이유로 JProfiler는 관찰된 모든 호출 스택을 누적 트리로 구성하고, 관찰된 타이밍과 호출 횟수로 주석을 답니다. 시간 순서는 제거되고 전체 수치만 유지됩니다. 트리의 각 노드는 최소 한 번 이상 관찰된 호출 스택을 나타냅니다. 노드는 해당 호출 스택에서 관찰된 모든 아웃고잉 호출을 자식으로 가집니다.

A B  A B C    A B DA: 7 msB: 6 msC: 3 msD: 1 msA C  2 ms1 ms3 ms1 msC: 1 ms메서드 호출과 호출 스택호출 트리

호출 트리는 "CPU 뷰" 섹션의 첫 번째 뷰이며, CPU 프로파일링을 시작할 때 좋은 출발점입니다. 시작점부터 가장 세분화된 끝점까지 메서드 호출을 따라가는 top-down 뷰는 가장 이해하기 쉽습니다. JProfiler는 자식 노드를 총 시간 기준으로 정렬하므로, 트리를 깊이 우선으로 열어 성능에 가장 큰 영향을 미치는 부분을 분석할 수 있습니다.

모든 측정은 메서드 단위로 수행되지만, JProfiler는 호출 트리를 클래스 또는 패키지 수준으로 집계하여 더 넓은 관점에서 볼 수 있도록 합니다. 집계 수준 선택기에는 "JEE/Spring 컴포넌트" 모드도 포함되어 있습니다. 애플리케이션이 JEE 또는 Spring을 사용한다면, 이 모드를 통해 클래스 수준에서 JEE 및 Spring 컴포넌트만 볼 수 있습니다. URL과 같은 분할 노드는 모든 집계 수준에서 유지됩니다.

호출 트리 필터

호출 트리에 모든 클래스의 메서드가 표시되면, 트리가 너무 깊어 관리하기 어렵습니다. 애플리케이션이 프레임워크에 의해 호출되는 경우, 호출 트리의 상단은 관심 없는 프레임워크 클래스들로 구성되고, 자신의 클래스는 깊이 묻히게 됩니다. 라이브러리로의 호출은 내부 구조를 보여주며, 수백 단계의 익숙하지 않은 메서드 호출이 나타나고, 이를 직접 제어할 수도 없습니다.

이 문제의 해결책은 호출 트리에 필터를 적용하여 일부 클래스만 기록되도록 하는 것입니다. 긍정적인 부수 효과로, 수집해야 할 데이터가 줄어들고, 계측해야 할 클래스 수도 감소하여 오버헤드가 줄어듭니다.

기본적으로 프로파일링 세션은 자주 사용되는 프레임워크와 라이브러리의 제외 패키지 목록으로 구성됩니다.

물론 이 목록은 완전하지 않으므로, 직접 삭제하고 관심 있는 패키지를 정의하는 것이 훨씬 좋습니다. 실제로 계측과 기본 필터의 조합은 바람직하지 않으므로, JProfiler는 세션 시작 대화상자에서 변경을 권장합니다.

필터 표현식은 완전한 클래스 이름과 비교되므로, com.mycorp.com.mycorp.myapp.Application과 같이 모든 하위 패키지의 클래스를 매칭합니다. 필터에는 "프로파일된", "compact", "ignored"의 세 가지 유형이 있습니다. "프로파일된" 클래스의 모든 메서드는 측정됩니다. 이는 자신의 코드에 필요합니다.

"compact" 필터에 포함된 클래스에서는 해당 클래스로의 첫 번째 호출만 측정되며, 그 이후 내부 호출은 표시되지 않습니다. "compact"는 JRE를 포함한 라이브러리에 적합합니다. 예를 들어, hashMap.put(a, b)를 호출할 때 호출 트리에서 HashMap.put()만 보고 싶고, 그 내부 동작은 map 구현체의 개발자가 아니라면 굳이 볼 필요가 없습니다.

마지막으로, "ignored" 메서드는 전혀 프로파일링되지 않습니다. 오버헤드 문제로 계측이 바람직하지 않거나, 호출 트리에서 방해가 되는 경우(예: 동적 호출 사이에 삽입되는 Groovy 내부 메서드 등) 해당됩니다.

패키지를 수동으로 입력하는 것은 오류가 발생하기 쉬우므로, 패키지 브라우저를 사용할 수 있습니다. 세션을 시작하기 전에는, 패키지 브라우저는 구성된 클래스패스에 있는 패키지만 보여주며, 실제로 로드된 모든 클래스를 포함하지 않을 수 있습니다. 런타임에는 패키지 브라우저가 모든 로드된 클래스를 보여줍니다.

구성된 필터 목록은 각 클래스에 대해 위에서 아래로 평가됩니다. 각 단계에서 현재 필터 유형은 매칭이 있으면 변경될 수 있습니다. 필터 목록의 시작 필터가 무엇인지가 중요합니다. "프로파일된" 필터로 시작하면, 클래스의 초기 필터 유형은 "compact"이므로 명시적으로 매칭되는 경우만 프로파일링됩니다.

a.*a.b.*a.b.c.*a.Aa.b.Ba.b.c.C기본값:123결과: 결과d.D프로파일된 클래스간소화 필터일치

"compact" 필터로 시작하면, 클래스의 초기 필터 유형은 "프로파일된"이 됩니다. 이 경우, 명시적으로 제외된 클래스를 제외하고 모든 클래스가 프로파일링됩니다.

a.*a.b.*a.b.c.*a.Aa.b.Ba.b.c.C기본값:123결과: 결과d.D프로파일된 클래스간소화 필터일치

호출 트리 시간

호출 트리를 올바르게 해석하려면, 호출 트리 노드에 표시되는 숫자를 이해하는 것이 중요합니다. 각 노드에서 관심 있는 시간은 총 시간과 자체 시간(self-time) 두 가지입니다. 자체 시간은 해당 노드의 총 시간에서 중첩된 노드의 총 시간을 뺀 값입니다.

일반적으로 자체 시간은 작지만, compact 필터가 적용된 클래스에서는 예외입니다. 대부분 compact 필터 클래스는 리프 노드이므로, 총 시간과 자체 시간이 동일합니다. 때로는 compact 필터 클래스가 콜백이나 호출 트리의 진입점(예: 현재 스레드의 run 메서드)처럼 프로파일된 클래스를 호출할 수도 있습니다. 이 경우, 프로파일링되지 않은 메서드가 소모한 시간은 호출 트리에 표시되지 않지만, 그 시간은 호출 트리에서 첫 번째로 나타나는 상위 노드로 전달되어 compact 필터 클래스의 자체 시간에 더해집니다.

A: 자체 시간 1 msC: 자체 시간 3 msB: 자체 시간 2 ms프로파일된 클래스간소화 필터B: 자체 시간 6 msX: 자체 시간 3 msY: 자체 시간 1 ms실제 호출 순서필터링된 호출 순서

호출 트리의 퍼센트 바는 총 시간을 표시하지만, 자체 시간 부분은 다른 색상으로 표시됩니다. 메서드는 같은 레벨에 오버로드된 두 메서드가 없는 한 시그니처 없이 표시됩니다. 호출 트리 노드의 표시 방법은 뷰 설정 대화상자에서 다양하게 커스터마이즈할 수 있습니다. 예를 들어, 자체 시간이나 평균 시간을 텍스트로 표시하거나, 항상 메서드 시그니처를 표시하거나, 사용되는 시간 단위를 변경할 수 있습니다. 또한, 퍼센트 계산을 전체 호출 트리 대신 부모 시간 기준으로 할 수도 있습니다.

스레드 상태

호출 트리 상단에는 표시되는 프로파일링 데이터의 유형과 범위를 변경하는 여러 뷰 파라미터가 있습니다. 기본적으로 모든 스레드가 누적되어 표시됩니다. JProfiler는 스레드별로 CPU 데이터를 유지하며, 단일 스레드나 스레드 그룹을 표시할 수 있습니다.

항상 각 스레드에는 연결된 스레드 상태가 있습니다. 스레드가 바이트코드 명령을 처리할 준비가 되었거나 CPU 코어에서 실행 중이면, 스레드 상태는 "Runnable"입니다. 이 상태는 성능 병목을 찾을 때 중요하므로 기본적으로 선택되어 있습니다.

또는, 스레드가 monitor에서 대기 중일 수도 있습니다. 예를 들어 Object.wait()Thread.sleep()을 호출하는 경우 스레드 상태는 "Waiting"입니다. synchronized 코드 블록 경계에서 monitor를 획득하려다 블록된 경우에는 "Blocking" 상태가 됩니다.

마지막으로, JProfiler는 스레드가 네트워크 데이터를 기다리는 시간을 추적하는 합성 "Net I/O" 상태를 추가합니다. 이는 서버나 데이터베이스 드라이버 분석에 중요하며, 느린 SQL 쿼리 조사 등 성능 분석에 유용합니다.

wall-clock 시간에 관심이 있다면, 스레드 상태를 "All states"로 선택하고 단일 스레드도 선택해야 합니다. 그래야만 System.currentTimeMillis()로 계산한 시간과 비교할 수 있습니다.

선택한 메서드를 다른 스레드 상태로 이동시키고 싶다면, 메서드 트리거와 "스레드 상태 재정의" 트리거 액션을 사용하거나, 임베디드 또는 인젝티드 프로브 API에서 ThreadStatus 클래스를 사용할 수 있습니다.

호출 트리에서 노드 찾기

호출 트리에서 텍스트를 검색하는 방법은 두 가지가 있습니다. 첫 번째는 View→Find 메뉴를 실행하거나 호출 트리에서 바로 타이핑하여 활성화되는 빠른 검색(quicksearch) 옵션입니다. 매칭 결과가 하이라이트되고, PageDown을 누르면 검색 옵션이 제공됩니다. ArrowUpArrowDown 키로 서로 다른 매칭 결과를 순환할 수 있습니다.

또 다른 방법은 호출 트리 하단의 뷰 필터를 사용하는 것입니다. 여기서는 쉼표로 구분된 필터 표현식을 입력할 수 있습니다. "-"로 시작하는 표현식은 ignored 필터와 같습니다. "!"로 시작하는 표현식은 compact 필터와 같습니다. 그 외의 표현식은 profiled 필터와 같습니다. 필터 설정과 마찬가지로, 초기 필터 유형에 따라 클래스가 기본적으로 포함 또는 제외됩니다.

뷰 설정 텍스트 필드 왼쪽의 아이콘을 클릭하면 뷰 필터 옵션이 표시됩니다. 기본 매칭 모드는 "포함(Contains)"이지만, 특정 패키지를 찾을 때는 "시작(Starts with)"이 더 적합할 수 있습니다.

Flame 그래프

호출 트리를 flame 그래프로 볼 수도 있습니다. 전체 호출 트리 또는 일부를 flame 그래프로 표시하려면 관련 호출 트리 분석을 실행하면 됩니다.

flame 그래프는 호출 트리의 전체 내용을 하나의 이미지로 보여줍니다. 호출은 flame 그래프의 하단에서 시작하여 상단으로 전파됩니다. 각 노드의 자식은 바로 위의 행에 정렬됩니다. 자식 노드는 알파벳 순으로 정렬되고, 부모 노드 중앙에 배치됩니다. 각 노드에서 소비된 자체 시간 때문에 "불꽃"은 위로 갈수록 점점 더 좁아집니다. 노드에 대한 추가 정보는 툴팁에 표시되며, 텍스트를 선택해 클립보드로 복사할 수 있습니다.

마우스 커서 근처의 툴팁이 분석을 방해한다면, 오른쪽 상단의 버튼으로 툴팁을 고정하고 상단의 그립을 이용해 원하는 위치로 이동할 수 있습니다. 동일한 버튼이나 flame 그래프에서 더블 클릭하면 툴팁이 닫힙니다.

flame 그래프는 정보 밀도가 매우 높으므로, 선택한 노드와 그 하위 계층에 집중하여 표시되는 내용을 좁힐 필요가 있습니다. 관심 영역을 확대(zoom)할 수도 있고, 노드를 더블 클릭하거나 컨텍스트 메뉴를 사용해 새로운 루트 노드를 설정할 수도 있습니다. 루트를 여러 번 변경한 경우, 루트 변경 이력을 따라 다시 돌아갈 수 있습니다.

flame 그래프를 분석하는 또 다른 방법은 클래스명, 패키지명 또는 임의의 검색어로 색상화를 추가하는 것입니다. 색상화는 컨텍스트 메뉴에서 추가할 수 있고, 색상화 대화상자에서 관리할 수 있습니다. 각 노드에는 첫 번째로 매칭되는 색상화가 적용됩니다. 색상화는 프로파일링 세션 간에 유지되며, 모든 세션과 스냅샷에 전역적으로 사용됩니다.

색상화 외에도, 빠른 검색 기능을 사용해 관심 있는 노드를 찾을 수 있습니다. 커서 키로 매칭 결과를 순환할 수 있으며, 현재 하이라이트된 매칭에 대해 툴팁이 표시됩니다.

핫스팟

애플리케이션이 너무 느리게 동작한다면, 가장 많은 시간을 소비하는 메서드를 찾아야 합니다. 호출 트리로 직접 찾을 수 있는 경우도 있지만, 트리가 너무 넓고 리프 노드가 많으면 쉽지 않습니다.

이럴 때는 호출 트리의 역방향, 즉 모든 호출 스택에서 누적된 자체 시간 기준으로 정렬된 메서드 목록과, 해당 메서드가 어떻게 호출되었는지 보여주는 백트레이스가 필요합니다. 핫스팟 트리에서는 리프가 진입점(예: 애플리케이션의 main 메서드나 스레드의 run 메서드)입니다. 핫스팟 트리의 가장 깊은 노드에서 호출이 상위 노드로 전파됩니다.

백트레이스의 호출 횟수와 실행 시간은 메서드 노드가 아니라, 해당 경로를 따라 최상위 핫스팟 노드가 호출된 횟수와 관련이 있습니다. 즉, 노드의 정보가 그 노드로의 호출 횟수를 의미할 것 같지만, 실제로는 기여도를 나타냅니다. 따라서 숫자는 이렇게 해석해야 합니다: 이 역방향 호출 스택을 따라 최상위 핫스팟이 n회 호출되었고, 총 소요 시간은 t초입니다.

메서드 A개수 5메서드 C개수 3메서드 C개수 1메서드 B개수 2호출 트리핫스팟메서드 C개수 4메서드 A개수 3메서드 B개수 1백트레이스핫스팟호출횟수반전

기본적으로 핫스팟은 자체 시간 기준으로 계산됩니다. 총 시간 기준으로도 계산할 수 있습니다. 이는 성능 병목 분석에는 크게 유용하지 않지만, 모든 메서드 목록을 보고 싶을 때는 흥미로울 수 있습니다. 핫스팟 뷰는 오버헤드를 줄이기 위해 최대 메서드 수만 표시하므로, 찾고 있는 메서드가 표시되지 않을 수 있습니다. 이 경우 하단의 뷰 필터로 패키지나 클래스를 필터링하세요. 호출 트리와 달리, 핫스팟 뷰 필터는 최상위 노드만 필터링합니다. 핫스팟 뷰의 컷오프는 전역이 아니라 표시된 클래스 기준이므로, 필터 적용 후 새로운 노드가 나타날 수 있습니다.

핫스팟과 필터

핫스팟의 개념은 절대적인 것이 아니라 호출 트리 필터에 따라 달라집니다. 호출 트리 필터가 전혀 없다면, 가장 큰 핫스팟은 JRE의 핵심 클래스(문자열 처리, I/O 루틴, 컬렉션 연산 등)일 것입니다. 이런 핫스팟은 직접 호출을 제어할 수 없고, 성능 개선 방법도 없으므로 그다지 유용하지 않습니다.

유용한 핫스팟은 자신의 클래스에 있는 메서드이거나, 직접 호출하는 라이브러리 클래스의 메서드여야 합니다. 호출 트리 필터 기준으로, 자신의 클래스는 "프로파일된" 필터에, 라이브러리 클래스는 "compact" 필터에 해당합니다.

성능 문제를 해결할 때, 라이브러리 계층을 제외하고 자신의 클래스만 보고 싶을 수 있습니다. 호출 트리에서 핫스팟 옵션 팝업의 호출한 프로파일된 클래스에 추가 라디오 버튼을 선택하면 이 관점으로 빠르게 전환할 수 있습니다.

호출 그래프

호출 트리와 핫스팟 뷰 모두에서 각 노드는 여러 번 나타날 수 있으며, 특히 재귀 호출이 있을 때 그렇습니다. 어떤 상황에서는 각 메서드가 한 번만 나타나고, 모든 인커밍/아웃고잉 호출이 보이는 메서드 중심 통계가 필요할 수 있습니다. 이런 뷰는 그래프로 표시하는 것이 가장 좋으며, JProfiler에서는 이를 호출 그래프(call graph)라고 합니다.

그래프의 단점은 트리보다 시각적 밀도가 낮다는 점입니다. 그래서 JProfiler는 기본적으로 패키지명을 축약하고, 총 시간의 1% 미만인 아웃고잉 호출은 숨깁니다. 노드에 아웃고잉 확장 아이콘이 있으면, 다시 클릭해 모든 호출을 표시할 수 있습니다. 뷰 설정에서 이 임계값을 조정하거나 패키지 축약을 끌 수 있습니다.

호출 그래프를 확장하다 보면, 특히 여러 번 백트랙할 경우 금방 복잡해질 수 있습니다. undo 기능을 활용해 그래프의 이전 상태로 복원하세요. 호출 트리와 마찬가지로, 호출 그래프도 빠른 검색을 제공합니다. 그래프에서 타이핑하면 검색이 시작됩니다.

그래프와 트리 뷰는 각각 장단점이 있으므로, 상황에 따라 뷰 타입을 전환하고 싶을 수 있습니다. 인터랙티브 세션에서는 호출 트리와 핫스팟 뷰가 라이브 데이터를 보여주며 주기적으로 갱신됩니다. 호출 그래프는 요청 시 계산되며, 노드를 확장해도 변경되지 않습니다. 호출 트리의 호출 그래프에서 보기 액션을 사용하면 새 호출 그래프를 계산해 선택한 메서드를 보여줍니다.

그래프에서 호출 트리로 전환하는 것은 불가능합니다. 왜냐하면 시간이 지난 후에는 데이터가 더 이상 비교 가능하지 않기 때문입니다. 하지만 호출 그래프는 View→Analyze 액션을 통해 누적된 아웃고잉 호출 트리와 각 선택 노드의 백트레이스를 보여주는 호출 트리 분석을 제공합니다.

기본을 넘어서

호출 트리, 핫스팟 뷰, 호출 그래프의 조합에는 다른 챕터에서 자세히 설명되는 다양한 고급 기능이 있습니다. 또한, 별도의 고급 CPU 뷰도 제공됩니다.