μΉ΄ν…Œκ³ λ¦¬ μ—†μŒ

Unity Addressables 동적 에셋 관리 μ‹œ AsyncOperationHandle λˆ„μˆ˜λ‘œ μΈν•œ 씬 μ „ν™˜ λ©”λͺ¨λ¦¬ ν•΄μ œ μ‹€νŒ¨ 뢄석과 μ°Έμ‘° μΉ΄μš΄νŒ… μ΅œμ ν™”

κ²Œμž„κ΅μˆ˜ 2026. 9. 4. 19:01
λ°˜μ‘ν˜•

🌐 English Abstract

This technical deep-dive addresses memory leak issues caused by unreleased AsyncOperationHandle instances during scene transitions in Unity's Addressable Asset System. It explains the mechanics of underlying AssetBundle reference counting and pinpoints edge cases where async handles remain dangling after scene unloads. Furthermore, it provides robust architectural patterns, C# code implementations, and profiling techniques to automate handle lifecycle management and optimize total memory footprint.

μœ λ‹ˆν‹°(Unity) ν”„λ‘œμ νŠΈκ°€ λŒ€ν˜•ν™”λ˜κ³  동적 λ¦¬μ†ŒμŠ€ λ‘œλ”© 방식이 λ‹¨μˆœ Resources 폴더 λ°©μ‹μ—μ„œ μ–΄λ“œλ ˆμ„œλΈ” 에셋 μ‹œμŠ€ν…œ(Addressable Asset System)으둜 μ„ΈλŒ€κ΅μ²΄κ°€ μ΄λ€„μ§€λ©΄μ„œ, λ©”λͺ¨λ¦¬ κ΄€λ¦¬μ˜ μ£Όλ„κΆŒμ€ μ—”μ§„ λ‚΄λΆ€μ—μ„œ 개발자의 μ½”λ“œλ‘œ μ΄λ™ν–ˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ–΄λ“œλ ˆμ„œλΈ”μ„ λ„μž…ν•œ λ§Žμ€ ν”„λ‘œμ νŠΈκ°€ 개발 μ€‘λ°˜ 이후 κ³΅ν†΅μ μœΌλ‘œ μ§λ©΄ν•˜λŠ” 치λͺ…적인 λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. λ°”λ‘œ 씬 μ „ν™˜(Scene Transition) μ‹œ λ°œμƒν•˜λŠ” λΆˆμ™„μ „ν•œ λ©”λͺ¨λ¦¬ ν•΄μ œ 및 에셋 λ²ˆλ“€(AssetBundle) λˆ„μˆ˜ ν˜„μƒμž…λ‹ˆλ‹€.

κΈ°μ‘΄ `Resources.UnloadUnusedAssets()`λ‚˜ 씬 μ „ν™˜ μ‹œμ˜ μžλ™ GC(Garbage Collection)에 μ˜μ‘΄ν•˜λ˜ μˆ˜λ™μ  λ©”λͺ¨λ¦¬ 관리 방식은 μ–΄λ“œλ ˆμ„œλΈ”μ˜ μ°Έμ‘° μΉ΄μš΄νŒ…(Reference Counting) ꡬ쑰와 μΆ©λŒμ„ μΌμœΌν‚΅λ‹ˆλ‹€. 특히 `AsyncOperationHandle`을 μ •ν™•νžˆ ν•΄μ œν•˜μ§€ μ•ŠμœΌλ©΄ 씬이 μ–Έλ‘œλ“œλ˜μ–΄λ„ 에셋 λ²ˆλ“€μ΄ 램(RAM) 및 VRAM에 영ꡬ히 μƒμ£Όν•˜κ²Œ λ©λ‹ˆλ‹€. λ³Έ ν¬μŠ€νŒ…μ—μ„œλŠ” μ΄λŸ¬ν•œ νŠΈλŸ¬λΈ”μŠˆνŒ… 사둀λ₯Ό λ°”νƒ•μœΌλ‘œ 근본적인 원인을 λΆ„μ„ν•˜κ³  ꡬ쑰적 해결책을 μ œμ‹œν•©λ‹ˆλ‹€.



1. Addressables μ°Έμ‘° μΉ΄μš΄νŒ…(Reference Counting) λ™μž‘ λ©”μ»€λ‹ˆμ¦˜

