JProfiler 도움말

프로브

CPU 및 메모리 프로파일링은 주로 JVM 애플리케이션의 기본 구성 요소인 객체와 메서드 호출을 다룹니다. 일부 기술의 경우, 실행 중인 애플리케이션에서 시맨틱 데이터를 추출하여 프로파일러에 표시하는 보다 고수준의 접근 방식이 필요합니다.

가장 대표적인 예는 JDBC를 사용한 데이터베이스 호출 프로파일링입니다. 호출 트리는 JDBC API를 언제 사용하는지와 이러한 호출에 걸리는 시간을 보여줍니다. 그러나 호출마다 서로 다른 SQL 문이 실행될 수 있으며, 어떤 호출이 성능 병목 현상의 원인인지 파악하기 어렵습니다. 또한 JDBC 호출은 애플리케이션의 여러 위치에서 발생하는 경우가 많으므로 일반 호출 트리에서 일일이 검색하는 대신 모든 데이터베이스 호출을 보여주는 단일 뷰를 갖는 것이 중요합니다.

이 문제를 해결하기 위해 JProfiler는 JRE의 주요 서브시스템을 위한 다양한 프로브를 제공합니다. 프로브는 특정 클래스에 인스트루멘테이션을 추가하여 데이터를 수집하고 "Databases", "HTTP, RPC & AI" 및 "JVM & Custom Probes" 뷰 섹션의 전용 뷰에 표시합니다. 또한 프로브는 호출 트리에 데이터를 어노테이션할 수 있어 일반 CPU 프로파일링과 고수준 데이터를 동시에 확인할 수 있습니다.

JProfiler에서 직접 지원하지 않는 기술에 대한 자세한 정보를 얻고자 하는 경우, 해당 기술을 위한 자체 프로브를 작성할 수 있습니다. 일부 라이브러리, 컨테이너 또는 데이터베이스 드라이버는 애플리케이션에서 사용할 때 JProfiler에 표시되는 자체 임베디드 프로브와 함께 제공될 수도 있습니다.

프로브 이벤트

프로브는 오버헤드를 발생시키기 때문에 기본적으로 녹화되지 않으며, 각 프로브에 대해 수동 또는 자동으로 녹화를 시작해야 합니다.

프로브 기능에 따라 프로브 데이터는 여러 뷰에 표시됩니다. 가장 하위 수준에는 프로브 이벤트가 있습니다. 다른 뷰들은 프로브 이벤트를 누적한 데이터를 표시합니다. 기본적으로 프로브가 녹화 중일 때도 프로브 이벤트는 유지되지 않습니다. 개별 이벤트가 중요해지면 프로브 이벤트 뷰에서 이를 녹화할 수 있습니다. 파일 프로브와 같은 일부 프로브의 경우 일반적으로 매우 높은 빈도로 이벤트를 생성하므로 권장되지 않습니다. 반면 "HTTP server" 프로브나 JDBC 프로브와 같은 다른 프로브는 훨씬 낮은 빈도로 이벤트를 생성할 수 있으므로 개별 이벤트를 녹화하는 것이 적합할 수 있습니다.

프로브 이벤트는 메서드 파라미터, 반환값, 인스트루멘테이션된 객체 및 발생한 예외를 포함한 다양한 소스로부터 프로브 문자열을 캡처합니다. 프로브는 실제 SQL 문자열을 구성하기 위해 prepared statement의 모든 setter 호출을 가로채야 하는 JDBC 프로브처럼 여러 메서드 호출에서 데이터를 수집할 수도 있습니다. 프로브 문자열은 프로브가 측정하는 상위 수준 서브시스템에 대한 기본 정보입니다. 또한 이벤트에는 시작 시간, 선택적 소요 시간, 연관된 스레드 및 스택 추적이 포함됩니다.

테이블 하단에는 표시된 총 이벤트 수를 보여주고 테이블의 모든 숫자 열을 합산하는 특수 행이 있습니다. 기본 열의 경우 여기에는 Duration 열만 포함됩니다. 테이블 위의 필터 선택기와 함께 사용하면 선택한 이벤트 하위 집합에 대해 수집된 데이터를 분석할 수 있습니다. 기본적으로 텍스트 필터는 모든 텍스트 필드 열에서 작동하지만 텍스트 필드 앞의 드롭다운에서 특정 필터 열을 선택할 수 있습니다. 컨텍스트 메뉴에서도 예를 들어 선택한 이벤트의 기간보다 긴 기간을 가진 모든 이벤트를 필터링하는 등의 필터 옵션을 사용할 수 있습니다.

