Оптимізація використання RAM в іграх
Крах без попередження на пристроях з 3 ГБ RAM — це не випадковість. Це результат накопленого memory pressure, який iOS та Android тихо копять, поки не убивають процес. SIGKILL приходить без логу, без стеку, без пояснень. Розробники часто списують це на «нестабільність руху», хоча реальна причина — неуправління ростом heap в Mono/IL2CPP та несвоєчасна виконання ассетів.
Проблема в тому, що Unity Memory Profiler часто показує «все нормально» — 400 МБ, видається, некритично. Але native-пам'ять, яку утримують об'єкти Texture2D без явних посилань, там не видна. Потрібен Total System Memory з Profiler window, а не тільки Managed Heap.
Три джерела витоків, які ми знаходимо в кожному другому проекті
Незакриті AssetBundle-посилання. Розробник завантажив AssetBundle, витяг з нього спрайт, але не викликав bundle.Unload(false). Спрайт в пам'яті. Потім спрайт знищений, але нативний об'єкт Texture2D все ще утримується через WeakReference в ResourceManager. Після 10 завантажень/виконання локацій — пам'ять не повертається. Класична фрагментація Unity native heap. Рішення: перехід на Addressables з явним управлінням часом життя через AsyncOperationHandle.Release().
Дублювання текстур при зміні сцен. При переході між сценами через SceneManager.LoadScene з параметром LoadSceneMode.Single стара сцена виконується, але якщо в новій сценці є текстури з тими ж іменами, завантажені через Resources.Load в коді — вони можуть опинитися в пам'яті двічі до виклику Resources.UnloadUnusedAssets(). На проектах з важкими сценами (100+ МБ текстур) це призводить до піку споживання пам'яті в момент переходу — саме тоді відбуваються крахи.
AudioClip з неправильними настройками Load Type. AudioClip з Load Type = Decompress On Load розпаковує PCM в пам'ять при завантаженні та тримає там. Для довгої музичної теми це може бути 50–80 МБ тільки для одного клипу. Правило: музика → Streaming, короткі SFX → Compressed In Memory, критичні SFX з мінімальною затримкою → Decompress On Load тільки якщо довжина < 2 секунд.
Як ми працюємо з пам'яттю
Memory Profiler (com.unity.memoryprofiler) — основний інструмент. Робимо снимок в кількох точках ігрового сеансу: після старту, після завантаження першої сцени, в середині геймплею, після зміни сцени. Порівнюємо через Compare Snapshots. Шукаємо об'єкти, які зростають від снимка до снимка і не повинні.
Особливо важливо звернути увагу на Texture2D в списку Objects. Сортуємо по Memory Size, відкриваємо References для підозрілих об'єктів — дивимось, хто їх утримує.
Addressables як архітектурне рішення. Перехід з Resources на Addressables дає явний контроль над часом життя ассетів. AssetReference + LoadAssetAsync + Release — повний цикл без «магії». Настроюємо профілі пам'яті через Addressables Analyze: Check Duplicate Bundle Dependencies знаходить ассети, упаковані в кілька бандлів одночасно (типова причина дублювання в пам'яті).
З практики: мобільний екшен, 9 рівнів. Після прохождення 3 рівнів підряд — крах на iPhone 8. Memory Profiler показав 847 МБ при старті 4 рівня. Джерело — 12 унікальних UI-атласів, завантажених через Resources.Load в Lobby-сценці, не виконані між рівнями. Після переносу на Addressables з явним Release при входженні в ігрову сцену та Resources.UnloadUnusedAssets в coroutine — пік знизився до 480 МБ.
Пул об'єктів замість Instantiate/Destroy. Кожен Instantiate виділяє нову пам'ять, кожен Destroy не повертає її миттєво — GC Alloc накопичується. Object Pool через Unity ObjectPool<T> (вбудована з 2021 LTS) повністю усуває цю категорію аллокацій для снарядів, ворогів, VFX.
Етапи роботи
- Зняття baseline-метрик через Profiler на цільовому пристрої (не Editor)
- Серія Memory Profiler snapshots по ігровому циклу
- Аналіз топ-10 об'єктів за споживанням пам'яті
- Виявлення джерел витоків через Compare Snapshots
- Пріоритизація по impact: текстури → AudioClip → Managed Heap → пулинг
- Реалізація виправлень з проміжними замірами
- Нагрузкове тестування: 1 година ігрової сесії без рестарту
| Масштаб задачі | Орієнтовні терміни |
|---|---|
| Аудит пам'яті + звіт | 2–4 дні |
| Усунення 2–3 конкретних джерел витоків | 1–2 тижні |
| Перехід Resources → Addressables + оптимізація | 3–6 тижнів |
| Повна архітектурна переробка управління ассетами | 6–12 тижнів |
Вартість — після аудиту поточного стану проекту.





