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

Сколько памяти займет объект класса Object?

1.2 Junior🔥 101 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью

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

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

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

Объем памяти объекта класса Object

Краткий ответ

Пустой объект класса Object занимает 16 байт в 64-bit JVM (на 32-bit JVM - 8 байт). Это состоит из: Object Header (12-16 байт) + padding для выравнивания. Точный размер зависит от JVM и её конфигурации.

Структура объекта в памяти (64-bit JVM)

Object (пустой) на 64-bit JVM:

┌─────────────────────────────────────┐
│ Mark Word (8 bytes)                 │  - хеш, блокировка, GC информация
├─────────────────────────────────────┤
│ Class Pointer (8 bytes)             │  - ссылка на метакласс
├─────────────────────────────────────┤
│ Padding (0 bytes при выравнивании)  │  - выравнивание на 8-byte boundary
└─────────────────────────────────────┘
Итого: 16 байт

Детальный анализ

Mark Word (8 байт)

Mark Word содержит:
- 25 bits: hash code
- 31 bits: GC age (для generational GC)
- 4 bits: state bits (normal, biased, forwarded, etc)
- 4 bits: unused

Он используется для:
- Хеширования объекта (hashCode())
- Блокировки (synchronized)
- Отслеживания поколения (generational GC)
- Биased locking оптимизации

Class Pointer (8 байт)

Указатель на метакласс:
- java.lang.Object::class
- Сохраняет информацию о типе объекта
- Позволяет JVM узнать структуру объекта
- Используется для instanceof и других операций

Практическое измерение

import java.lang.instrument.Instrumentation;

public class ObjectSizeCalculator {
    
    // Способ 1: использование sun.misc.Unsafe
    public static void main(String[] args) {
        Object obj = new Object();
        
        // Приблизительный размер (может быть компилятором)
        System.out.println("Object size: " + 
            sun.misc.Unsafe.getUnsafe().getAddress(
                sun.misc.Unsafe.getUnsafe().staticFieldBase(
                    java.lang.Object.class.getDeclaredFields()[0])));
    }
    
    // Способ 2: использование JOL (Java Object Layout)
    // Добавь dependency: org.openjdk.jol:jol-core:0.16
    public static void printObjectLayout() {
        Object obj = new Object();
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        
        // Вывод:
        // OFFSET  SIZE  TYPE DESCRIPTION
        // 0       4         (object header)
        // 4       4         (object header)
        // 8       4         (object header - on 32-bit JVM)
        // 12      4    (alignment/padding)
        // Instance size: 16 bytes
    }
}

Примеры размеров разных объектов

Простые классы

public class Person {
    private String name;       // 8 bytes (reference)
    private int age;          // 4 bytes
    private double salary;    // 8 bytes
}

// Размер на 64-bit JVM:
// Mark Word:        8 bytes
// Class Pointer:    8 bytes
// name (reference): 8 bytes
// age (int):        4 bytes
// salary (double):  8 bytes
// Padding:          4 bytes (выравнивание)
// ────────────────────────
// Total:           40 bytes

public class ShallowVsDeep {
    public static void main(String[] args) {
        Person person = new Person();
        person.name = "John";
        
        // Shallow size: 40 bytes (только объект Person)
        // Deep size: 40 + String размер + String внутренние данные
        //          = 40 + 56 (String) + 24 (byte[]) + 'John' = ~130 bytes
    }
}

Объекты с примитивами

public class SimpleObject {
    private int x;      // 4 bytes
    private int y;      // 4 bytes
}

// Размер:
// Header:       16 bytes
// x (int):       4 bytes
// y (int):       4 bytes
// ────────────────────
// Total:        24 bytes

public class ObjectWithReference {
    private Object ref;  // 8 bytes (reference)
}

// Размер:
// Header:            16 bytes
// ref (reference):    8 bytes
// ────────────────────
// Total:            24 bytes (16-byte alignment)

Массивы

int[] arr = new int[10];

// Размер:
// Header:         16 bytes
// Length (int):    4 bytes
// Padding:         4 bytes
// Data (10 ints): 40 bytes (10 * 4)
// ────────────────────
// Total:          64 bytes

// Для Object[]:
Object[] arr = new Object[10];

// Header:          16 bytes
// Length (int):     4 bytes
// Padding:          4 bytes
// Data (10 refs):  80 bytes (10 * 8)
// ────────────────────
// Total:          104 bytes

Влияние на производительность

Проблема: Object overhead

// ❌ Неправильно: много маленьких объектов
public class Point {
    private int x;
    private int y;
}

