Post

Optimizing Java - 프로파일링

1. 개요

프로파일링은 보통 다음을 의미한다.

  • 실행
  • 할당(메모리 프로파일링)

프로파일러는 애플리케이션 동작을 잘못 나타내거나 편향이 드러날 수 있는데 프로파일러의 동작 방식을 이해하지 못하면 잘못된 결론을 낼 수 있다.


2. 프로파일링 개요

프로파일링 툴은 보통 저수준의 계측(instrumentation)을 이용하여 작동하고 데이터는 콘솔에 보여지거나 로그에 저장된다. 이 프로파일링의 목표는 리팩토링 및 성능 최적화 대상 코드를 식별이다.

무작정 프로파일링을 수행하기 전에 문제의 위치 식별이 중요하다. 예를 들면 애플리케이션이 유저 모드에서 100%에 육박하는 CPU 사용률을 보인다면 애플리케이션의 실행 프로파일링을 시도해 볼 수 있다. 그렇지 않다면 실행 프로파일링 대신 다른 해결책을 찾아야 한다.

1) 샘플링, 세이프포인팅 편향

데이터 수집 비용을 줄이기 위해 주기적으로 스레드 실행 스냅샷을 찍는 샘플링을 수행한다. 샘플링 주기는 항상 논란이 되는 문제이다.

  • 너무 자주 샘플링하면 오버헤드
  • 뜸하게 샘플링하면 중요 정보를 놓칠 가능성 증가

또한 대부분 샘플링이 세이프포인트에서만 일어나는 세이프포인트 편향도 무시할 수 없다. 세이프포인트에서만 샘플링을 하기 때문에 중요한 정보를 놓칠 수 있고 모든 스레드가 세이프포인트에 도달하기 까지의 오버헤드가 있다.

핫스팟 C++ API에 있는 GetCallTrace() 함수는 세이프포인트에서 샘플링을 하기 때문에 이러한 점을 고려해야 한다.

JIT로 인한 세이프포인트 편향

1
2
3
for (int i = 0; i < LIMIT; i++) {
  //단순 작업
  }
  • LIMIT이 작은 경우 루프가 펼쳐져 전체 루프에서 세이프포인트가 하나 존재
  • LIMIT가 큰 경우 루프마다 세이프포인트 체크를 삽입

3. 개발자용 프로파일링 툴

1) VisualVM

img.png

출처

실행/메모리 프로파일러 모두 있는 기본적인 툴이다. 필수적인 기능만 있어 본격적인 프로파일링에는 다른 상용 툴을 사용하는 경우가 많다.

2) JProfiler

img_1.png

출처

GUI/CUI 모두 지원하며 로컬 또는 원격 애플리케이션을 프로파일링할 수 있다. 지원하는 기능은 다음과 같다.

  • 원하는 시점에서 프로파일링 가능
  • 메서드별 고유 시간
  • 메모리, CPU 사용량
  • 세이프포인트 샘플링

3) YourKit

img_2.png

출처

  • 원하는 시점에서 프로파일링 가능
  • IntelliJ, Eclipse 플러그인 지원
  • CPU 사용 시간, 사용량 등을 상세히 확인 가능
  • 세이프포인트 샘플링

4) JFR/JMC

img_3.png

오라클이 제공하는 프로파일링/모니터링 기술

5) 운영 툴

프로파일러는 문제점을 진단하거나 저수준의 동작을 파악하기 위해 사용하지만 운영계 모니터링 툴로도 많이 쓴다.

  • 레드햇 서모스탯(Thermostat)

서버 모니터링(클러스터도 모니터링 가능)

필요 시 유저가 직접 만든 지표를 수집하고 분석할 수 있다.

  • 뉴 렐릭(New Relic)

JVM을 포함한 종합 모니터링 및 풀스택 지원

자바 에이전트 API로 커스텀 인스트루먼테이션을 구현할 수 있다.

  • jClarity 일루미네이트(illuminate)

외부의 데몬 프로세스가 메인 자바 어플리케이션을 프로파일링

머신 러닝을 활용하여 OS, GC 로그, JVM의 데이터를 분석해서 근본 원인 진단


4. 최신 프로파일러

기존 프로파일러보다 더 정확하고 나은 통찰력을 제시하는 오픈 소스 툴들이 등장

1) 어니스트 프로파일러(Honest Profiler)

어니스트 프로파일러의 목표는 다음과 같다.

  • 세이프포인트 편향 제거
  • 오버헤드를 최소화