μ–΄λ“œλ ˆμ„œλΈ” μ‹œμŠ€ν…œμ˜ 핡심은 내뢀적인 μ°Έμ‘° 카운트(Reference Count) κ΄€λ¦¬μž…λ‹ˆλ‹€. μ—μ…‹μ΄λ‚˜ 씬, λ²ˆλ“€μ„ λ‘œλ“œν•  λ•Œλ§ˆλ‹€ ν•΄λ‹Ή λ¦¬μ†ŒμŠ€μ˜ μ°Έμ‘° μΉ΄μš΄νŠΈκ°€ 1μ”© μ¦κ°€ν•˜κ³ , λͺ…μ‹œμ μœΌλ‘œ ν•΄μ œ(Release)λ₯Ό ν˜ΈμΆœν•  λ•Œ 1μ”© κ°μ†Œν•©λ‹ˆλ‹€. 이 μΉ΄μš΄νŠΈκ°€ μ •ν™•νžˆ 0에 λ„λ‹¬ν•΄μ•Όλ§Œ μ‹€μ œ AssetBundle.Unload(true)κ°€ ν˜ΈμΆœλ˜μ–΄ λ©”λͺ¨λ¦¬μ—μ„œ μ œκ±°λ©λ‹ˆλ‹€.

AsyncOperationHandle의 μ—­ν• κ³Ό 수λͺ… μ£ΌκΈ°(Lifecycle)

`Addressables.LoadAssetAsync()` λ˜λŠ” `Addressables.InstantiateAsync()`λ₯Ό ν˜ΈμΆœν•˜λ©΄ μœ λ‹ˆν‹°λŠ” λ‚΄λΆ€ μž‘μ—… μƒνƒœλ₯Ό μΆ”μ ν•˜λŠ” `AsyncOperationHandle` λ˜λŠ” `AsyncOperationHandle` ꡬ쑰체(Struct)λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€. 이 핸듀은 λ‹¨μˆœν•œ 데이터 λž˜νΌκ°€ μ•„λ‹ˆλΌ, λ‚΄λΆ€ C++ 에셋 λ²ˆλ“€ λ©”λͺ¨λ¦¬ ν¬μΈν„°μ˜ μ°Έμ‘° 카운트λ₯Ό μœ μ§€ν•˜λŠ” μ—΄μ‡  역할을 ν•©λ‹ˆλ‹€.

  • Addressables.Release(handle): ν•΄λ‹Ή 핸듀이 κ°€λ¦¬ν‚€λŠ” μ—μ…‹μ˜ μ°Έμ‘° 카운트λ₯Ό 1 κ°μ†Œμ‹œν‚΅λ‹ˆλ‹€. μΉ΄μš΄νŠΈκ°€ 0이 되면 에셋 및 ν¬ν•¨λœ λ²ˆλ“€μ΄ λ©”λͺ¨λ¦¬μ—μ„œ μ–Έλ‘œλ“œλ©λ‹ˆλ‹€.
  • Addressables.ReleaseInstance(gameObject): `InstantiateAsync`둜 μƒμ„±λœ κ²Œμž„ 였브젝트 μΈμŠ€ν„΄μŠ€λ₯Ό 파괴(Destroy)함과 λ™μ‹œμ— λ‚΄λΆ€ ν•Έλ“€μ˜ μ°Έμ‘° 카운트λ₯Ό κ°μ†Œμ‹œν‚΅λ‹ˆλ‹€.

λ¬Έμ œλŠ” `AsyncOperationHandle`이 κ°’ νƒ€μž…(Value Type Struct)μ΄λΌλŠ” μ μž…λ‹ˆλ‹€. μ—¬λŸ¬ ν΄λž˜μŠ€λ‚˜ λ³€μˆ˜ μˆ˜μ‹μ–΄λ‘œ λ³΅μ‚¬λ˜μ–΄ 전달될 수 μžˆμ§€λ§Œ, μ΅œμ’…μ μœΌλ‘œ ν•΄μ œ ν•¨μˆ˜μ— μ „λ‹¬λ˜μ–΄μ•Ό ν•˜λŠ” 것은 μ΅œμ΄ˆμ— λ‘œλ“œλ˜μ–΄ 카운트λ₯Ό μ¦κ°€μ‹œν‚¨ λ™μΌν•œ λ‚΄λΆ€ μž‘μ—… μƒνƒœ(AsyncOperationImpl)μž…λ‹ˆλ‹€. λ§Œμ•½ C# μŠ€ν¬λ¦½νŠΈκ°€ νŒŒκ΄΄λ˜λ©΄μ„œ 이 핸듀을 μžƒμ–΄λ²„λ¦¬κ±°λ‚˜(Orphaned Handle) ν•΄μ œν•˜μ§€ μ•ŠμœΌλ©΄, 에셋 λ²ˆλ“€μ˜ μ°Έμ‘° μΉ΄μš΄νŠΈλŠ” 1둜 κ³ μ •λ˜μ–΄ 영ꡬ적인 λ©”λͺ¨λ¦¬ λˆ„μˆ˜(Memory Leak)κ°€ λ°œμƒν•©λ‹ˆλ‹€.

