← Назад к вопросам

Как Array хранится в оперативной памяти?

1.3 Junior🔥 171 комментариев
#Управление памятью

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Хранение массива в памяти Unity/C#

В Unity, которая использует C# как основной язык, массив (Array) хранится в оперативной памяти как непрерывный блок. Это фундаментальная структура данных, и понимание её организации критично для оптимизации производительности в играх.

Основной принцип хранения

Массив в C# — это ссылочный тип (Reference Type), который хранится в управляемой куче (Managed Heap). Однако, в отличие от других ссылочных типов, сам массив управляет блоком непрерывной памяти для своих элементов.

Когда вы объявляете массив:

int[] numbers = new int[5];

В памяти происходит следующее:

  1. Переменная numbers (сама ссылка) хранится обычно в стеке вызовов (Stack), если это локальная переменная метода.
  2. Оператор new выделяет в управляемой куче объект массива. Этот объект содержит метаданные, такие как длина массива, тип элементов и синхронный блок.
  3. Непосредственно за метаданными в том же непрерывном блоке кучи выделяется память под все элементы массива. Для new int[5] это будет блок, способный вместить 5 значений типа int.

Визуально это можно представить так:

[ Стек ]
numbers -> 0x1234 (адрес в куче)

[ Куча по адресу 0x1234 ]
[ Mетаданныe массива (тип, длина, sync block) ]
[ numbers[0] ] // 4 байта для int
[ numbers[1] ] // 4 байта для int
[ numbers[2] ] // 4 байта для int
[ numbers[3] ] // 4 байта для int
[ numbers[4] ] // 4 байта для int

Ключевые особенности и последствия для разработчика в Unity

  • Непрерывность памяти (Contiguous Memory): Все элементы расположены друг за другом. Это главное преимущество для производительности. Процессор может эффективно предзагружать данные в кэш (принцип локальности данных), что делает итерацию по массиву очень быстрой.

    // Этот цикл выполняется максимально эффективно благодаря последовательному доступу
    for(int i = 0; i < numbers.Length; i++) {
        numbers[i] = i * 2;
    }
    
  • Фиксированный размер: Память выделяется сразу при создании. Изменить размер существующего массива нельзя. Array.Resize создает новый массив и копирует в него старые данные, что затратно по производительности и создает мусор для сборщика.

  • Ссылочные vs значимые типы элементов:

    *   Если массив хранит **значимые типы (Value Types)** — `int`, `float`, `Vector3`, `struct` — то в непрерывном блоке хранятся непосредственно их значения.
    *   Если массив хранит **ссылочные типы (Reference Types)** — `GameObject`, `string`, `class` — то в непрерывном блоке хранятся **ссылки** (указатели) на эти объекты. Сами объекты лежат в куче в произвольных местах. Это может приводить к **промахам кэша (cache misses)** при обходе.
```csharp
GameObject[] enemies = new GameObject[100]; // В массиве лежат 100 ссылок
// Сами префабы или инстансы GameObject разбросаны по куче.
```

Важность в контексте Unity и оптимизации

  1. Оптимизация итераций: Для критического кода производительности (Update, работа с тысячами объектов) массивы — предпочтительный выбор по сравнению с List<T> из-за предсказуемости и непрерывности памяти. List<T> внутри использует массив, но добавляет уровень абстракции.

  2. Сборка мусора (Garbage Collection): Создание новых массивов в куче, особенно в игровом цикле, генерирует аллокации в управляемой куче, что провоцирует частые вызовы Garbage Collector (GC). Это одна из главных причин просадок FPS.

    // ПЛОХО: Создает новый массив каждый кадр -> аллокация -> нагрузка на GC.
    void Update() {
        int[] frameData = new int[100];
    }
    
    // ЛУЧШЕ: Использовать пул или выделить массив один раз.
    private int[] _frameData = new int[100];
    void Update() {
        // Переиспользуем существующий массив
    }
    
  3. Unsafe-код и фиксированные буферы: В высокопроизводительных сценариях (обработка больших данных, нативные плагины) используется fixed и unsafe для прямого доступа к памяти массивов, чтобы избежать проверок границ и для взаимодействия с нативным кодом.

  4. Массивы структур (Array of Structs): Частая оптимизация в Unity ECS-подобных подходах. Создается массив из пользовательских структур (struct), что гарантирует компактное, последовательное хранение всех данных в памяти, максимизируя использование кэша процессора. Это фундаментальный принцип Data-Oriented Design.

Итог: Массив в памяти Unity/C# — это непрерывный блок в управляемой куче. Его эффективность заключается в локальности данных, что минимизирует промахи кэша процессора. Для разработчика игр понимание этого является основой для написания производительного кода, минимизации аллокаций и снижения нагрузки на сборщик мусора, что напрямую влияет на стабильность частоты кадров.