Оптимизация RAM в играх: от аудита до внедрения
Краш без предупреждения на устройствах с 3 ГБ RAM — не случайность. В нашей практике каждый второй проект сталкивается с утечками памяти Unity. iOS и Android тихо копят memory pressure, пока не убивают процесс SIGKILL без лога и стека. Разработчики часто списывают это на «нестабильность движка», хотя реальная причина — неуправляемый рост heap в Mono/IL2CPP и несвоевременная выгрузка ассетов. Мы гарантируем анализ с Memory Profiler выявляет реальные источники утечек, а не поверхностные показатели.
Проблема в том, что Unity Memory Profiler часто показывает «всё нормально» — 400 МБ, казалось бы, некритично. Но native-память, которую удерживают Texture2D объекты без явных ссылок, там не видна. Нужен Total System Memory из Profiler window, а не только Managed Heap. Наш опыт показывает, что игнорирование этого приводит к крашам на этапе тестирования.
Почему стандартный Profiler не показывает всей картины?
Unity Memory Profiler (com.unity.memoryprofiler) делает snapshot, но его Managed Heap часто не отражает реальное потребление native-ресурсов. Текстуры, шейдеры, AudioClip — всё это живёт вне C#-кучи. Мы используем Compare Snapshots в нескольких точках сессии: после старта, после загрузки сцены, в середине геймплея, после смены сцены. Только так видно объекты, растущие от снимка к снимку. Это проверенный метод, наработанный за 7 лет оптимизаций (см. документацию Unity Memory Profiler).
Какие три источника утечек мы находим в каждом втором проекте?
Незакрытые AssetBundle-ссылки. Разработчик загрузил AssetBundle, достал из него спрайт, но не вызвал bundle.Unload(false). Спрайт в памяти. Потом спрайт уничтожен, но нативный объект Texture2D всё ещё удерживается через WeakReference в ResourceManager. Через 10 загрузок/выгрузок локаций — память не возвращается. Это классическая фрагментация Unity native heap. Решение: переход на Addressables с явным управлением временем жизни через AsyncOperationHandle.Release(). Addressables лучше Resources: они дают явный контроль и сокращают пиковое потребление в 2–3 раза.
Дублирование текстур при смене сцен. При переходе между сценами через SceneManager.LoadScene с LoadSceneMode.Single старая сцена выгружается, но если в новой сцене есть текстуры с теми же именами, загруженные через Resources.Load в коде — они могут оказаться в памяти дважды до вызова Resources.UnloadUnusedAssets(). На проектах с тяжёлыми сценами (100+ МБ текстур) это приводит к пику потребления в момент перехода — именно тогда происходят крэши. Экономия после исправления: до $2000 в месяц на облачных тестах за счёт снижения числа рестартов.
AudioClip с неправильными настройками Load Type. AudioClip с Load Type = Decompress On Load распаковывает PCM в память при загрузке и держит там. Для длинной музыкальной темы это может быть 50–80 МБ только для одного клипа. Правило: музыка → Streaming, короткие SFX → Compressed In Memory, критичные SFX с минимальной задержкой → Decompress On Load только если длина < 2 секунд. Этот подход — гарантия стабильной работы на устройствах с 2 ГБ RAM.
| Load Type | Use Case | Память | Задержка |
|---|---|---|---|
| Decompress On Load | Короткие SFX | Высокая | Низкая |
| Compressed In Memory | Средние звуки | Средняя | Средняя |
| Streaming | Музыка, длинные | Низкая | Высокая |
Как Addressables решают проблему дублирования?
Переход с Resources на Addressables даёт явный контроль над временем жизни ассетов. AssetReference + LoadAssetAsync + Release — полный цикл без «магии». Настраиваем профили памяти через Addressables Analyze: Check Duplicate Bundle Dependencies находит ассеты, упакованные в несколько бандлов одновременно (типичная причина дублирования). В одном проекте это снизило пиковое потребление с 847 МБ до 480 МБ на iPhone 8 (см. кейс ниже).
Пример из практики: мобильный экшен, 9 уровней. После прохождения 3 уровней подряд — краш на iPhone 8. Memory Profiler показал 847 МБ при старте 4 уровня. Источник — 12 уникальных UI-атласов, загруженных через Resources.Load в Lobby-сцене, не выгружались между уровнями. После переноса на Addressables с явным Release при входе в игровую сцену и Resources.UnloadUnusedAssets в coroutine — пик снизился до 480 МБ. Экономия составила $1500 в месяц на ферме устройств.
Пул объектов вместо Instantiate/Destroy. Каждый Instantiate выделяет новую память, каждый Destroy не возвращает её мгновенно — GC Alloc накапливается. ObjectPool<T> из Unity 2021 LTS полностью устраняет эту категорию аллокаций для снарядов, врагов, VFX. Сравнение: при 1000 спаунов пул даёт нулевые аллокации, тогда как Instantiate/Destroy тратит 10+ МБ на GC.
Что входит в нашу работу?
- Аудит текущего состояния памяти с Memory Profiler snapshots на целевом устройстве
- Подробный отчёт с топ-10 источниками утечек и приоритетами
- Реализация исправлений: Addressables, пул объектов, оптимизация AudioClip, исправление AssetBundle
- Повторное нагрузочное тестирование (1 час без рестарта)
- Обучение команды: работа с Profiler, Addressables, best practices
- Гарантия на результат: стабильность на устройствах с 3 ГБ RAM — письменное обязательство
Наша команда — 7+ лет в геймдеве, более 50 проектов оптимизации под iOS и Android. Мы работаем под ключ — от аудита до внедрения.
Этапы работы
- Снятие 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 недель |
Свяжитесь с нами для консультации. Если ваша игра падает на старых девайсах — получите аудит за 2 дня и узнайте реальные источники утечек. Закажите аудит памяти уже сегодня — оценим проект в течение 24 часов.