2. 씬 μ „ν™˜ μ‹œ λ°œμƒ λΉˆλ„κ°€ 높은 μ—£μ§€ μΌ€μ΄μŠ€(Edge Cases) 뢄석

λ‹¨μˆœνžˆ `OnDestroy()`μ—μ„œ `Release()`λ₯Ό λΆ€λ₯΄λŠ” κ²ƒλ§ŒμœΌλ‘œλŠ” λͺ¨λ“  λ©”λͺ¨λ¦¬ λˆ„μˆ˜λ₯Ό 막을 수 μ—†μŠ΅λ‹ˆλ‹€. 싀무 개발 ν™˜κ²½μ—μ„œ λΉˆλ²ˆν•˜κ²Œ λ°œμƒν•˜λŠ” μ—£μ§€ μΌ€μ΄μŠ€λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

μΌ€μ΄μŠ€ A: 비동기 λ‘œλ”© λ―Έμ™„λ£Œ μƒνƒœμ—μ„œ 씬 μ „ν™˜ (Interrupted Async Operation)

에셋 λ‘œλ”©(`LoadAssetAsync`)을 μš”μ²­ν•œ 직후, λ‘œλ”©μ΄ μ™„λ£Œλ˜κΈ° 전에 μœ μ €κ°€ 씬을 μ „ν™˜ν•˜κ±°λ‚˜ λ‹€λ₯Έ μ”¬μœΌλ‘œ μ΄νƒˆν•˜λŠ” κ²½μš°μž…λ‹ˆλ‹€. 비동기 μž‘μ—…(Task λ˜λŠ” Coroutine)이 μ™„λ£Œλ˜μ§€ μ•Šμ€ μƒνƒœμ—μ„œ 씬이 μ–Έλ‘œλ“œλ˜λ©΄, μ™„λ£Œ 콜백(`Completed`)이 ν˜ΈμΆœλ˜μ§€ μ•Šκ±°λ‚˜ 콜백 λ‚΄λΆ€μ—μ„œ μ°Έμ‘°ν•˜λ €λ˜ 객체가 이미 `Null` μƒνƒœκ°€ λ˜μ–΄ ν•΄μ œ 둜직이 μœ μ‹€λ©λ‹ˆλ‹€.

μΌ€μ΄μŠ€ B: InstantiateAsync 호좜 ν›„ Destroy()λ₯Ό μœ λ‹ˆν‹° κΈ°λ³Έ ν•¨μˆ˜λ‘œ 호좜

`Addressables.InstantiateAsync()`둜 μƒμ„±ν•œ Prefab μΈμŠ€ν„΄μŠ€λ₯Ό νŒŒκ΄΄ν•  λ•Œ `UnityEngine.Object.Destroy(gameObject)`λ₯Ό μ‚¬μš©ν•˜λŠ” μ‹€μˆ˜μž…λ‹ˆλ‹€. μœ λ‹ˆν‹°μ˜ κΈ°λ³Έ `Destroy()`λŠ” C# 및 C++ μ—”μ§„ 레벨의 프리 κ²Œμž„μ˜€λΈŒμ νŠΈλ§Œ μ œκ±°ν•  뿐, μ–΄λ“œλ ˆμ„œλΈ”μ˜ μ°Έμ‘° μΉ΄μš΄ν„°λ₯Ό κ°μ†Œμ‹œν‚€μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이둜 인해 씬에 μ˜€λΈŒμ νŠΈλŠ” μ‚¬λΌμ‘Œμ§€λ§Œ memory 상에 에셋 λ²ˆλ“€μ΄ κ·ΈλŒ€λ‘œ λ‚¨μ•„μžˆλŠ” ν˜„μƒμ΄ λ°œμƒν•©λ‹ˆλ‹€.