다른 프로브 뷰에서도 프로브 이벤트를 필터링하는 옵션을 제공합니다. 프로브 텔레메트리 뷰에서는 시간 범위를 선택할 수 있고, 프로브 호출 트리 뷰에서는 선택한 호출 스택의 이벤트를 필터링할 수 있으며, 프로브 핫스팟 뷰에서는 선택한 역추적 또는 핫스팟을 기반으로 프로브 이벤트 필터를 제공하고, 제어 객체 및 타임라인 뷰에서는 선택한 제어 객체에 대한 프로브 이벤트를 필터링하는 액션을 제공합니다.

선택한 프로브 이벤트의 스택 추적은 하단에 표시됩니다. 여러 프로브 이벤트가 선택된 경우 스택 추적이 누적되어 호출 트리, 역추적이 포함된 프로브 핫스팟, 또는 역추적이 포함된 CPU 핫스팟으로 표시됩니다.

스택 추적 뷰 옆에는 이벤트 지속 시간에 대한 히스토그램 뷰와 선택적으로 녹화된 처리량에 대한 히스토그램 뷰가 표시됩니다. 이러한 히스토그램에서 마우스로 지속 시간 범위를 선택하여 상단 테이블의 프로브 이벤트를 필터링할 수 있습니다.

프로브는 다양한 종류의 활동을 기록하고 이벤트 유형을 프로브 이벤트와 연결할 수 있습니다. 예를 들어, JDBC 프로브는 statement, prepared statement 및 batch 실행을 서로 다른 색상의 이벤트 유형으로 표시합니다.

단일 이벤트가 녹화될 때 과도한 메모리 사용을 방지하기 위해 JProfiler는 이벤트를 통합(consolidate)합니다. 이벤트 상한선은 프로파일링 설정에서 구성되며 모든 프로브에 적용됩니다. 가장 최근의 이벤트만 유지되고 오래된 이벤트는 삭제됩니다. 이러한 통합은 상위 수준 뷰에는 영향을 미치지 않습니다.

프로브 호출 트리 및 핫스팟

프로브 녹화는 CPU 녹화와 긴밀하게 연동됩니다. 프로브 이벤트는 프로브 호출 트리로 집계되며, 여기서 프로브 문자열은 "페이로드"라고 불리는 리프 노드가 됩니다. 프로브 이벤트가 생성된 호출 스택만 해당 트리에 포함됩니다. 메서드 노드의 정보는 기록된 페이로드 이름을 나타냅니다. 예를 들어, 특정 호출 스택에서 SQL 문이 총 9000ms의 시간으로 42회 실행된 경우, 모든 상위 호출 트리 노드에 이벤트 횟수 42와 시간 9000ms가 추가됩니다. 기록된 모든 페이로드의 누적은 프로브 고유의 시간을 가장 많이 소비하는 호출 경로를 보여주는 호출 트리를 형성합니다. 프로브 트리의 초점은 페이로드이므로, 컨텍스트 메뉴에서 클래스를 필터링하는 모드도 제공하지만 뷰 필터는 기본적으로 페이로드를 검색합니다.

CPU 녹화가 꺼져 있으면 역추적에는 "No CPU data was recorded" 노드만 포함됩니다. CPU 데이터가 부분적으로만 녹화된 경우 이러한 노드와 실제 역추적이 혼합되어 나타날 수 있습니다. 샘플링이 활성화되어 있더라도 JProfiler는 기본적으로 프로브 페이로드에 대한 정확한 호출 추적을 녹화합니다. 이러한 오버헤드를 피하려면 프로파일링 설정에서 이를 끌 수 있습니다. 데이터 수집을 늘리거나 오버헤드를 줄이기 위해 조정할 수 있는 프로브 녹화용 튜닝 옵션이 몇 가지 더 있습니다.

