← Назад к вопросам
Что такое участок памяти Metaspace?
1.8 Middle🔥 161 комментариев
#JVM и управление памятью
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Metaspace в Java
Metaspace — это участок памяти, который используется JVM для хранения метаданных о классах, методах, полях и другой информации, необходимой для выполнения программы. В Java 8+ Metaspace заменил PermGen (Permanent Generation) из более ранних версий.
История: PermGen → Metaspace
PermGen (Java 7 и ранее)
heap
├── Young Generation (Eden, S0, S1)
├── Old Generation
└── PermGen ← отдельный участок heap для метаклассов
PercGen был частью heap, имел фиксированный размер:
# Конфигурация старых версий
java -Xms1024m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m MyApp
Проблемы PermGen:
- Ограниченный размер → OutOfMemoryError: PermGen space
- Full GC на PermGen был дорогим
- Трудно управлять размером
- Классы не могли быть выгружены в конце программы
Metaspace (Java 8+)
heap
├── Young Generation (Eden, S0, S1)
├── Old Generation
native memory (системная память)
├── Metaspace ← за пределами heap
│ ├── Compressed class space (классы)
│ └── NonClass метаданные (методы, поля, константы)
└── Code Cache
Что хранится в Metaspace
public class Example {
private int field; // metadata в Metaspace
public void method() { // информация о методе → Metaspace
int localVar = 10; // локальная переменная → stack
String obj = "hello"; // объект → heap
}
}
Метаданные класса:
- Структура класса: поля, методы, конструкторы
- Байтокод методов: скомпилированный код
- Константный пул: строковые константы, ссылки на методы
- Таблица символов: имена методов, полей
- Информация о безопасности: исключения, аннотации
- Code Cache: JIT-скомпилированный нативный код
public class MetadataExample {
private String name = "John"; // поле → Metaspace metadata
private int age = 30; // поле → Metaspace metadata
public void greet() { // metadata метода → Metaspace
System.out.println("Hello"); // bytecode → Metaspace
}
}
// В Metaspace хранится:
// - Класс MetadataExample (структура)
// - Информация о полях: name (String), age (int)
// - Информация о методе: greet()
// - Bytecode метода greet
// - Ссылка на System.out.println
Различия Metaspace от PermGen
1. Местоположение памяти
PermGen (Java 7):
┌─────────────────────────────────┐
│ JVM Heap │
├─────────────────────────────────┤
│ Young Gen │ Old Gen │ PermGen │
└─────────────────────────────────┘
Ограничено размером heap
Metaspace (Java 8+):
┌─────────────────────────────────┐ ┌──────────────────┐
│ JVM Heap │ │ Native Memory │
├──────────┬──────────────────────┤ ├──────────────────┤
│ Young Gen│ Old Gen │ │ Metaspace │
└──────────┴──────────────────────┘ ├──────────────────┤
│ Code Cache │
└──────────────────┘
Ограничено системной памятью (почти не ограничено)
2. Размер по умолчанию
# Java 7 (PermGen) — фиксированный размер
java -Xms1024m -Xmx2048m \
-XX:PermSize=64m \
-XX:MaxPermSize=256m MyApp
# Java 8+ (Metaspace) — автоматический, не ограничен
java -Xms1024m -Xmx2048m MyApp
# Metaspace растёт по потребности
# Можно ограничить если нужно:
java -XX:MetaspaceSize=128m \
-XX:MaxMetaspaceSize=512m MyApp
3. Сборка мусора
// PermGen
// Мусор в PermGen собирался редко и дорого (full GC)
public class ClassLoader {
// Классы загружались в PermGen и оставались там
// Выгрузка классов была редкой и сложной
}
// Metaspace
// Классы выгружаются когда удаляется ClassLoader
public class DynamicClassLoader {
ClassLoader loader = new URLClassLoader(urls);
// Когда loader становится недостижимым
loader = null;
System.gc(); // Metaspace можно освободить
// Все классы, загруженные этим loader'ом, удаляются из Metaspace
}
Структура Metaspace (Java 8+)
Metaspace
├── Compressed Class Space (если используется -XX:+UseCompressedClassPointers)
│ └── Хранит метаданные классов (~ 1/3 всех метаданных)
│
└── NonClass метаданные
├── Методы
├── Поля
├── Константный пул
├── Таблица символов
├── Аннотации
└── Информация о безопасности
Outofmemory в Metaspace
// Типичная причина: утечка ClassLoader
public class MemoryLeak {
public static void main(String[] args) throws Exception {
while(true) {
// Создаём новый ClassLoader с новыми классами
URLClassLoader loader = new URLClassLoader(new URL[] {});
// Генерируем класс (например, через bytecode)
byte[] classBytes = generateClass(); // много кода
// Но забываем удалить ссылку на ClassLoader
// или класс остаётся в памяти
// Метаспейс растёт, никогда не очищается
// OutOfMemoryError: Metaspace
}
}
}
// Правильно:
public class NoLeak {
public static void main(String[] args) throws Exception {
for(int i = 0; i < 100; i++) {
try(URLClassLoader loader = new URLClassLoader(new URL[] {})) {
byte[] classBytes = generateClass();
Class<?> clazz = loadClass(loader, classBytes);
// Используем класс
} // loader закрывается, классы выгружаются
}
}
}
Мониторинг Metaspace
import java.lang.management.*;
public class MetaspaceMonitoring {
public static void main(String[] args) {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
// Информация о Metaspace
for(MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
if(pool.getName().contains("Metaspace")) {
MemoryUsage usage = pool.getUsage();
System.out.println("Metaspace used: " + usage.getUsed());
System.out.println("Metaspace committed: " + usage.getCommitted());
System.out.println("Metaspace max: " + usage.getMax());
}
}
}
}
GC в Metaspace
# JVM опции для контроля Metaspace GC
java -XX:MetaspaceSize=128m \
-XX:MaxMetaspaceSize=512m \
-XX:CompressedClassSpaceSize=256m \
-XX:+PrintGCDetails \
MyApp
# В логах GC видно:
# [GC (Metadata GC Threshold) ... PS Scavenge ...]
# [Full GC (Metadata GC Threshold) ... PS MarkSweep ...]
Best Practices
// 1. Управляй ClassLoader'ами
URLClassLoader loader = new URLClassLoader(urls);
try {
// Используй loader
} finally {
loader.close(); // Важно!
}
// 2. Избегай динамического создания классов
// ❌ Плохо: создание класса в цикле
for(int i = 0; i < 10000; i++) {
Class<?> clazz = generateAndLoadClass(); // утечка
}
// ✅ Хорошо: переиспользование классов
List<String> classNames = generateClassNames();
for(String name : classNames) {
Class<?> clazz = Class.forName(name);
}
// 3. Мониторь использование Metaspace
// Используй JMX или -XX:+PrintGCDetails
// 4. Задавай разумные лимиты
// -XX:MaxMetaspaceSize=512m для production
Сравнительная таблица
| Параметр | PermGen (Java 7) | Metaspace (Java 8+) |
|---|---|---|
| Местоположение | Part of Heap | Native Memory |
| Размер | Фиксированный | Динамический |
| Default Max | 64m-256m | Почти не ограничено |
| Сборка мусора | Full GC | Per-class loader |
| Выгрузка | Редкая | Автоматическая |
| OutOfMemory | PermGen Space | Metaspace |
Заключение
- Metaspace — это нативная память JVM для хранения метаданных классов
- Замена PermGen — решает проблемы с OutOfMemoryError
- Автоматическое управление — растёт по потребности
- Выгрузка классов — когда удаляется их ClassLoader
- Нужен мониторинг — для обнаружения утечек