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

Как устроен по памяти массив примитивов?

1.8 Middle🔥 81 комментариев
#JVM и управление памятью

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

Структура массива примитивов в памяти Java

Это глубокий вопрос о том, как JVM хранит данные. Понимание этого критично для оптимизации производительности и отладки проблем с памятью.

Базовая структура

Массив в Java — это объект с заголовком и блоком данных. Структура выглядит так:

Объект Array:
┌─────────────────────────────────────┐
│ Object Header (12-16 байт)          │
│ - Mark Word (8 байт)                │
│ - Klass Pointer (4 байт, или 8)     │
│ - Array Length (4 байт)             │
├─────────────────────────────────────┤
│ Data (примитивные значения)         │
│ int[] → каждый int занимает 4 байта │
│ long[] → каждый long занимает 8 байт│
│ byte[] → каждый byte занимает 1 байт│
└─────────────────────────────────────┘

Размер заголовка объекта

Object Header состоит из:

  1. Mark Word (8 байт) — хеш-код, флаги блокировки, возраст объекта
  2. Klass Pointer (4-8 байт) — указатель на класс в Metaspace
  3. Array Length (4 байта) — только для массивов, хранит длину

Итого: 16 байт на 64-битной JVM (с compressed OOP)

Пример для int[]

int[] array = new int[100];

// Размер в памяти:
// Object Header: 16 байт
// Data: 100 * 4 = 400 байт
// Padding: 0 байт (уже выровнено на 8 байт)
// Всего: 416 байт

Пример для byte[]

byte[] bytes = new byte[50];

// Размер в памяти:
// Object Header: 16 байт
// Data: 50 * 1 = 50 байт
// Padding: 6 байт (выровнять на 8: 66 → 72)
// Всего: 72 байта

Выравнивание памяти (Alignment)

JVM выравнивает размер объектов на 8 байт по умолчанию (может быть 16). Это улучшает производительность кэша.

int[3]:
- Header: 16 байт
- Data: 3 * 4 = 12 байт
- Сумма: 28 байт
- Padding: 4 байта (до ближайшего кратного 8 = 32)
- Итого: 32 байта

Сравнение с массивом объектов

Очень важное отличие — массив примитивов vs массив объектов:

// Массив примитивов — данные лежат в сплошном блоке
int[] primitives = new int[1000];
// Память: линейная, кэш-friendly

// Массив объектов — ссылки лежат, сами объекты где-то в heap
Object[] objects = new Object[1000];
// Object Header (16 байт)
// 1000 * 8 байт = 8000 байт ссылок
// Сами объекты могут быть разбросаны по heap (плохо для кэша)

Оптимизация в 64-битной JVM

Compressed Ordinary Object Pointers (OOP):

-XX:+UseCompressedOops → Указатели 4 байта вместо 8
-XX:+UseCompressedClassPointers → Klass Pointer тоже 4 байта

Это уменьшает overhead и улучшает кэширование.

Кэширование и производительность

Массив примитивов лучше для производительности:

// ✅ БЫСТРО — данные в одном куске памяти
int[] array = new int[1000000];
for (int i = 0; i < array.length; i++) {
    array[i]++; // CPU может prefetch следующие элементы
}

// ❌ МЕДЛЕННО — ссылки на разрозненные объекты
Integer[] array = new Integer[1000000];
for (Integer i : array) {
    i++; // Каждый Integer может быть где-то далеко в памяти
}

Утечки памяти с массивами

public class Cache {
    private Object[] buffer = new Object[100];
    private int size = 0;
    
    public void add(Object obj) {
        buffer[size++] = obj;
    }
    
    public Object remove() {
        if (size == 0) return null;
        Object obj = buffer[--size];
        // ❌ ПРОБЛЕМА: buffer[size] ещё указывает на объект!
        // GC не может удалить объект, пока массив его "помнит"
        return obj;
    }
    
    // ✅ ПРАВИЛЬНО:
    public Object removeFixed() {
        if (size == 0) return null;
        Object obj = buffer[--size];
        buffer[size] = null; // Явно удалить ссылку
        return obj;
    }
}

Проверка размера объекта

// Использовать JOL (Java Object Layout) для анализа
import org.openjdk.jol.info.ClassLayout;

public class ArrayMemoryTest {
    public static void main(String[] args) {
        int[] array = new int[10];
        System.out.println(ClassLayout.parseInstance(array).toPrintable());
        
        // Вывод покажет точную структуру в памяти
    }
}

Важные выводы

  • Массив примитивов — это объект с линейным блоком данных
  • Заголовок занимает 16 байт на 64-bit JVM
  • Каждый примитив занимает фиксированное место (int=4, long=8, byte=1 и т.д.)
  • Размер выравнивается на 8 байт
  • Массивы примитивов кэш-friendly и быстрее массивов объектов
  • Нужно явно обнулять ссылки в больших массивах, иначе GC не удалит объекты
Как устроен по памяти массив примитивов? | PrepBro