핫스팟은 프로브 호출 트리로부터 계산할 수 있습니다. 핫스팟 노드는 이제 CPU 뷰 섹션에서처럼 메서드 호출이 아니라 페이로드가 됩니다. 이는 종종 프로브에서 가장 즉각적으로 유용한 뷰입니다. CPU 녹화가 활성화되어 있으면 일반 CPU 핫스팟 뷰에서와 마찬가지로 최상위 핫스팟을 열고 메서드 역추적을 분석할 수 있습니다. 역추적 노드의 숫자는 가장 깊은 노드부터 핫스팟 바로 아래 노드까지 이어지는 호출 스택을 따라 측정된 프로브 이벤트 수와 총 지속 시간을 나타냅니다.

프로브 호출 트리와 프로브 핫스팟 뷰 모두 해당 CPU 뷰와 마찬가지로 스레드 또는 스레드 그룹, 스레드 상태, 메서드 노드의 집계 수준을 선택할 수 있습니다. 데이터를 비교하기 위해 CPU 뷰에서 넘어올 때 염두에 두어야 할 중요한 점은 프로브 뷰의 기본 스레드 상태가 CPU 뷰의 "Runnable"이 아니라 "All states"라는 것입니다. 프로브 이벤트는 데이터베이스 호출, socket 작업 또는 프로세스 실행과 같은 외부 시스템과 관련된 경우가 많아 현재 JVM이 작업에 소비한 시간뿐만 아니라 총 시간을 확인하는 것이 중요하기 때문입니다.

제어 객체

외부 리소스에 대한 액세스를 제공하는 많은 라이브러리는 리소스와 상호 작용하는 데 사용할 수 있는 커넥션 객체를 제공합니다. 예를 들어 프로세스를 시작할 때 java.lang.Process 객체를 통해 출력 스트림에서 읽고 입력 스트림에 쓸 수 있습니다. JDBC로 작업할 때는 SQL 쿼리를 수행하기 위해 java.sql.Connection 객체가 필요합니다. 이러한 종류의 객체를 가리켜 JProfiler에서 사용하는 일반적인 용어는 "제어 객체"입니다.

프로브 이벤트를 제어 객체별로 그룹화하고 이들의 수명 주기를 표시하면 문제의 원인을 더 잘 이해하는 데 도움이 될 수 있습니다. 또한 제어 객체 생성은 비용이 많이 드는 경우가 많으므로 애플리케이션이 너무 많은 객체를 생성하지 않고 올바르게 닫도록 해야 합니다. 이를 위해 제어 객체를 지원하는 프로브에는 "Timeline" 및 "Control objects" 뷰가 있으며, 후자는 JDBC 프로브의 "Connections"와 같이 더 구체적으로 명명될 수 있습니다. 제어 객체가 열리거나 닫힐 때 프로브는 이벤트 뷰에 표시되는 특수 프로브 이벤트를 생성하므로 연결된 스택 추적을 검사할 수 있습니다.

타임라인 뷰에서 각 제어 객체는 막대로 표시되며, 막대의 색상은 제어 객체가 언제 활성 상태였는지를 보여줍니다. 프로브는 서로 다른 이벤트 유형을 기록할 수 있으며 타임라인은 이에 따라 색상화됩니다. 이 상태 정보는 통합되었거나 사용조차 불가능할 수 있는 이벤트 목록에서 가져오는 것이 아니라, 마지막 상태에서 100ms마다 샘플링됩니다. 제어 객체에는 이를 식별할 수 있는 이름이 있습니다. 예를 들어 파일 프로브는 파일 이름을 사용하여 제어 객체를 생성하는 반면 JDBC 프로브는 커넥션 문자열을 제어 객체의 이름으로 표시합니다.

제어 객체 뷰는 모든 제어 객체를 표 형식으로 보여줍니다. 기본적으로 열려 있는 제어 객체와 닫힌 제어 객체가 모두 표시됩니다. 상단의 컨트롤을 사용하여 표시를 열려 있거나 닫힌 제어 객체로만 제한하거나 특정 열의 콘텐츠를 필터링할 수 있습니다. 제어 객체에 대한 기본 수명 주기 데이터 외에도 테이블에는 이벤트 수 및 평균 이벤트 지속 시간과 같은 각 제어 객체의 누적 활동에 대한 데이터가 표시됩니다.

