성능 측정 및 분석 예

이 예에서는 Macrobenchmark와 함께 시스템 추적과 메모리 프로파일링을 사용하여 특정 종류의 성능 문제를 측정하고 개선하는 방법을 보여줍니다.

systrace를 사용하여 앱 시작 디버깅

시작 시간을 디버깅할 때 systrace 로그를 사용하는 것이 좋습니다. systrace는 사전 계측 코드를 사용하여 특정 이벤트가 발생하는 데 걸리는 시간을 출력하는 시스템입니다. 이러한 트레이스를 사용하면 애플리케이션에서 또는 시스템의 다른 프로세스에서 발생하는 상황을 확인할 수 있습니다. Android 플랫폼과 Jetpack 라이브러리에는 애플리케이션의 여러 주요 이벤트에 관한 계측 기능이 있어 이러한 상황이 적절하게 기록됩니다. 같은 systrace 시각화 도구에 표시되는 자체 맞춤 트레이스로 애플리케이션을 계측하여 애플리케이션에서 발생한 전반적인 상황을 파악할 수도 있습니다.

systrace 또는 Perfetto 사용

기본 systrace 사용에 관한 자세한 내용은 다음 동영상 애플리케이션 성능 디버깅을 참고하세요.

시작 시간을 분석하려면 먼저 시작 시 발생하는 상황을 파악해야 합니다. 이 페이지의 설명보다 자세한 내용을 알고 싶다면 애플리케이션 시작 프로세스 개요를 제공하는 앱 시작 시간에 관한 문서를 참고하세요.

앱 시작 단계는 다음과 같습니다.

  • 프로세스를 실행합니다.
  • 일반 애플리케이션 객체를 초기화합니다.
  • 활동을 만들고 초기화합니다.
  • 레이아웃을 확장합니다.
  • 첫 번째 프레임을 그립니다.

시작 유형 단계는 다음과 같습니다.

  • 콜드 스타트: 애플리케이션이 부팅 후 또는 사용자나 시스템에 의해 애플리케이션 프로세스가 종료된 후 처음 시작될 때 발생합니다. 시작 시 저장된 상태 없이 새 프로세스가 만들어집니다.
  • 웜 스타트: 애플리케이션이 이미 백그라운드에서 실행되고 있지만 활동을 다시 만들어 포그라운드로 가져와야 할 때 발생합니다. 활동이 기존 프로세스를 재사용하는 동안 다시 만들어지거나 프로세스가 저장된 상태로 다시 만들어집니다. Macrobenchmark 테스트 라이브러리는 첫 번째 옵션을 사용하여 일관된 웜 스타트 테스트를 지원합니다.
  • 핫 스타트: 프로세스와 활동이 여전히 실행되고 있고 포그라운드로 가져오기만 해야 할 때 발생하며 필요에 따라 일부 객체를 다시 만들고 새 포그라운드 활동을 렌더링할 수 있습니다. 가장 짧은 시작 시나리오입니다.

개발자 옵션에서 제공되는 온디바이스 시스템 추적 앱을 사용하여 systrace를 캡처하는 것이 좋습니다. 명령줄 도구를 사용하려면 Android 10 (API 수준 29) 이상에서 Perfetto를 사용할 수 있고 이전 버전의 기기는 systrace를 사용해야 합니다.

'첫 번째 프레임'이라는 용어는 약간 부적절한 명칭입니다. 애플리케이션이 초기 활동을 만든 후 시작을 처리하는 방식에서 크게 다를 수 있기 때문입니다. 일부 애플리케이션은 여러 프레임에서 확장을 계속하지만 다른 애플리케이션은 즉시 보조 활동으로 시작됩니다.

가능하다면 시작이 애플리케이션 측면에서 완료될 때 reportFullyDrawn 호출(Android 10 이상에서 사용 가능)을 포함하는 것이 좋습니다.

이러한 시스템 추적에서 주의해야 할 사항은 다음과 같습니다.

모니터 경합
그림 1. 모니터로 보호되는 리소스 경쟁은 앱 시작을 크게 지연시킬 수 있음

동기 바인더 트랜잭션
그림 2. 애플리케이션의 중요 경로에서 불필요한 트랜잭션 찾기

동시 가비지 컬렉션
그림 3. 동시 가비지 컬렉션이 일반적이고 영향도 비교적 적지만 자주 하게 되면 Android 스튜디오 메모리 프로파일러를 사용하여 조사하는 것이 좋음

시작 시 I/O
그림 4. 시작 시 I/O 확인 및 긴 중단 찾기

그림 4에서 동시에 I/O를 실행하는 다른 프로세스는 I/O 경합을 유발할 수 있으므로 다른 프로세스가 실행되지 않도록 해야 합니다.

다른 스레드에서 중요한 활동은 UI 스레드를 방해할 수 있으므로 시작 시 백그라운드 작업에 주의합니다. 기기마다 CPU 구성이 다를 수 있으므로 병렬로 실행될 수 있는 스레드 수도 기기마다 다를 수 있습니다.