List<Point> points = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
    Point p = new Point();  // 1M объектов!
    p.x = i;
    p.y = i * 2;
    points.add(p);
}

// Память:
// 1M объектов * 16 bytes (header) = 16 MB
// + references в List = 8 MB
// + actual data = 8 MB
// Итого: ~32 MB

// ✅ Правильно: использовать примитивные массивы
int[] xs = new int[1_000_000];
int[] ys = new int[1_000_000];

for (int i = 0; i < 1_000_000; i++) {
    xs[i] = i;
    ys[i] = i * 2;
}

// Память:
// 2 массива * (16 + 1M * 4) = ~8 MB
// Экономия: 4x меньше памяти!

Compressed Oops (Object Pointers)

На 64-bit JVM используется оптимизация для ссылок:

ByDefault (64-bit JVM < 32GB heap):
- Ссылки занимают 4 bytes вместо 8
- Экономия ~50% памяти для ссылок

Ключевая опция JVM:
-XX:+UseCompressedOops

Инструменты для измерения

1. JOL (Java Object Layout)

<!-- pom.xml -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.16</version>
</dependency>
import org.openjdk.jol.info.ClassLayout;

public class MemoryAnalyzer {
    public static void main(String[] args) {
        Object obj = new Object();
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        
        // Вывод:
        // java.lang.Object object internals:
        // OFFSET  SIZE   TYPE DESCRIPTION
        // 0       4        (object header: mark)
        // 4       4        (object header: class)
        // Instance size: 8 bytes
        // Space loss: 0 bytes/0%
    }
}

2. JProfiler

JProfiler предоставляет:
- Heap snapshot анализ
- Memory leak detection
- Детальная информация о объектах
- Allocation tracking

3. YourKit

YourKit показывает:
- Размер каждого объекта
- GC overhead
- Instance count
- Shallow vs Deep size

Оптимизация памяти

1. Уменьшение Object Headers

// ❌ Много маленьких объектов
List<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    numbers.add(i);  // каждый Integer занимает 16 bytes
}

// ✅ Используй примитивные коллекции
IntList numbers = new IntArrayList();
for (int i = 0; i < 1000; i++) {
    numbers.add(i);  // только 4 bytes за число
}

// Экономия: 4x меньше памяти

2. Object Pooling

// Для часто создаваемых объектов
public class ObjectPool {
    private Queue<ExpensiveObject> pool;
    
    public ExpensiveObject acquire() {
        return pool.isEmpty() ? 
            new ExpensiveObject() : 
            pool.poll();
    }
    
    public void release(ExpensiveObject obj) {
        obj.reset();
        pool.offer(obj);
    }
}

3. Структурные изменения

// ❌ Неправильно: много ссылок
public class Node {
    private Object value;
    private Node next;
}

// ✅ Правильно: value field если часто используется
public class Node<T> {
    private T value;
    private Node<T> next;
}

Сравнение 32-bit vs 64-bit JVM

32-bit JVM:
- Mark Word:      4 bytes
- Class Pointer:  4 bytes
- Total:          8 bytes
- Alignment:      8 bytes
- Object size:   16 bytes (обычно)

64-bit JVM:
- Mark Word:      8 bytes
- Class Pointer:  8 bytes
- Total:         16 bytes
- Alignment:     16 bytes (для выравнивания)
- Object size:   16+ bytes

64-bit использует больше памяти, но быстрее

Real-world пример

public class MemoryUsageExample {
    public static void main(String[] args) {
        // 1 миллион пустых объектов
        Object[] objects = new Object[1_000_000];
        for (int i = 0; i < objects.length; i++) {
            objects[i] = new Object();
        }
        
        // Память:
        // Array header: 24 bytes
        // References: 1M * 8 = 8MB
        // Objects: 1M * 16 = 16MB
        // ─────────────────
        // Total: ~24MB
        
        Runtime runtime = Runtime.getRuntime();
        long used = runtime.totalMemory() - runtime.freeMemory();
        System.out.println("Memory used: " + (used / 1024 / 1024) + " MB");
    }
}

Вывод

Пустой Object занимает 16 байт на 64-bit JVM:

  1. Mark Word: 8 bytes (hash, age, locks)
  2. Class Pointer: 8 bytes (тип объекта)
  3. Padding: 0 bytes (выравнивание уже есть)
  4. Total: 16 bytes

Практическое значение:

  • Для больших объемов данных используй примитивные типы
  • Object overhead существенен для маленьких объектов
  • Используй инструменты типа JOL для анализа
  • Compressed Oops уменьшает использование памяти ссылками