효과적인 게임 메모리 관리

Android 플랫폼에서 시스템은 최대한 많은 시스템 메모리(RAM)를 사용하려고 하고 다양한 메모리 최적화를 실행하여 필요할 때 공간을 확보합니다. 이러한 최적화가 게임에 미칠 수 있는 부정적인 영향은 게임 속도가 느려지거나 게임이 완전히 종료되는 것입니다. 이러한 최적화에 관한 자세한 내용은 프로세스 간 메모리 할당을 참고하세요.

이 페이지에서는 게임에 영향을 미치는 메모리 부족 상태를 방지하기 위해 실행할 수 있는 단계를 설명합니다.

onTrimMemory()에 응답

시스템은 onTrimMemory()를 사용하여 메모리 부족으로 인해 앱이 종료될 수 있다고 앱에 알립니다. 대부분 앱에서 수신하는 유일한 경고입니다. 이 콜백에는 로우 메모리 킬러(LMK)를 기준으로 긴 지연 시간이 있으므로 콜백에 빠르게 응답하는 것이 중요합니다.

이 콜백에 응답하여 할당 속도와 수, 크기를 줄입니다. onTrimMemory()는 심각도를 나타내는 상수를 전달하지만 onTrimMemory()가 반응할 수 있는 것보다 빠르게 할당할 수 있으므로 첫 번째 경고에 응답해야 합니다.

Kotlin

class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
    override fun onTrimMemory(level: Int) {
        when (level) {
            ComponentCallbacks2.TRIM_MEMORY_MODERATE,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> // Respond to low memory condition
            else -> Unit
        }
    }
}

자바

public class MainActivity extends AppCompatActivity implements ComponentCallbacks2 {
    public void onTrimMemory(int level) {
        switch (level) {
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
              // Respond to low memory condition
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
              // Respond to low memory condition
                break;
            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
              // Respond to low memory condition
                break;
            default:
                break;

C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

class LowMemoryTrigger : MonoBehaviour
{
    private void Start()
    {
        Application.lowMemory += OnLowMemory;
    }
    private void OnLowMemory()
    {
        // Respond to low memory condition (e.g., Resources.UnloadUnusedAssets())
    }
}

보수적인 메모리 예산 책정

메모리 예산을 보수적으로 책정하여 메모리 부족을 방지하세요. 고려할 항목은 다음과 같습니다.

  • 실제 RAM 크기: 게임은 기기에서 실제 RAM 용량의 ¼~½을 사용하는 경우가 많습니다.
  • 최대 zRAM 크기: zRAM이 많을수록 게임에는 할당할 메모리가 더 많아질 수 있습니다. 이 양은 기기에 따라 다를 수 있습니다. /proc/meminfoSwapTotal에서 이 값을 확인하세요.
  • OS의 메모리 사용량: 시스템 프로세스에 더 많은 RAM을 지정하는 기기에는 게임에 사용할 메모리가 적습니다. 시스템은 시스템 프로세스를 종료하기 전에 게임 프로세스를 종료합니다.
  • 설치된 앱의 메모리 사용량: 설치된 앱이 많은 기기에서 게임을 테스트합니다. 소셜 미디어 앱과 채팅 앱은 지속적으로 실행되어야 하므로 사용 가능한 메모리의 양에 영향을 미칩니다.

메모리 예산을 보수적으로 세울 수 없다면 좀 더 유연한 접근 방식을 사용합니다. 시스템에 메모리 부족 문제가 발생하면 게임에서 사용하는 메모리 양을 줄입니다. 예를 들어 onTrimMemory()에 응답하여 해상도가 낮은 텍스처를 할당하거나 더 적은 셰이더를 저장합니다. 이러한 동적 메모리 할당 접근 방식에는 개발자의 작업이 더 많이 필요한데 특히 게임 디자인 단계에서 그렇습니다.

스래싱 피하기

스래싱은 사용 가능한 메모리가 부족하지만 게임을 종료할 만큼 부족하지는 않을 때 발생합니다. 이 상황에서 kswapd가 게임에 여전히 필요한 페이지를 회수했으므로 메모리에서 페이지를 다시 로드하려고 합니다. 공간이 충분하지 않아서 페이지가 계속 교체됩니다(연속 교체). 시스템 추적은 이러한 상황을 kswapd가 계속 실행되는 스레드로 보고합니다.

스래싱의 한 가지 증상은 긴 프레임 시간으로, 2초 이상일 수 있습니다. 이 상황을 해결하려면 게임의 메모리 공간을 줄이세요.

사용 가능한 도구 사용

Android에는 시스템에서 메모리를 관리하는 방법을 이해하는 데 도움이 되는 도구 모음이 있습니다.

Meminfo

이 도구는 할당된 PSS 메모리의 양과 메모리가 사용된 카테고리를 보여 주는 메모리 통계를 수집합니다.

다음 방법 중 하나로 meminfo 통계를 출력하세요.

  • adb shell dumpsys meminfo package-name 명령어를 사용합니다.
  • Android Debug API의 MemoryInfo 호출을 사용합니다.

PrivateDirty 통계는 디스크로 페이징할 수 없고 다른 프로세스와 공유되지 않는 프로세스 내의 RAM 양을 보여 줍니다. 이 양의 대부분은 이 프로세스가 종료될 때 시스템에서 사용할 수 있게 됩니다.

메모리 tracepoint

메모리 tracepoint는 게임에서 사용하는 RSS 메모리 양을 추적합니다. RSS 메모리 사용량을 계산하는 작업은 PSS 사용량을 계산하는 것보다 훨씬 빠릅니다. 더 빠르게 계산되므로 RSS는 최대 메모리 사용량을 더 정확하게 측정하도록 메모리 크기의 변경사항을 자세하게 보여 줍니다. 따라서 게임의 메모리가 부족해질 수 있는 최댓점을 쉽게 알 수 있습니다.

Perfetto 및 장기 트레이스

Perfetto는 기기에서 성능 및 메모리 정보를 수집하여 웹 기반 UI에 표시하는 도구 모음입니다. 장기 트레이스를 임의로 지원하므로 시간 경과에 따른 RSS의 변화를 확인할 수 있습니다. 오프라인 처리를 위해 생성하는 데이터에 SQL 쿼리를 실행할 수도 있습니다. 시스템 추적 앱에서 장기 트레이스를 사용 설정합니다. 트레이스에 memory:Memory 카테고리가 사용 설정되어 있는지 확인합니다.

heapprofd

heapprofd는 메모리 추적 도구로, Perfetto의 일부입니다. 이 도구는 malloc를 사용하여 메모리가 할당된 위치를 표시함으로써 메모리 누수를 찾는 데 도움이 될 수 있습니다. heapprofd는 Python 스크립트를 사용하여 시작할 수 있고 Malloc Debug와 같은 다른 도구처럼 성능에 영향을 미치지 않습니다(오버헤드가 낮기 때문).

bugreport

bugreport는 메모리가 부족하여 게임이 비정상 종료되었는지 확인하는 로깅 도구입니다. 도구에서는 logcat을 사용하는 것보다 훨씬 자세한 내용을 출력합니다. 게임이 메모리가 부족하여 비정상 종료되었는지 또는 LMK로 인해 종료되었는지 보여 주므로 메모리 디버깅에 유용합니다.

자세한 내용은 버그 신고 캡처 및 읽기를 참고하세요.