← Назад к вопросам
Сколько памяти займет объект класса 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:
- Mark Word: 8 bytes (hash, age, locks)
- Class Pointer: 8 bytes (тип объекта)
- Padding: 0 bytes (выравнивание уже есть)
- Total: 16 bytes
Практическое значение:
- Для больших объемов данных используй примитивные типы
- Object overhead существенен для маленьких объектов
- Используй инструменты типа JOL для анализа
- Compressed Oops уменьшает использование памяти ссылками