μΌ€μ΄μŠ€ C: 쒅속 에셋(Dependencies)의 κΌ¬μž„ ν˜„μƒ

A μ”¬μ—μ„œ λ‘œλ“œν•œ UI ν…μŠ€μ²˜κ°€ B μ”¬μ˜ 3D λͺ¨λΈ 머티리얼과 λ™μΌν•œ 에셋 λ²ˆλ“€μ— λ¬Άμ—¬μžˆλŠ” κ²½μš°μž…λ‹ˆλ‹€. A 씬이 μ’…λ£Œλ  λ•Œ 핸듀을 ν•΄μ œν•˜μ§€ μ•ŠμœΌλ©΄, B 씬이 μ’…λ£Œλ˜κ³  B 에셋을 ν•΄μ œν•˜λ”λΌλ„ 곡톡 λ²ˆλ“€μ˜ μ°Έμ‘° μΉ΄μš΄νŠΈκ°€ 0이 λ˜μ§€ μ•Šμ•„ λ©”λͺ¨λ¦¬κ°€ μ§€μ†μ μœΌλ‘œ λˆ„μ λ©λ‹ˆλ‹€.



3. 싀무 νŠΈλŸ¬λΈ”μŠˆνŒ…: λ©”λͺ¨λ¦¬ λˆ„μˆ˜ λ°©μ§€ μ½”λ“œ μ•„ν‚€ν…μ²˜

μ΄λŸ¬ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ 1) 비동기 μ·¨μ†Œ 토큰(CancellationToken)의 동기화, 2) ν•Έλ“€ 좔적 μžλ™ν™” 래퍼(Wrapper) νŒ¨ν„΄, 3) MonoBehavior 수λͺ… μ£ΌκΈ° 연동(Auto-Release Agent)을 κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€.

μ•ˆμ „ν•œ 비동기 λ‘œλ”© 및 μžλ™ ν•΄μ œ μ»΄ν¬λ„ŒνŠΈ κ΅¬ν˜„

μ•„λž˜ μ½”λ“œλŠ” GameObject의 `OnDestroy` νƒ€μž„λ°κ³Ό `CancellationToken`을 κ²°ν•©ν•˜μ—¬, λ‘œλ”© 도쀑 씬이 μ „ν™˜λ˜κ±°λ‚˜ 객체가 νŒŒκ΄΄λ˜λ”λΌλ„ 핸듀이 λ―Έν•΄μ œλ˜λŠ” 일을 μ™„λ²½νžˆ λ°©μ§€ν•˜λŠ” ν”„λ‘œλ•μ…˜ 레벨의 C# νŒ¨ν„΄μž…λ‹ˆλ‹€.

using System;
using System.Threading;
using Cysharp.Threading.Tasks; // UniTask ν™œμš© μΆ”μ²œ
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class AddressableAssetLoader : MonoBehaviour
{
    private AsyncOperationHandle _handle;
    private bool _isLoaded = false;

    /// <summary>
    /// μ•ˆμ „ν•˜κ²Œ 에셋을 λ‘œλ“œν•˜κ³  κ²Œμž„ 였브젝트의 수λͺ… 주기에 λ°”μΈλ”©ν•©λ‹ˆλ‹€.
    /// </summary>
    public async UniTaskVoid LoadAssetSafe<T>(AssetReferenceT<T> assetReference, Action<T> onComplete) where T : UnityEngine.Object
    {
        CancellationToken cancellationToken = this.GetCancellationTokenOnDestroy();

        try
        {
            // 비동기 μž‘μ—… μ‹œμž‘ 및 ν•Έλ“€ μ €μž₯
            _handle = assetReference.LoadAssetAsync();

            // Task λŒ€κΈ° 쀑 씬 μ „ν™˜ λ“±μœΌλ‘œ 인해 GameObjectκ°€ Destroy되면 CancellationException λ°œμƒ
            T result = await _handle.ToUniTask(cancellationToken: cancellationToken);

            if (_handle.Status == AsyncOperationStatus.Succeeded)
            {
                _isLoaded = true;
                onComplete?.Invoke(result);
            }
        }
        catch (OperationCanceledException)
        {
            // 씬 μ „ν™˜ λ“±μœΌλ‘œ λ‘œλ”© 도쀑 객체가 파괴된 경우 처리
            Debug.LogWarning($"[Addressables] Loading canceled for {assetReference.RuntimeKey}. Releasing handle.");
            ReleaseHandle();
        }
        catch (Exception ex)
        {
            Debug.LogError($"[Addressables] Failed to load asset: {ex.Message}");
            ReleaseHandle();
        }
    }

    private void ReleaseHandle()
    {
        if (_handle.IsValid())
        {
            Addressables.Release(_handle);
            _isLoaded = false;
            Debug.Log("[Addressables] Handle successfully released.");
        }
    }

    private void OnDestroy()
    {
        // 이미 μ™„λ£Œλœ λ‘œλ“œμ— λŒ€ν•œ ν•Έλ“€ ν•΄μ œ
        ReleaseHandle();
    }
}