프로브마다 여기에 서로 다른 열이 표시됩니다. 예를 들어 프로세스 프로브는 읽기 및 쓰기 이벤트에 대해 별도의 열 세트를 표시합니다. 이 정보는 단일 이벤트 녹화가 비활성화된 경우에도 사용할 수 있습니다. 이벤트 뷰와 마찬가지로 하단의 총계 행을 필터링과 함께 사용하여 제어 객체의 일부 집합에 대한 누적 데이터를 얻을 수 있습니다.

프로브는 특정 속성을 중첩 테이블에 게시할 수 있습니다. 이는 메인 테이블의 정보 과부하를 줄이고 테이블 열에 더 많은 공간을 제공하기 위해 수행됩니다. 파일 및 프로세스 프로브와 같이 중첩 테이블이 있는 경우, 각 행의 왼쪽에 속성-값 테이블을 즉시 열 수 있는 확장 핸들이 있습니다.

타임라인, 제어 객체 뷰 및 이벤트 뷰는 탐색 액션으로 연결되어 있습니다. 예를 들어 타임라인 뷰에서 행을 우클릭하여 다른 각 뷰로 이동할 수 있으므로 선택한 제어 객체의 데이터만 표시되도록 할 수 있습니다. 이는 제어 객체 ID를 선택한 값으로 필터링함으로써 수행됩니다.

텔레메트리 및 트래커

프로브에 의해 수집된 누적 데이터로부터 여러 텔레메트리가 기록됩니다. 모든 프로브에 대해 초당 프로브 이벤트 수와 평균 지속 시간 또는 I/O 작업의 처리량과 같은 프로브 이벤트에 대한 일정한 평균 측정값을 사용할 수 있습니다. 제어 객체가 있는 프로브의 경우 열려 있는 제어 객체 수도 표준 텔레메트리입니다. 각 프로브는 추가 텔레메트리를 추가할 수 있습니다. 예를 들어 JPA 프로브는 쿼리 수와 엔티티 작업 수에 대한 별도의 텔레메트리를 보여줍니다.

핫스팟 뷰와 제어 객체 뷰는 시간에 따라 추적하기에 유용한 누적 데이터를 보여줍니다. 이러한 특수 텔레메트리는 프로브 트래커로 녹화됩니다. 추적을 설정하는 가장 쉬운 방법은 핫스팟 또는 제어 객체 뷰에서 Add Selection To Tracker 액션을 사용하여 새 텔레메트리를 추가하는 것입니다. 두 경우 모두 시간 또는 횟수 중 추적할 항목을 선택해야 합니다. 제어 객체를 추적할 때 텔레메트리는 모든 서로 다른 프로브 이벤트 유형에 대한 누적 영역 그래프(stacked area graph)가 됩니다. 추적된 핫스팟의 경우 추적된 시간은 서로 다른 스레드 상태로 분할됩니다.

프로브 텔레메트리는 시스템 텔레메트리 또는 사용자 정의 텔레메트리와 비교하기 위해 "Telemetries" 섹션에 추가할 수 있습니다. 그런 다음 텔레메트리 개요의 컨텍스트 메뉴 액션을 통해 프로브 녹화를 제어할 수도 있습니다.

JDBC 및 JPA

JDBC 및 JPA 프로브는 긴밀하게 연동되어 작동합니다. JPA 프로브와 함께 JDBC 프로브가 녹화된 경우, JPA 프로브의 이벤트 뷰에서 개별 이벤트를 확장하여 연관된 JDBC 이벤트를 확인할 수 있습니다.

마찬가지로 핫스팟 뷰는 모든 핫스팟에 JPA 작업에 의해 트리거된 JDBC 호출을 포함하는 특수 "JDBC calls" 노드를 추가합니다. 일부 JPA 작업은 비동기적이며 즉시 실행되지 않고 세션이 플러시되는 임의의 나중 시점에 실행됩니다. 성능 문제를 찾을 때 해당 플러시의 스택 추적은 도움이 되지 않으므로, JProfiler는 기존 엔티티를 획득했거나 새 엔티티가 영속화된 위치의 스택 추적을 기억하여 프로브 이벤트에 연결합니다. 이 경우 핫스팟의 역추적은 "Deferred operations"라는 레이블이 지정된 노드 안에 포함되며, 그렇지 않은 경우 "Direct operations" 노드가 삽입됩니다.