버벅거림의 일반적인 원인에 관한 가이드도 확인하세요.

Android 스튜디오 메모리 프로파일러 사용

Android 스튜디오 메모리 프로파일러는 메모리 누수나 잘못된 사용 패턴으로 발생할 수 있는 메모리 압력을 줄이는 강력한 도구입니다. 객체 할당 및 컬렉션을 실시간으로 확인할 수 있습니다.

앱에서 발생하는 메모리 문제를 해결하려면 메모리 프로파일러를 사용하여 가비지 컬렉션이 발생하는 이유와 빈도는 물론 시간이 지남에 따라 힙을 계속 증가시키는 메모리 누수가 있을 수 있는지 추적하면 됩니다.

앱 메모리 프로파일링은 다음 단계로 구분됩니다.

1. 메모리 문제 감지

메모리 문제를 감지하려면 먼저 앱의 메모리 프로파일링 세션을 기록합니다. 그런 다음 메모리 공간이 증가하여 결국 가비지 컬렉션 이벤트를 트리거하는 객체를 찾습니다.

증가하는 객체 수
그림 5. 시간이 지남에 따라 증가된 객체 할당을 보여주는 메모리 프로파일러

가비지 컬렉션
그림 6. 가비지 컬렉션 이벤트를 보여주는 메모리 프로파일러{.:image-caption}

메모리 압력을 추가하는 사용 사례를 확인했다면 근본 원인 분석을 시작합니다.

2. 메모리 압력 핫스팟 진단

타임라인에서 범위를 선택하여 할당과 Shallow Size를 모두 시각화합니다.

할당 및 Shallow Size 시각화
그림 7. 타임라인에서 선택한 범위의 할당 및 크기를 보여주는 메모리 프로파일러

이 데이터를 정렬하는 방법에는 여러 가지가 있습니다. 다음 섹션에서는 각 뷰를 통해 문제를 분석할 수 있는 방법에 관한 예를 제공합니다.

클래스별 정렬

클래스별 정렬은 다른 경우에는 메모리 풀에서 캐시되거나 재사용되어야 하는 객체를 생성하는 클래스를 찾으려고 할 때 유용합니다.

예를 들어 'Vertex'라는 클래스의 객체를 초당 2,000개 만드는 앱이 있다고 가정해보겠습니다. 그러면 초당 2,000씩 할당 수가 증가하고 클래스별로 정렬할 때 확인할 수 있습니다. 가비지를 생성하지 않으려면 이러한 객체를 재사용해야 하나요? 답변이 예라면 메모리 풀을 구현해야 할 수 있습니다.

callstack별 정렬

callstack별 정렬은 루프 내부 또는 많은 할당 작업을 하는 특정 함수와 같이 메모리가 할당되는 핫 경로가 있을 때 유용합니다. callstack별 보기를 통해 이러한 할당 핫스팟을 확인할 수 있습니다.

Shallow Size와 Retained Size

Shallow Size는 객체 자체의 메모리만 추적하므로 주로 프리미티브로 구성된 간단한 클래스를 추적하는 데 가장 유용합니다.

Retained Size는 객체에서 직접 할당한 총 메모리는 물론 객체에서 참조하기만 하는 할당된 다른 객체를 보여줍니다. 프리미티브 필드만이 아닌 다른 객체의 할당이 필요한 복잡한 객체로 인한 메모리 압력을 추적하는 데 유용합니다. 이 값을 가져오려면 메모리 프로파일러를 사용하여 메모리 덤프를 만듭니다. 이 힙에 할당된 객체가 디스플레이에 추가됩니다.

전체 메모리 덤프
그림 8. 메모리 프로파일러 툴바에서 Dump Java heap 버튼을 클릭하여 언제든지 메모리 덤프를 만들 수 있음

열로 추가됨
그림 9. 메모리 덤프를 만들면 힙의 객체 할당을 보여주는 열이 표시됨

3. 최적화의 영향 측정

측정하기 쉬운 메모리 최적화 개선사항은 가비지 컬렉션입니다. 최적화로 메모리 압력이 줄면 가비지 컬렉션(GC) 횟수도 줄어듭니다. 이를 측정하려면 프로파일러 타임라인에서 GC 간 시간을 측정하세요. 메모리 최적화 후에는 GC 간 지속 시간이 더 길어집니다.

이와 같은 메모리 개선의 최종 영향은 다음과 같습니다.

  • 앱에 지속적인 메모리 압력이 없으면 메모리 부족 문제로 인해 앱이 종료되는 경우가 줄어듭니다.
  • GC가 줄면 버벅거림 측정항목이 개선됩니다. GC가 CPU 경합을 유발하여 GC가 실행되는 동안 렌더링 작업이 지연될 수 있기 때문입니다.