면접 질문실전 질문꼬리 질문

안드로이드 메모리 관리와 메모리 누수

skydovesJaewoong Eum (skydoves)||8분 소요

안드로이드 메모리 관리와 메모리 누수

안드로이드는 가비지 컬렉션(Garbage Collection) 메커니즘을 통해 메모리를 관리하며, 애플리케이션에서 더 이상 참조하지 않는 객체를 자동으로 회수합니다. Dalvik과 ART 런타임은 객체의 도달 가능성(reachability)을 추적하고, GC 사이클 동안 도달 불가능한 객체를 해제합니다. 하지만 자동 가비지 컬렉션이 메모리 누수(memory leak)까지 방지해 주지는 않습니다. 메모리 누수는 회수 대상이어야 할 객체가 의도치 않은 참조 체인에 의해 살아 있는 상태로 유지될 때 발생합니다.

안드로이드가 메모리를 할당하고, 추적하고, 회수하는 과정을 이해하는 것은 제한된 리소스 환경에서 높은 성능을 발휘하는 앱을 작성하기 위한 기초가 됩니다. 실무에서도 메모리 관련 문제는 앱 크래시와 ANR의 주요 원인 중 하나이므로, 이 주제를 깊이 있게 학습해 두시면 면접뿐만 아니라 실제 개발에서도 큰 도움이 될 것입니다.

이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.

  • 안드로이드의 가비지 컬렉터가 어떤 객체를 회수할지 결정하는 방식
  • Low Memory Killer가 안드로이드 프로세스 관리에서 수행하는 역할
  • 안드로이드 애플리케이션에서 메모리 누수가 발생하는 대표적인 원인
  • 생명주기 인식 패턴과 도구를 활용한 메모리 누수 탐지 및 예방 방법

안드로이드에서 가비지 컬렉션이 동작하는 방식

ART는 세대별 가비지 컬렉터(generational garbage collector)를 사용하여 힙(heap)을 객체의 나이에 따라 여러 영역으로 나눕니다. 새로 할당된 객체는 Young Generation 영역에 배치되고, 여러 차례 GC 사이클을 거쳐 살아남은 객체는 Old Generation으로 승격됩니다. 이러한 설계는 대부분의 객체가 짧은 수명을 가진다는 관찰에 기반한 최적화 전략입니다.

GC 사이클이 시작되면, 컬렉터는 루트 참조(root reference) 집합에서부터 탐색을 시작합니다. 루트 참조에는 스택 변수, 정적 필드, JNI 참조 등이 포함되며, 이 루트에서 도달 가능한 모든 객체를 순회합니다. 루트에서 도달할 수 없는 객체는 모두 회수 대상이 됩니다. ART는 동시성 컬렉션(concurrent collection) 방식을 채택하여 일시 정지(pause) 시간을 최소화하며, 마킹(marking) 단계의 대부분을 백그라운드 스레드에서 수행하여 애플리케이션이 계속 실행될 수 있도록 합니다.

class ImageProcessor {
    fun processImage(bitmap: Bitmap): Bitmap {
        val tempBuffer = ByteArray(bitmap.byteCount)
        // processImage가 반환된 후 tempBuffer는 GC 대상이 됩니다
        return applyFilter(bitmap, tempBuffer)
    }
}

위 예제에서 tempBuffer는 메서드가 반환된 이후 더 이상 도달할 수 없으므로, 이후 GC 사이클에서 회수됩니다. 이처럼 메서드 내부에서 생성된 지역 변수는 메서드 종료 시 자연스럽게 GC 대상이 되는 것이 일반적입니다.

Low Memory Killer

안드로이드는 여러 앱을 동시에 실행하며, 각 앱은 독립된 프로세스에서 동작합니다. 사용 가능한 메모리가 특정 임곗값 아래로 떨어지면, Low Memory Killer(LMK)가 프로세스를 종료하여 메모리를 확보합니다. 프로세스는 중요도에 따라 다음과 같이 순위가 매겨집니다.

  1. 포그라운드 프로세스: 현재 화면에 보이는 Activity를 실행 중인 프로세스로, 가장 높은 우선순위를 가집니다.
  2. 가시적 프로세스(Visible Process): 부분적으로 화면에 보이는 Activity가 있는 프로세스입니다.
  3. 서비스 프로세스: 백그라운드에서 Service를 실행 중인 프로세스입니다.
  4. 캐시된(백그라운드) 프로세스: 가장 먼저 종료 대상이 됩니다.

시스템은 LMK가 프로세스를 종료하기 전에 앱이 캐시와 불필요한 리소스를 해제할 수 있도록 onTrimMemory() 콜백을 전달합니다. TRIM_MEMORY_RUNNING_LOW, TRIM_MEMORY_UI_HIDDEN 등 다양한 레벨이 있으며, 각 레벨에 맞는 적절한 대응 전략을 구현하는 것이 중요합니다. 이 콜백을 적극적으로 활용하면 LMK에 의한 강제 종료를 상당 부분 예방할 수 있습니다.

이 면접 질문은 구독자 전용입니다

Dove Letter를 구독하시면 안드로이드, 코틀린 개발 관련 독점 면접 질문의 전체 내용을 볼 수 있습니다.

구독하기