태스크의 비동기 실행은 일반 Java 코드뿐만 아니라 리액티브 프레임워크에서 더욱 흔히 사용되는 일반적인 관행입니다. 소스 파일에서 인접한 코드가 이제 두 개 이상의 서로 다른 thread에서 실행됩니다. 디버깅과 프로파일링 시 이러한 thread 변경은 두 가지 문제를 야기합니다: 한편으로는 호출된 작업이 얼마나 비용이 드는지 명확하지 않습니다. 다른 한편으로는, 비용이 많이 드는 작업을 그 실행을 유발한 코드로 추적할 수 없습니다.
JProfiler는 호출이 동일한 JVM 내에 머무르는지 여부에 따라 이 문제에 대한 다양한 솔루션을 제공합니다. 비동기 실행이 호출한 JVM과 동일한 JVM에서 발생하는 경우, "Inline Async Executions" 호출 트리 분석은 호출 사이트와 실행 사이트 모두를 포함하는 단일 호출 트리를 계산합니다. 원격 JVM으로 요청이 전송되는 경우, 호출 트리에는 호출 사이트와 실행 사이트로의 하이퍼링크가 포함되어 있어, 관련된 JVM들의 프로파일링 세션을 보여주는 JProfiler의 서로 다른 최상위 창 사이를 양방향으로 원활하게 탐색할 수 있습니다.
비동기 및 원격 요청 추적 활성화
비동기 메커니즘은 다양한 방식으로 구현될 수 있으며, 별도의 thread 또는 다른 JVM에서 태스크를 시작하는 의미론은 일반적인 방법으로 감지할 수 없습니다. JProfiler는 여러 일반적인 비동기 및 원격 요청 기술을 명시적으로 지원합니다. 요청 추적 설정에서 이들을 활성화하거나 비활성화할 수 있습니다. 기본적으로 요청 추적은 활성화되어 있지 않습니다. 또한 세션 시작 직전에 표시되는 세션 시작 대화상자에서 요청 추적을 구성할 수도 있습니다.
JProfiler의 메인 창에서는 상태 표시줄을 통해 일부 비동기 및 원격 요청 추적 유형이 활성화되어 있는지 표시하며, 설정 대화상자로 바로 이동할 수 있는 바로가기를 제공합니다.
JProfiler는 프로파일된 JVM에서 활성화되지 않은 비동기 요청 추적 유형이 사용되는 경우 알림 아이콘을 상태 표시줄의 비동기 및 원격 요청 추적 아이콘 옆에 표시합니다. 알림 아이콘을 클릭하면 감지된 추적 유형을 활성화할 수 있습니다. 비동기 및 원격 요청 추적은 상당한 오버헤드를 유발할 수 있으므로 필요한 경우에만 활성화해야 합니다.
비동기 추적
하나 이상의 비동기 추적 유형이 활성화된 경우, CPU, 할당, 프로브 녹화에 대한 호출 트리 및 핫스팟 뷰에서 모든 활성화된 추적 유형에 대한 정보를 "Inline Async Executions" 호출 트리 분석을 계산하는 버튼과 함께 보여줍니다. 해당 분석의 결과 뷰에서는 모든 비동기 실행의 호출 트리가 호출 사이트와 "비동기 실행" 노드를 통해 연결됩니다. 기본적으로 비동기 실행 측정값은 호출 트리의 상위 노드에 더해지지 않습니다. 집계된 값을 보는 것이 유용한 경우도 있으므로, 분석 상단의 체크박스를 통해 필요에 따라 이를 활성화할 수 있습니다.
다른 thread에서 태스크를 오프로딩하는 가장 간단한 방법은 새 thread를 시작하는 것입니다. JProfiler를 사용하면 "Thread start" 요청 추적 유형을 활성화하여 thread의 생성부터 실행 사이트까지 추적할 수 있습니다. 하지만 thread는 무거운 객체이며 반복 호출에 재사용되는 경우가 많으므로, 이 요청 추적 유형은 디버깅 목적에 더 적합합니다.
다른 thread에서 태스크를 시작하는 가장 중요하고 일반적인 방법은 java.util.concurrent 패키지의 executor를 사용하는 것입니다. executor는 비동기
실행을 다루는 많은 상위 수준의 서드파티 라이브러리의 기반이 되기도 합니다. executor를 지원함으로써, JProfiler는 멀티스레드 및 병렬 프로그래밍을 다루는 전체 라이브러리 계열을 지원합니다.
위의 일반적인 경우 외에도, JProfiler는 JVM용 두 가지 GUI 툴킷(AWT와 SWT)도 지원합니다. 두 툴킷 모두 싱글 thread 기반으로, 하나의 특별한 이벤트 디스패치 thread가
GUI 위젯을 조작하고 그리기 작업을 수행할 수 있습니다. GUI가 블로킹되지 않도록, 장시간 실행되는 태스크는 백그라운드 thread에서 수행해야 합니다. 하지만 백그라운드 thread는 종종 진행
상황이나 완료를 표시하기 위해 GUI를 업데이트해야 하므로, 이를 위해 Runnable을 이벤트 디스패치 thread에서 실행하도록 예약하는 특별한 메서드를 사용합니다.
GUI 프로그래밍에서는 원인과 결과를 연결하기 위해 여러 번의 thread 변경을 따라가야 하는 경우가 많습니다. 사용자가 이벤트 디스패치 thread에서 액션을 시작하면, 그에 따라 executor를 통해 백그라운드 작업이 시작됩니다. 완료 후, 해당 executor가 이벤트 디스패치 thread로 작업을 다시 전달합니다. 만약 마지막 작업에서 성능 문제가 발생한다면, 이는 최초 이벤트로부터 두 번의 thread 변경이 일어난 결과입니다.
마지막으로, JProfiler는 Kotlin coroutine도 지원합니다. Kotlin의 멀티스레딩 솔루션으로, 모든 Kotlin 백엔드에서 구현되어 있습니다. 비동기 실행 자체는 coroutine이 시작되는 지점입니다. Kotlin coroutine의 디스패치 메커니즘은 유연하여, 실제로 현재 thread에서 시작될 수도 있는데, 이 경우 "비동기 실행" 노드에 인라인 부분이 추가되어 노드의 텍스트에 별도로 표시됩니다.
suspend 메서드는 실행을 중단할 수 있으며, 이후 다른 thread에서 재개될 수 있습니다. suspend가 감지된 메서드에는 "suspend" 아이콘이 추가로 표시되며, 툴팁에는 실제 호출 횟수와 의미론적 호출 횟수가 표시됩니다. Kotlin coroutine은 의도적으로 suspend될 수 있지만, thread에 바인딩되어 있지 않으므로 대기 시간은 호출 트리 어디에도 나타나지 않습니다. coroutine 실행이 완료될 때까지 걸린 전체 시간을 확인하려면, "비동기 실행" 노드 아래에 "suspended" 시간 노드가 추가되어 coroutine의 전체 suspend 시간을 캡처합니다. 비동기 실행의 CPU 시간 또는 wall clock 시간 중 어떤 것에 관심이 있는지에 따라, 분석 상단의 "Show suspended times" 체크박스를 통해 해당 노드를 실시간으로 추가하거나 제거할 수 있습니다.
"프로파일되지 않은 호출 사이트 추적">프로파일되지 않은 호출 사이트 추적
기본적으로 executor와 Kotlin coroutine 추적은 호출 사이트가 프로파일된 클래스에 있을 때만 비동기 실행을 추적합니다. 이는 프레임워크나 라이브러리가 이러한 비동기 메커니즘을 직접적으로 사용자 코드 실행과 관련 없는 방식으로 사용할 수 있기 때문이며, 추가된 호출 및 실행 사이트가 오버헤드와 혼란만을 야기할 수 있기 때문입니다. 하지만, 프로파일되지 않은 호출 사이트를 추적해야 하는 경우도 있습니다. 예를 들어, 프레임워크가 Kotlin coroutine을 시작하고 그 위에서 사용자 코드가 실행되는 경우가 있습니다.
프로파일되지 않은 클래스에서 이러한 호출 사이트가 감지되면, 호출 트리 및 핫스팟 뷰의 추적 정보에 해당 알림 메시지가 표시됩니다. 라이브 세션에서는 executor와 Kotlin coroutine 추적에 대해 프로파일되지 않은 호출 사이트 추적을 해당 뷰에서 개별적으로 활성화할 수 있습니다. 이러한 옵션은 언제든지 세션 설정 대화상자의 "CPU 프로파일링" 단계에서 변경할 수 있습니다.
Kotlin coroutine은 launch 시점에 CPU 녹화가 활성화되어 있어야만 추적이 가능하다는 점을 이해하는 것이 중요합니다. 나중에 CPU 녹화를 시작하면, Kotlin coroutine의 비동기 실행은 인라인 처리할 수 없습니다. JProfiler는 프로파일되지 않은 클래스의 호출 사이트 감지와 마찬가지로 알림을 표시합니다. 애플리케이션 시작 시점에 시작되는 장수 coroutine을 프로파일링해야 한다면, attach 모드를 사용할 수 없습니다. 이 경우, -agentpath VM 파라미터로 JVM을 실행하고, 시작 시점에 CPU 녹화를 시작해야 합니다.
원격 요청 추적
선택된 통신 프로토콜에 대해, JProfiler는 메타데이터를 삽입하고 JVM 경계를 넘어 요청을 추적할 수 있습니다. 지원되는 기술은 다음과 같습니다:
-
HTTP: HttpURLConnection, java.net.http.HttpClient, Apache Http Client 4.x, Apache Async Http Client 4.x, OkHttp 3.9+ on the client side, any Servlet-API implementation or Jetty without Servlets on the server side
HTTP: HttpURLConnection, java.net.http.HttpClient, Apache Http Client 4.x, Apache Async Http Client 4.x, OkHttp 3.9+ (클라이언트 측), 모든 Servlet-API 구현 또는 Jetty(서블릿 없이) (서버 측) -
Additional support for async JAX-RS calls for Jersey Async Client 2.x, RestEasy Async Client 3.x, Cxf Async Client 3.1.1+
비동기 JAX-RS 호출 추가 지원: Jersey Async Client 2.x, RestEasy Async Client 3.x, Cxf Async Client 3.1.1+ -
Web services: JAX-WS-RI, Apache Axis2 and Apache CXF
웹 서비스: JAX-WS-RI, Apache Axis2, Apache CXF - RMI
- gRPC
-
Remote EJB calls: JBoss 7.1+ and Weblogic 11+
원격 EJB 호출: JBoss 7.1+, Weblogic 11+
JProfiler에서 요청을 추적하려면 두 VM 모두를 프로파일링하고, 각각을 별도의 JProfiler 최상위 창에서 동시에 열어야 합니다. 이는 라이브 세션과 스냅샷 모두에서 동작합니다. 대상 JVM이 현재 열려 있지 않거나, 원격 호출 시점에 CPU 녹화가 활성화되어 있지 않은 경우, 호출 사이트 하이퍼링크를 클릭하면 오류 메시지가 표시됩니다.
원격 요청을 추적할 때, JProfiler는 관련된 JVM의 호출 트리에서 호출 사이트와 실행 사이트를 명시적으로 표시합니다. JProfiler에서 호출 사이트는 기록된 원격 요청이 수행되기 전 마지막으로 프로파일된 메서드 호출입니다. 이는 다른 VM에 위치한 실행 사이트에서 태스크를 시작합니다. JProfiler에서는 호출 트리 뷰에 표시되는 하이퍼링크를 사용하여 호출 사이트와 실행 사이트를 오갈 수 있습니다.
호출 사이트는 모든 thread에 대해 원격 요청 추적에서 동일한 ID를 가집니다. 즉, 호출 사이트와 실행 사이트를 오갈 때 thread 해상도가 필요 없으며, 항상 "모든 thread 그룹" 및 "모든 thread 상태" thread 상태 선택이 활성화되어 대상이 표시된 트리의 일부가 되도록 보장됩니다.
호출 사이트와 실행 사이트는 1:n 관계입니다. 하나의 호출 사이트가 여러 실행 사이트에서 원격 태스크를 시작할 수 있으며, 특히 서로 다른 원격 VM에 있을 때 그렇습니다. 동일한 VM 내에서는 하나의 호출 사이트에 대해 여러 실행 사이트가 존재하는 경우가 드물지만, 이는 서로 다른 호출 스택에서 발생해야 합니다. 하나의 호출 사이트가 둘 이상의 실행 사이트를 호출하는 경우, 대화상자에서 그 중 하나를 선택할 수 있습니다.
실행 사이트는 호출 트리 내에서 하나의 호출 사이트에 의해 시작된 모든 실행을 포함하는 합성 노드입니다. 실행 사이트 노드의 하이퍼링크를 클릭하면 해당 호출 사이트로 다시 이동할 수 있습니다.
동일한 호출 사이트가 동일한 실행 사이트를 반복적으로 호출하는 경우, 실행 사이트에는 모든 호출의 병합된 호출 트리가 표시됩니다. 만약 이를 원하지 않는 경우, 예외적인 메소드 기능을 사용하여 아래 스크린샷과 같이 호출 트리를 추가로 분할할 수 있습니다.
실행 사이트는 단일 호출 사이트에서만 참조되지만, 호출 사이트는 여러 실행 사이트로 연결될 수 있습니다. 호출 사이트의 숫자 ID를 통해, 서로 다른 실행 사이트에서 참조되는 동일한 호출 사이트임을 식별할 수 있습니다. 또한, 호출 사이트에는 원격 VM의 ID가 표시됩니다. 프로파일된 VM의 ID는 상태 표시줄에서 확인할 수 있습니다. 이는 JProfiler가 내부적으로 관리하는 고유 ID가 아니라, JProfiler에서 새로 열린 각 프로파일된 VM에 대해 1부터 시작하여 증가하는 표시용 ID입니다.