CPU 프로파일링
JProfiler가 메서드 호출의 실행 시간과 호출 스택을 측정할 때, 이를 "CPU 프로파일링"이라고 합니다. 이 데이터는 다양한 방식으로 제공됩니다. 해결하려는 문제에 따라, 어느 한 가지 표현이 가장 유용할 것입니다. CPU 데이터는 기본적으로 기록되지 않으며, 흥미로운 사용 사례를 캡처하려면 CPU 기록을 켜야 합니다.
호출 트리
모든 메서드 호출과 호출 스택을 추적하면 상당한 양의 메모리를 소비하며, 모든 메모리가 소진될 때까지 짧은 시간 동안만 유지할 수 있습니다. 또한, 바쁜 JVM에서 메서드 호출 수를 직관적으로 파악하기 쉽지 않습니다. 일반적으로 그 수가 너무 많아 추적을 찾고 따르는 것이 불가능합니다.
또 다른 측면은 많은 성능 문제가 수집된 데이터가 집계될 때만 명확해진다는 것입니다. 이렇게 하면 특정 시간 기간 동안 전체 활동에 대해 메서드 호출이 얼마나 중요한지 알 수 있습니다. 단일 추적으로는 보고 있는 데이터의 상대적 중요성을 알 수 없습니다.
이러한 이유로 JProfiler는 관찰된 모든 호출 스택의 누적 트리를 구축하고, 관찰된 타이밍과 호출 횟수로 주석을 달아줍니다. 시간적 측면은 제거되고 총 숫자만 유지됩니다. 트리의 각 노드는 적어도 한 번 이상 관찰된 호출 스택을 나타냅니다. 노드는 해당 호출 스택에서 본 모든 아웃고잉 호출을 나타내는 자식을 가지고 있습니다.
호출 트리는 "CPU 뷰" 섹션의 첫 번째 뷰이며, CPU 프로파일링을 시작할 때 좋은 출발점입니다. 메서드 호출을 시작점에서 가장 세분화된 끝점까지 따라가는 상향식 뷰가 가장 쉽게 이해되기 때문입니다. JProfiler는 자식을 총 시간으로 정렬하므로, 트리를 깊이 우선으로 열어 성능에 가장 큰 영향을 미치는 트리 부분을 분석할 수 있습니다.
모든 측정은 메서드에 대해 수행되지만, JProfiler는 클래스 또는 패키지 수준에서 호출 트리를 집계하여 더 넓은 관점을 제공할 수 있습니다. 집계 수준 선택기에는 "JEE/Spring 구성 요소" 모드도 포함되어 있습니다. 애플리케이션이 JEE 또는 Spring을 사용하는 경우, 이 모드를 사용하여 클래스 수준에서 JEE 및 Spring 구성 요소만 볼 수 있습니다. URL과 같은 분할 노드는 모든 집계 수준에서 유지됩니다.
호출 트리 필터
모든 클래스의 메서드가 호출 트리에 표시되면 트리가 너무 깊어 관리하기 어려울 수 있습니다. 애플리케이션이 프레임워크에 의해 호출되는 경우, 호출 트리의 상단은 관심 없는 프레임워크 클래스들로 구성되며, 자신의 클래스는 깊이 묻힐 것입니다. 라이브러리로의 호출은 내부 구조를 보여주며, 수백 개의 메서드 호출 레벨이 있을 수 있으며, 이는 익숙하지 않고 영향을 미칠 수 없는 것입니다.
이 문제의 해결책은 호출 트리에 필터를 적용하여 일부 클래스만 기록되도록 하는 것입니다. 긍정적인 부작용으로는 수집해야 할 데이터가 줄어들고, 계측해야 할 클래스가 줄어들어 오버헤드가 감소합니다.
기본적으로 프로파일링 세션은 일반적으로 사용되는 프레임워크 및 라이브러리에서 제외된 패키지 목록으로 구성됩니다.
물론 이 목록은 불완전하므로 삭제하고 관심 있는 패키지를 직접 정의하는 것이 훨씬 좋습니다. 실제로 계측과 기본 필터의 조합은 매우 바람직하지 않으므로 JProfiler는 세션 시작 대화 상자에서 이를 변경할 것을 제안합니다.
필터 표현식은 완전한 클래스 이름과 비교되므로 com.mycorp.
는 com.mycorp.myapp.Application
과 같은 모든 중첩 패키지의
클래스를 일치시킵니다. 필터에는 "프로파일된", "압축", "무시됨"이라는 세 가지 유형이 있습니다. "프로파일된" 클래스의 모든 메서드는 측정됩니다. 이는 자신의 코드에 필요합니다.
"압축" 필터에 포함된 클래스에서는 해당 클래스에 대한 첫 번째 호출만 측정되며, 추가 내부 호출은 표시되지 않습니다. "압축"은 JRE를 포함한 라이브러리에 적합합니다. 예를 들어,
hashMap.put(a, b)
를 호출할 때 호출 트리에서 HashMap.put()
을 보고 싶지만 그 이상은 보고 싶지 않습니다. 내부 작동은 맵 구현의
개발자가 아닌 한 불투명하게 처리되어야 합니다.
마지막으로, "무시됨" 메서드는 전혀 프로파일링되지 않습니다. 오버헤드 고려 사항으로 인해 계측이 바람직하지 않을 수 있으며, 동적 호출 사이에 삽입된 내부 Groovy 메서드와 같이 호출 트리에서 단순히 방해가 될 수 있습니다.
패키지를 수동으로 입력하는 것은 오류가 발생하기 쉬우므로 패키지 브라우저를 사용할 수 있습니다. 세션을 시작하기 전에 패키지 브라우저는 구성된 클래스 경로에 있는 패키지만 표시할 수 있으며, 이는 종종 실제로 로드된 모든 클래스를 포함하지 않습니다. 런타임 시 패키지 브라우저는 로드된 모든 클래스를 표시합니다.
구성된 필터 목록은 각 클래스에 대해 위에서 아래로 평가됩니다. 각 단계에서 일치하는 경우 현재 필터 유형이 변경될 수 있습니다. 필터 목록의 시작 필터 유형이 중요합니다. "프로파일된" 필터로 시작하면 클래스의 초기 필터 유형은 "압축"이며, 명시적인 일치 항목만 프로파일링됩니다.
"압축" 필터로 시작하면 클래스의 초기 필터 유형은 "프로파일된"입니다. 이 경우 명시적으로 제외된 클래스를 제외하고 모든 클래스가 프로파일링됩니다.
호출 트리 시간
호출 트리를 올바르게 해석하려면 호출 트리 노드에 표시되는 숫자를 이해하는 것이 중요합니다. 모든 노드에 대해 흥미로운 두 가지 시간이 있으며, 총 시간과 자체 시간이 있습니다. 자체 시간은 노드의 총 시간에서 중첩된 노드의 총 시간을 뺀 것입니다.
일반적으로 자체 시간은 작으며, 특히 압축 필터링된 클래스에서는 그렇습니다. 대부분의 경우 압축 필터링된 클래스는 리프 노드이며, 자식 노드가 없기 때문에 총 시간은 자체 시간과 같습니다. 때때로 압축 필터링된 클래스는 콜백을 통해 또는 호출 트리의 진입점이기 때문에 프로파일된 클래스를 호출할 수 있습니다. 이 경우 프로파일되지 않은 메서드가 시간을 소비했지만 호출 트리에는 표시되지 않습니다. 그 시간은 호출 트리에서 사용할 수 있는 첫 번째 조상 노드로 거품처럼 올라가며, 압축 필터링된 클래스의 자체 시간에 기여합니다.
호출 트리의 퍼센트 바는 총 시간을 표시하지만, 자체 시간 부분은 다른 색상으로 표시됩니다. 메서드는 동일한 레벨에서 두 메서드가 오버로드되지 않는 한 서명 없이 표시됩니다. 호출 트리 노드의 표시를 사용자 정의하는 다양한 방법이 뷰 설정 대화 상자에 있습니다. 예를 들어, 자체 시간 또는 평균 시간을 텍스트로 표시하거나, 항상 메서드 서명을 표시하거나, 사용된 시간 스케일을 변경할 수 있습니다. 또한, 퍼센트 계산은 전체 호출 트리의 시간이 아닌 부모 시간에 기반할 수 있습니다.
스레드 상태
호출 트리의 상단에는 표시된 프로파일링 데이터의 유형과 범위를 변경하는 여러 뷰 매개변수가 있습니다. 기본적으로 모든 스레드는 누적됩니다. JProfiler는 스레드별로 CPU 데이터를 유지하며, 단일 스레드 또는 스레드 그룹을 표시할 수 있습니다.
모든 시간 동안 각 스레드에는 관련된 스레드 상태가 있습니다. 스레드가 바이트코드 명령을 처리할 준비가 되었거나 현재 CPU 코어에서 실행 중인 경우, 스레드 상태는 "Runnable"이라고 합니다. 이 스레드 상태는 성능 병목 현상을 찾을 때 관심이 있으므로 기본적으로 선택됩니다.
대안으로, 스레드는 Object.wait()
또는 Thread.sleep()
을 호출하여 모니터를 기다리고 있을 수 있으며, 이 경우 스레드 상태는
"Waiting"이라고 합니다. synchronized
코드 블록의 경계에서 모니터를 획득하려고 할 때 차단된 스레드는 "Blocking" 상태에 있습니다.
마지막으로, JProfiler는 스레드가 네트워크 데이터를 기다릴 때의 시간을 추적하는 합성 "Net I/O" 상태를 추가합니다. 이는 서버 및 데이터베이스 드라이버를 분석할 때 중요합니다. 이 시간은 느린 SQL 쿼리를 조사하는 것과 같은 성능 분석에 관련될 수 있습니다.
벽 시계 시간을 알고 싶다면, 스레드 상태 "모든 상태"를 선택하고 단일 스레드를 선택해야 합니다. 그래야만 코드에서 System.currentTimeMillis()
호출로 계산한
시간과 비교할 수 있습니다.
선택한 메서드를 다른 스레드 상태로 이동하려면 메서드 트리거와 "스레드 상태 재정의" 트리거 동작을 사용하거나 임베디드
또는 인젝션된 프로브 API의 ThreadStatus
클래스를 사용할 수
있습니다.
호출 트리에서 노드 찾기
호출 트리에서 텍스트를 검색하는 두 가지 방법이 있습니다. 첫째, 보기→찾기 메뉴를 호출하거나 호출 트리에서 직접 입력을 시작하여 활성화되는 빠른
검색 옵션이 있습니다. 일치 항목이 강조 표시되며, PageDown
을 누르면 검색 옵션을 사용할 수 있습니다. ArrowUp
및
ArrowDown
키를 사용하여 다른 일치 항목을 순환할 수 있습니다.
메서드, 클래스 또는 패키지를 검색하는 또 다른 방법은 호출 트리 하단의 뷰 필터를 사용하는 것입니다. 여기에서 쉼표로 구분된 필터 표현식 목록을 입력할 수 있습니다. "-"로 시작하는 필터 표현식은 무시된 필터와 같습니다. "!"로 시작하는 표현식은 압축 필터와 같습니다. 다른 모든 표현식은 프로파일된 필터와 같습니다. 필터 설정과 마찬가지로 초기 필터 유형은 클래스가 기본적으로 포함되거나 제외되는지를 결정합니다.
뷰 설정 텍스트 필드 왼쪽의 아이콘을 클릭하면 뷰 필터 옵션이 표시됩니다. 기본적으로 일치 모드는 "포함"이지만, 특정 패키지를 검색할 때 "시작"이 더 적합할 수 있습니다.
플레임 그래프
호출 트리를 보는 또 다른 방법은 플레임 그래프로 보는 것입니다. 관련 호출 트리 분석을 호출하여 전체 호출 트리 또는 그 일부를 플레임 그래프로 표시할 수 있습니다.
플레임 그래프는 하나의 이미지에 호출 트리의 전체 내용을 보여줍니다. 호출은 플레임 그래프의 하단에서 시작하여 상단으로 전파됩니다. 각 노드의 자식은 바로 위의 행에 배치됩니다. 자식 노드는 알파벳순으로 정렬되며 부모 노드에 중심을 맞춥니다. 각 노드에서 소비된 자체 시간으로 인해 "플레임"은 상단으로 갈수록 점점 좁아집니다. 노드에 대한 추가 정보는 도구 팁에 표시되며, 텍스트를 마크하여 클립보드에 복사할 수 있습니다.
마우스 커서 근처의 도구 팁이 분석을 방해하면, 오른쪽 상단의 버튼으로 잠글 수 있으며, 도구 팁 상단의 그리퍼로 편리한 위치로 이동할 수 있습니다. 동일한 버튼 또는 플레임 그래프를 두 번 클릭하면 도구 팁이 닫힙니다.
플레임 그래프는 정보 밀도가 매우 높으므로, 선택한 노드와 그 하위 계층 구조에 집중하여 표시된 내용을 좁힐 필요가 있을 수 있습니다. 관심 있는 영역을 확대할 수 있을 뿐만 아니라, 노드를 두 번 클릭하거나 컨텍스트 메뉴를 사용하여 새 루트 노드를 설정할 수 있습니다. 여러 번 연속으로 루트를 변경할 때, 루트의 기록에서 다시 이동할 수 있습니다.
플레임 그래프를 분석하는 또 다른 방법은 클래스 이름, 패키지 이름 또는 임의의 검색어를 기반으로 색상화를 추가하는 것입니다. 색상화는 컨텍스트 메뉴에서 추가할 수 있으며, 색상화 대화 상자에서 관리할 수 있습니다. 각 노드에 대해 첫 번째 일치하는 색상화가 사용됩니다. 색상화는 프로파일링 세션 간에 지속되며, 모든 세션 및 스냅샷에 대해 전역적으로 사용됩니다.
색상화 외에도, 빠른 검색 기능을 사용하여 관심 있는 노드를 찾을 수 있습니다. 커서 키를 사용하여 일치 결과를 순환할 수 있으며, 현재 강조 표시된 일치 항목에 대한 도구 팁이 표시됩니다.
핫스팟
애플리케이션이 너무 느리게 실행되는 경우, 대부분의 시간을 소비하는 메서드를 찾고 싶을 것입니다. 호출 트리로는 이러한 메서드를 직접 찾을 수 있는 경우도 있지만, 종종 호출 트리가 넓고 리프 노드가 많아 작동하지 않을 수 있습니다.
이 경우, 호출 트리의 반대가 필요합니다: 모든 메서드를 총 자체 시간으로 정렬한 목록으로, 모든 다른 호출 스택에서 누적되며, 메서드가 호출된 방법을 보여주는 백트레이스를 포함합니다. 핫스팟 트리에서는
리프가 진입점이며, 애플리케이션의 main
메서드나 스레드의 run
메서드와 같습니다. 핫스팟 트리의 가장 깊은 노드에서 호출은 최상위 노드로 위로
전파됩니다.
백트레이스의 호출 횟수와 실행 시간은 메서드 노드가 아니라, 이 경로를 따라 최상위 핫스팟 노드가 호출된 횟수를 나타냅니다. 이것은 이해하는 것이 중요합니다: 대충 보면, 노드의 정보를 해당 노드에 대한
호출을 정량화할 것으로 예상할 수 있습니다. 그러나 핫스팟 트리에서는 그 정보가 최상위 노드에 대한 노드의 기여를 보여줍니다. 따라서 이 반전된 호출 스택을 따라 최상위 핫스팟이
n
번 호출되었고 총 지속 시간은 t
초였다고 읽어야 합니다.
기본적으로 핫스팟은 자체 시간에서 계산됩니다. 총 시간에서 계산할 수도 있습니다. 이는 성능 병목 현상을 분석하는 데는 그다지 유용하지 않지만, 모든 메서드 목록을 보고 싶을 때 흥미로울 수 있습니다. 핫스팟 뷰는 오버헤드를 줄이기 위해 최대 메서드 수만 표시하므로 찾고 있는 메서드가 전혀 표시되지 않을 수 있습니다. 이 경우, 하단의 뷰 필터를 사용하여 패키지 또는 클래스를 필터링하십시오. 호출 트리와 달리 핫스팟 뷰 필터는 최상위 노드만 필터링합니다. 핫스팟 뷰의 컷오프는 전역적으로 적용되지 않으며, 표시된 클래스에 따라 적용되므로 필터를 적용한 후 새 노드가 나타날 수 있습니다.
핫스팟과 필터
핫스팟의 개념은 절대적이지 않으며 호출 트리 필터에 따라 다릅니다. 호출 트리 필터가 전혀 없는 경우, 가장 큰 핫스팟은 문자열 조작, I/O 루틴 또는 컬렉션 작업과 같은 JRE의 코어 클래스의 메서드일 가능성이 큽니다. 이러한 핫스팟은 매우 유용하지 않을 것입니다. 왜냐하면 이러한 메서드의 호출을 직접 제어할 수 없으며, 속도를 높일 방법도 없기 때문입니다.
유용하려면 핫스팟은 자신의 클래스의 메서드이거나 직접 호출하는 라이브러리 클래스의 메서드여야 합니다. 호출 트리 필터의 관점에서 자신의 클래스는 "프로파일된" 필터에 있고, 라이브러리 클래스는 "압축" 필터에 있습니다.
성능 문제를 해결할 때 라이브러리 계층을 제거하고 자신의 클래스만 보고 싶을 수 있습니다. 핫스팟 옵션 팝업에서 호출된 프로파일된 클래스에 추가 라디오 버튼을 선택하여 호출 트리에서 그 관점으로 빠르게 전환할 수 있습니다.
호출 그래프
호출 트리와 핫스팟 뷰 모두에서 각 노드는 특히 재귀적으로 호출될 때 여러 번 발생할 수 있습니다. 어떤 상황에서는 각 메서드가 한 번만 발생하고 모든 인바운드 및 아웃바운드 호출이 보이는 메서드 중심의 통계에 관심이 있을 수 있습니다. 이러한 뷰는 그래프로 표시하는 것이 가장 좋으며, JProfiler에서는 호출 그래프라고 합니다.
그래프의 단점 중 하나는 시각적 밀도가 트리보다 낮다는 것입니다. 이 때문에 JProfiler는 기본적으로 패키지 이름을 축약하고, 기본적으로 총 시간의 1% 미만인 아웃고잉 호출을 숨깁니다. 노드에 아웃고잉 확장 아이콘이 있는 한, 모든 호출을 표시하기 위해 다시 클릭할 수 있습니다. 뷰 설정에서 이 임계값을 구성하고 패키지 축약을 끌 수 있습니다.
호출 그래프를 확장할 때, 특히 여러 번 백트랙할 경우 매우 빠르게 혼란스러워질 수 있습니다. 그래프의 이전 상태를 복원하려면 실행 취소 기능을 사용하십시오. 호출 트리와 마찬가지로 호출 그래프는 빠른 검색을 제공합니다. 그래프에 입력하여 검색을 시작할 수 있습니다.
그래프와 트리 뷰는 각각 장단점이 있으므로 때로는 한 뷰 유형에서 다른 뷰 유형으로 전환하고 싶을 수 있습니다. 대화형 세션에서는 호출 트리와 핫스팟 뷰가 실시간 데이터를 표시하며 주기적으로 업데이트됩니다. 그러나 호출 그래프는 요청 시 계산되며 노드를 확장할 때 변경되지 않습니다. 호출 트리의 호출 그래프에서 보기 작업은 새 호출 그래프를 계산하고 선택한 메서드를 표시합니다.
그래프에서 호출 트리로 전환하는 것은 나중에 데이터가 더 이상 비교할 수 없기 때문에 불가능합니다. 그러나 호출 그래프는 보기→분석 작업을 통해 누적된 아웃고잉 호출 및 각 선택된 노드에 대한 백트레이스를 보여주는 호출 트리 분석을 제공합니다.
기본을 넘어
호출 트리, 핫스팟 뷰 및 호출 그래프의 앙상블에는 다른 장에서 자세히 설명된 많은 고급 기능이 있습니다. 또한, 별도로 제공되는 다른 고급 CPU 뷰도 있습니다.