씬 μ „ν™˜ μ‹œ μ „μ—­ ν•Έλ“€ νŠΈλž˜ν‚Ή λ§€λ‹ˆμ € (Addressable Tracker Manager)

κ°œλ³„ 슀크립트 외에도 씬 λ‹¨μœ„λ‘œ 호좜된 λͺ¨λ“  `AsyncOperationHandle`을 μ€‘μ•™μ—μ„œ λ“±λ‘ν•˜κ³ , 씬 μ „ν™˜ 이벀트(`SceneManager.sceneUnloaded`) λ°œμƒ μ‹œ λ―Έν•΄μ œλœ 핸듀을 μ „μˆ˜ μ‘°μ‚¬ν•˜μ—¬ κ°•μ œ ν•΄μ œν•˜λŠ” μ•ˆμ „μž₯치(Safety Net) μ•„ν‚€ν…μ²˜κ°€ ν•„μš”ν•©λ‹ˆλ‹€.

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;

public class AddressableSceneScopeManager : MonoBehaviour
{
    private readonly List<AsyncOperationHandle> _trackedHandles = new List<AsyncOperationHandle>();

    private void Awake()
    {
        SceneManager.sceneUnloaded += OnSceneUnloaded;
    }

    public void TrackHandle(AsyncOperationHandle handle)
    {
        if (handle.IsValid())
        {
            _trackedHandles.Add(handle);
        }
    }

    private void OnSceneUnloaded(Scene current)
    {
        // 씬 μ „ν™˜ μ‹œ λˆ„μˆ˜λœ λͺ¨λ“  ν•Έλ“€ 일괄 Release
        foreach (var handle in _trackedHandles)
        {
            if (handle.IsValid())
            {
                Addressables.Release(handle);
            }
        }
        _trackedHandles.Clear();
        SceneManager.sceneUnloaded -= OnSceneUnloaded;
    }
}

4. Addressables Profiler & Event Viewerλ₯Ό ν™œμš©ν•œ 디버깅 및 뢄석

μ½”λ“œ μž‘μ„± ν›„ μ‹€μ œ λ©”λͺ¨λ¦¬μ—μ„œ 에셋 λ²ˆλ“€μ΄ μ •ν™•ν•˜κ²Œ ν•΄μ œλ˜κ³  μžˆλŠ”μ§€ κ²€μ¦ν•˜λ €λ©΄ μœ λ‹ˆν‹° λ‚΄μž₯ 툴인 Addressables Event Viewerλ₯Ό ν™œμš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

μ„€μ • 및 λͺ¨λ‹ˆν„°λ§ 방법:

  1. `Window` > `Asset Management` > `Addressables` > `Event Viewer` 메뉴λ₯Ό μ—½λ‹ˆλ‹€.
  2. Addressables System Settingsμ—μ„œ Send Profiler Events ν•­λͺ©μ„ ν™œμ„±ν™”(Check)ν•©λ‹ˆλ‹€.
  3. κ²Œμž„ ν”Œλ ˆμ΄ λͺ¨λ“œ μ§„μž… ν›„ 씬 μ „ν™˜μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

πŸ’‘ Event Viewer κ·Έλž˜ν”„ μ½λŠ” 법:
- 녹색 κ·Έλž˜ν”„(Ref Count): νŠΉμ • 에셋 λ²ˆλ“€μ˜ μ°Έμ‘° 카운트λ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€. 씬이 νŒŒκ΄΄λ˜μ—ˆμŒμ—λ„ 녹색 κ·Έλž˜ν”„κ°€ 0으둜 λ–¨μ–΄μ§€μ§€ μ•Šκ³  평행선을 μœ μ§€ν•œλ‹€λ©΄ ν•΄λ‹Ή 씬에 AsyncOperationHandle λˆ„μˆ˜κ°€ μ‘΄μž¬ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.
- 뢄홍색/뢉은색 블둝: λ©”λͺ¨λ¦¬μ— μƒμ£Όν•˜λŠ” μ‹€μ œ 에셋 λ°μ΄ν„°μž…λ‹ˆλ‹€. μ°Έμ‘° μΉ΄μš΄νŠΈκ°€ 0이 λ˜λŠ” μ¦‰μ‹œ ν•΄λ‹Ή 블둝이 ν™”λ©΄μ—μ„œ 사라져야 μ •μƒμž…λ‹ˆλ‹€.



