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

Что такое участок памяти 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
    }
}

Метаданные класса:

  1. Структура класса: поля, методы, конструкторы
  2. Байтокод методов: скомпилированный код
  3. Константный пул: строковые константы, ссылки на методы
  4. Таблица символов: имена методов, полей
  5. Информация о безопасности: исключения, аннотации
  6. 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 HeapNative Memory
РазмерФиксированныйДинамический
Default Max64m-256mПочти не ограничено
Сборка мусораFull GCPer-class loader
ВыгрузкаРедкаяАвтоматическая
OutOfMemoryPermGen SpaceMetaspace

Заключение

  1. Metaspace — это нативная память JVM для хранения метаданных классов
  2. Замена PermGen — решает проблемы с OutOfMemoryError
  3. Автоматическое управление — растёт по потребности
  4. Выгрузка классов — когда удаляется их ClassLoader
  5. Нужен мониторинг — для обнаружения утечек