어니스트 프로파일러는 다음과 같은 특징을 가진다.

  • 어니스트 프로파일러는 GetCallTrace() 대신 AsyncGetCallTrace()라는 OpenJDK 프라이빗 API를 활용한다.
  • UNIX OS의 SIGPROF 시그널을 활용하여 실행 중인 특정 스레드만 인터럽트하여 측정할 수 있어 오버헤드가 줄어든다.
  • 수집된 샘플링 결과는 락-프리한 버퍼에 기록된 후 별도의 전용 스레드가 처리하므로 애플리케이션 성능에 미치는 영향이 적다.

2) perf

리눅스 애플리케이션의 경량급 프로파일링 툴이다. 하드웨어 이벤트를 카운팅하는 하드웨어의 성능 카운터(performance counter)를 읽는다. 바로 JVM을 모니터링할 수는 없고 perf-map-agent라는 에이전트를 통해 성능을 확인할 수 있다.

-XX:+PreserveFramePointer 플래그로 perf와의 상호작용을 개선할 수 있지만 JIT 컴파일 최적화 기능이 해제된다는 단점이 있다. perf의 GUI 틀로는 FlameGraph가 있으며 그래프를 보면 정확한 실행 시간이 기록된 자세한 내역을 확인할 수 있다.

3) Async

어니스트 프로파일러와 같은 내부 API와 perf를 함께 써서 모니터링하는 툴로 perf를 쓰기 때문에 리눅스에서만 사용할 수 있다.


5. 할당 프로파일링

메모리를 프로파일링함으로써 애플리케이션의 할당 동작을 살펴볼 수 있다. HeapVisitor 방식은 힙의 모든 객체를 방문하는 가장 단순한 방식으로 jmap이나 VisualVM, Yourkit에서 사용할 수 있다. 이 외에도 다양한 방식으로 할당 프로파일링을 구현한 에이전트가 있다.

1) ASM

OpCode가 실행되기 전에 바이트코드를 조작하여 로깅 코드를 삽입할 수 있는 바이트코드 조작 라이브러리이다. ASM은 NEW, NEWARRAY, ANEWARRAY 바이트코드가 호출되기 전에 기록 메서드를 호출하는 코드를 끼워넣는다.

  • NEW: 객체 생성할 공간 할당
  • NEWARRAY: 기본 타입 배열 공간을 할당
  • ANEWARRAY: 객체 타입 배열 공간을 할당

2) Async

TLAB를 이용하여 할당 프로파일링한다.

  • 새로 만든 TLAB에 객체가 할당될 때
  • TLAB 밖에 객체가 할당될 때

완벽하게 모니터링하기 보다는 위와 같은 조작이 이루어졌을 때 알림을 받고 TLAB 평균 크기 단위로 프로파일링한다. 저렴한 비용으로 샘플링할 수 있지만 정확도가 떨어질 수는 있다.


6. 힙 덤프 분석

힙 덤프 분석 또한 할당 프로파일링 기법이다. 힙을 스냅샷으로 저장하고 이 스냅샷 내의 객체의 개수, 타입 등을 자세히 파악한다.

정확한 객체의 상호 관계, 형상, 구조를 파악하는데 도움이 되지만 다음의 단점 또한 존재한다.

  • 힙을 덤프 분석하면 기존의 힙보다 3배, 4배 용량이 커진다.(디스크 저장, 네트워크 전송 시 문제)
  • 힙을 모두 뒤져서 덤프 파일을 쓰므로 STW가 발생

7. 결론

실행/할당 프로파일러의 기본 동작 원리를 알고 나중에 적절한 툴을 선정할 수 있도록 인지

  • 실행 프로파일링에서는 모든 쓰레드를 세이프포인트에 세워서 데이터를 가져가는 세이프포인트 샘플링을 주로 사용
  • 샘플링 빈도를 높이면 정확한 측정, 대신 오버헤드
  • 최신 프로파일러는 세이프포인트 샘플링 대신 특정 쓰레드만 인터럽트하여 측정하는 방식을 사용하여 성능 향상 시도
  • 할당 프로파일링은 운영계에서 TLAB 단위로 추적하는 Async가 좋다
  • 그 외에 할당 Opcode 전에 바이트코드 삽입하는 방식이나 힙을 스냅샷하여 분석하는 방식 존재
This post is licensed under CC BY 4.0 by the author.

© . Some rights reserved.

Using the Chirpy theme for Jekyll.