MongoDB 프로브와 같은 다른 프로브는 직접 작업과 비동기 작업을 모두 지원합니다. 비동기 작업은 현재 스레드에서 실행되지 않고 동일한 JVM 또는 다른 프로세스의 하나 이상의 다른 스레드 등 다른 곳에서 실행됩니다. 이러한 프로브의 경우 핫스팟의 역추적은 "Direct operations" 및 "Async operations" 컨테이너 노드로 분류됩니다.

JDBC 프로브의 특수한 문제는 ID와 같은 리터럴 데이터가 SQL 문자열에 포함되지 않아야만 양호한 핫스팟을 얻을 수 있다는 점입니다. prepared statement를 사용하는 경우에는 자동으로 처리되지만, 일반 statement를 실행하는 경우에는 그렇지 않습니다. 후자의 경우 대부분의 쿼리가 한 번만 실행되는 핫스팟 목록이 나타날 가능성이 높습니다. 이를 해결하기 위해 JProfiler는 unprepared statement의 리터럴을 대체할 수 있는 기본값이 아닌 옵션을 JDBC 프로브 설정에서 제공합니다. 디버깅 목적을 위해 이벤트 뷰에서 리터럴을 계속 확인하고 싶을 수도 있습니다. 이 옵션을 비활성화하면 JProfiler가 그렇게 많은 서로 다른 문자열을 캐시할 필요가 없으므로 메모리 오버헤드가 줄어듭니다.

반면에 JProfiler는 prepared statement에 대한 파라미터를 수집하고 이벤트 뷰에 플레이스홀더가 없는 완전한 SQL 문자열을 표시합니다. 이 역시 디버깅할 때 유용하지만, 필요하지 않은 경우 메모리를 절약하기 위해 프로브 설정에서 끌 수 있습니다.

JDBC 커넥션 누수

JDBC 프로브에는 데이터베이스 풀로 반환되지 않은 열려 있는 가상 데이터베이스 커넥션을 보여주는 "Connection leaks" 뷰가 있습니다. 이는 풀링된 데이터베이스 소스에 의해 생성된 가상 커넥션에만 영향을 미칩니다. 가상 커넥션은 닫힐 때까지 물리적 커넥션을 차단합니다.

누수 후보에는 "unclosed" 커넥션과 "unclosed collected" 커넥션의 두 가지 유형이 있습니다. 두 유형 모두 데이터베이스 풀에서 제공한 커넥션 객체가 여전히 힙에 남아 있지만, close()가 호출되지 않은 가상 커넥션입니다. "Unclosed collected" 커넥션은 가비지 컬렉션되었으며 명백한 커넥션 누수입니다.

"Unclosed" 커넥션 객체는 여전히 힙에 있습니다. Open Since 기간이 길수록 해당 가상 커넥션이 누수 후보일 가능성이 높습니다. 가상 커넥션이 10초 이상 열려 있으면 잠재적인 누수로 간주됩니다. 그러나 여전히 close()가 호출될 수 있으며, 이 경우 "Connection leaks" 뷰의 항목이 제거됩니다.

커넥션 누수 테이블에는 커넥션 클래스의 이름을 보여주는 Class Name 열이 포함되어 있습니다. 이를 통해 어떤 유형의 풀이 커넥션을 생성했는지 알 수 있습니다. JProfiler는 수많은 데이터베이스 드라이버와 커넥션 풀을 명시적으로 지원하며 어떤 클래스가 가상 커넥션이고 물리적 커넥션인지 알고 있습니다. 알 수 없는 풀이나 데이터베이스 드라이버의 경우 JProfiler가 물리적 커넥션을 가상 커넥션으로 오인할 수 있습니다. 물리적 커넥션은 오래 유지되는 경우가 많으므로 "Connection leaks" 뷰에 표시될 수 있습니다. 이 경우 커넥션 객체의 클래스 이름을 통해 거짓 양성(false positive)임을 식별할 수 있습니다.

기본적으로 프로브 녹화를 시작할 때 커넥션 누수 분석은 활성화되지 않습니다. 커넥션 누수 뷰에는 JDBC 프로브 설정의 Record open virtual connections for connection leak analysis 체크박스 상태와 일치하는 별도의 녹화 버튼이 있습니다. 이벤트 녹화와 마찬가지로 버튼의 상태는 유지되므로 분석을 한 번 시작하면 다음 프로브 녹화 세션에서도 자동으로 시작됩니다.