5. μ΅œμ’… μš”μ•½ 및 ν”„λ‘œλ•μ…˜ ν™˜κ²½ ꢌμž₯ κ°€μ΄λ“œ

Unity Addressables 기반의 동적 에셋 관리 μ‹œμŠ€ν…œμ„ μ•ˆμ •μ μœΌλ‘œ μš΄μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ‹€μŒ 원칙을 개발 ν‘œμ€€μœΌλ‘œ μ •μ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

  • InstantiateAsync μ‚¬μš© μ‹œ 단짝 κ·œμΉ™: `InstantiateAsync`둜 λ§Œλ“  κ°μ²΄λŠ” μ ˆλŒ€λ‘œ 일반 `Destroy()`둜 μ§€μš°μ§€ 말고, λ°˜λ“œμ‹œ `Addressables.ReleaseInstance()`λ₯Ό ν†΅ν•΄μ„œλ§Œ νŒŒκ΄΄ν•©λ‹ˆλ‹€.
  • CancellationToken ν•„μˆ˜ 적용: 비동기 λ‘œλ”© λ©”μ„œλ“œλŠ” 씬 μ „ν™˜ μ‹œ λ‘œλ”© ν”„λ‘œμ„ΈμŠ€λ₯Ό μ¦‰μ‹œ μ·¨μ†Œν•  수 μžˆλ„λ‘ UniTask의 `GetCancellationTokenOnDestroy()` λ˜λŠ” C#의 `CancellationToken`을 μ—°κ²°ν•©λ‹ˆλ‹€.
  • 에셋 λ²ˆλ“€ 뢄리 μ΅œμ ν™”: 씬 λ‹¨μœ„λ‘œ μ™„μ „νžˆ 수λͺ… μ£ΌκΈ°κ°€ λ‚˜λˆ„μ–΄μ§€λŠ” 에셋듀은 λ™μΌν•œ 에셋 λ²ˆλ“€ 그룹에 묢이지 μ•Šλ„λ‘ μ–΄λ“œλ ˆμ„œλΈ” κ·Έλ£Ή(Addressable Groups) 섀정을 μ„ΈλΆ„ν™”ν•©λ‹ˆλ‹€. λ¬Άμ—¬μžˆλŠ” 에셋 쀑 ν•˜λ‚˜λΌλ„ μ°Έμ‘° μΉ΄μš΄νŠΈκ°€ 1이면 전체 λ²ˆλ“€μ΄ λ©”λͺ¨λ¦¬μ— 남기 λ•Œλ¬Έμž…λ‹ˆλ‹€.
  • 정기적인 Memory Profiler 캑처: 씬 μ „ν™˜ μ „/ν›„μ˜ λ©”λͺ¨λ¦¬ μŠ€λƒ…μƒ·(Snapshot)을 찍어 Native Memory Object ν•­λͺ©μ— Unloaded λ˜μ§€ μ•Šμ€ `AssetBundleData`κ°€ μž”μ‘΄ν•˜λŠ”μ§€ μ§€μ†μ μœΌλ‘œ 감사(Audit)ν•΄μ•Ό ν•©λ‹ˆλ‹€.

에셋 λ²ˆλ“€μ˜ μ°Έμ‘° μΉ΄μš΄νŒ… 라이프사이클을 μ—„κ²©ν•˜κ²Œ κ΄€λ¦¬ν•˜λŠ” μ•„ν‚€ν…μ²˜λ₯Ό λ„μž…ν•˜λ©΄ 씬 μ „ν™˜ μ‹œ λ°œμƒν•˜λŠ” 원인 뢈λͺ…μ˜ ν¬λž˜μ‹œ(OOM - Out Of Memory)와 λ©”λͺ¨λ¦¬ 핑퐁 ν˜„μƒμ„ μ™„λ²½νžˆ ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ°˜μ‘ν˜•