호출 트리의 페이로드 데이터

CPU 호출 트리를 볼 때 프로브가 페이로드 데이터를 녹화한 위치를 확인하는 것은 흥미롭습니다. 해당 데이터는 측정된 CPU 시간을 해석하는 데 도움이 될 수 있습니다. 이것이 바로 많은 프로브가 CPU 호출 트리에 상호 링크를 추가하는 이유입니다. 예를 들어 클래스로더 프로브는 클래스 로딩이 트리거된 위치를 보여줄 수 있습니다. 이는 호출 트리에서 다른 방법으로는 볼 수 없으며 예기치 않은 오버헤드를 추가할 수 있습니다. 호출 트리 뷰에서 불투명하게 보이는 데이터베이스 호출은 한 번의 클릭으로 해당 프로브에서 추가 분석할 수 있습니다. 이는 프로브 링크를 클릭할 때 프로브 호출 트리 뷰의 컨텍스트에서 분석이 자동으로 반복되는 호출 트리 분석에서도 작동합니다.

또 다른 가능성은 페이로드 정보를 CPU 호출 트리에 직접 인라인으로 표시하는 것입니다. 관련된 모든 프로브에는 이를 위한 Annotate in call tree 옵션이 설정에 있습니다. 이 경우 프로브 호출 트리로 연결되는 링크는 제공되지 않습니다. 각 프로브에는 고유한 페이로드 컨테이너 노드가 있습니다. 동일한 페이로드 이름을 가진 이벤트가 집계되고 호출 횟수 및 총 시간이 표시됩니다. 페이로드 이름은 호출 스택별로 통합되며, 가장 오래된 항목은 "[earlier calls]" 노드로 집계됩니다. 호출 스택당 기록되는 페이로드 이름의 최대 수는 프로파일링 설정에서 구성할 수 있습니다.

호출 트리 분할

일부 프로브는 프로브 문자열을 사용하여 호출 트리에 페이로드 데이터를 어노테이션하지 않습니다. 대신 서로 다른 각 프로브 문자열에 대해 호출 트리를 분할합니다. 이는 들어오는 각 요청 유형별로 호출 트리를 별도로 확인하려는 서버 유형 프로브에 특히 유용합니다. "HTTP server" 프로브는 URL을 가로채고 호출 트리를 분할하는 데 URL의 어떤 부분을 사용할지 세밀하게 제어할 수 있도록 합니다. 기본적으로 파라미터 없이 요청 URI 경로만 사용합니다.

유연성을 높이기 위해 분할 문자열을 결정하는 스크립트를 정의할 수 있습니다. 스크립트에서는 현재 javax.servlet.http.HttpServletRequest를 파라미터로 받아 원하는 문자열을 반환합니다.

또한 단일 분할 수준으로 제한되지 않고 중첩된 여러 분할을 정의할 수 있습니다. 예를 들어 먼저 요청 URI 경로로 분할한 다음 HTTP 세션 객체에서 추출된 사용자 이름으로 분할할 수 있습니다. 또는 요청 URI로 분할하기 전에 요청 메서드별로 요청을 그룹화할 수도 있습니다.

중첩 분할을 사용하면 호출 트리의 각 수준에 대한 개별 데이터를 볼 수 있습니다. 호출 트리를 볼 때 특정 수준이 방해가 되어 "HTTP server" 프로브 설정에서 이를 제거해야 하는 상황이 발생할 수 있습니다. 기록된 데이터 손실 없이 보다 편리하게, 해당 분할 노드의 컨텍스트 메뉴를 사용하여 호출 트리에서 분할 수준을 즉시 일시적으로 병합 및 병합 해제할 수 있습니다.

호출 트리를 분할하면 상당한 메모리 오버헤드가 발생할 수 있으므로 주의해서 사용해야 합니다. 메모리 과부하를 방지하기 위해 JProfiler는 최대 분할 수를 제한합니다. 특정 분할 수준의 분할 상한에 도달하면 상한 카운터를 재설정할 수 있는 하이퍼링크가 포함된 특수 "[capped nodes]" 분할 노드가 추가됩니다. 기본 상한이 목적에 비해 너무 낮으면 프로파일링 설정에서 늘릴 수 있습니다.