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

Какие классы в Metaspace знаешь

2.0 Middle🔥 151 комментариев
#Docker, Kubernetes и DevOps#Основы Java

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

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

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

Metaspace в Java: Структура и классы

Что такое Metaspace?

Metaspace — это область памяти в Java 8+, где JVM хранит метаинформацию о классах (metadata). Это часть нативной памяти (native heap), а не Java heap.

История: PermGen → Metaspace

Java 7 и ранее: PermGen (Permanent Generation)

  • Жёсткий лимит памяти
  • Часто вызывал OutOfMemoryError: PermGen space
  • Размер: -XX:PermSize, -XX:MaxPermSize

Java 8+: Metaspace

  • Использует нативную память
  • Автоматический resize
  • По умолчанию лимит = общая памяти системы (может быть отредактирован)
  • Размер: -XX:MetaspaceSize, -XX:MaxMetaspaceSize

Что хранится в Metaspace?

Metaspace (Native Memory)
├── Class Metadata
│   ├── Class Structure (структура класса)
│   ├── Field Descriptors (описание полей)
│   ├── Method Descriptors (описание методов)
│   ├── Bytecode (бинарный код методов)
│   ├── Constant Pool (пул констант)
│   ├── Annotations (аннотации класса)
│   └── Access Flags (модификаторы доступа)
├── Method Code (compiled код от JIT-компилятора)
├── String Interned Pool (для intern() строк)
├── Klass Names (имена классов)
└── Method Tables (таблицы методов для dispatch)

Ключевые классы метаинформации

1. Klass (Основной класс метаинформации)

Описание: внутренний класс JVM, представляющий метаинформацию о Java классе.

// Это внутреннее представление JVM, недоступное в Java коде напрямую
// Но можешь видеть эффекты через Reflection
public class MyClass {
    private int age;
    
    public void printInfo() {
        // В памяти Metaspace существует Klass объект:
        // Klass для MyClass содержит:
        // - Имя класса: "MyClass"
        // - Fields: age (int)
        // - Methods: printInfo() и т.д.
        // - Superclass: Object
        // - Access flags: PUBLIC
    }
}

2. Class Object (Java-уровень доступа)

Описание: объект класса java.lang.Class<T>, через который получаешь информацию о классе.

// Class объект содержит ссылку на Klass из Metaspace
Class<?> clazz = MyClass.class;

// Получение информации из Metaspace через Class
String className = clazz.getName();  // "com.example.MyClass"

Field[] fields = clazz.getDeclaredFields();  // Доступ к fields в Metaspace
for (Field field : fields) {
    System.out.println(field.getName());  // age
}

Method[] methods = clazz.getDeclaredMethods();  // Доступ к methods
for (Method method : methods) {
    System.out.println(method.getName());  // printInfo
}

Constructor<?>[] constructors = clazz.getDeclaredConstructors();

Modifier modifier = clazz.getModifiers();  // Модификаторы (public, final и т.д.)

3. MethodArea

Описание: часть Metaspace, где хранятся скомпилированные методы.

public class Calculator {
    public int add(int a, int b) {
        // Bytecode этого метода хранится в MethodArea
        // После JIT-компиляции машинный код также в Metaspace
        return a + b;
    }
}

// Команда для мониторинга:
// jps - список Java процессов
// jcmd <pid> GC.metaspace - информация о Metaspace

4. ConstantPool (Пул констант)

Описание: часть метаинформации класса, содержит все константы (строки, числа, ссылки на методы).

public class Constants {
    public static final String MESSAGE = "Hello World";
    public static final int VERSION = 100;
    
    public void printInfo() {
        System.out.println("Hello World");  // Строка в ConstantPool
        System.out.println(100);  // Число в ConstantPool
    }
}

// ConstantPool содержит:
// - Строковые константы ("Hello World")
// - Числовые константы (100)
// - Ссылки на методы (println)
// - Ссылки на классы (String, System)

5. Symbol Table

Описание: таблица для хранения имён (класса, методов, полей, переменных).

// Все имена в classloader хранятся в Symbol Table
public class UserService {
    // "UserService" - в Symbol Table
    private UserRepository userRepository;  // "userRepository" - в Symbol Table
    
    public User findUser(String name) {
        // "findUser" - в Symbol Table
        // Параметр "name" - в Symbol Table
        return null;
    }
}

6. StringTable (Intern Pool)

Описание: таблица для интернированных строк.

// String interning в Metaspace
String s1 = "Hello";  // Создаётся в StringTable (Metaspace)
String s2 = "Hello";  // Берётся из StringTable (same reference)

assert s1 == s2;  // true - они указывают на одну строку в Metaspace

String s3 = new String("Hello");  // Создаётся в heap
assert s1 == s3;  // false - разные объекты

String s4 = s3.intern();  // Добавляет в StringTable
assert s1 == s4;  // true - теперь указывают на StringTable

Проблемы Metaspace и Outofmemory

// Проблема 1: Утечка класса при неправильном ClassLoader
public class ClassLoaderLeak {
    // Каждый раз создавать новый ClassLoader опасно
    // Классы хранятся в Metaspace пока ClassLoader жив
    
    URLClassLoader loader = new URLClassLoader(urls);
    Class<?> clazz = loader.loadClass("com.example.MyClass");
    // Если loader не удалить, класс останется в Metaspace
}

// Проблема 2: Очень много классов (обычно при использовании cglib, bytecode generation)
public class BytecodeGenerationLeak {
    // Каждый вызов createClass() создаёт новый класс в Metaspace
    // Если это происходит в loop, может быть OutOfMemoryError
    
    for (int i = 0; i < 1_000_000; i++) {
        createDynamicClass();  // ОПАСНО!
    }
}

// Проблема 3: Неправильный лимит Metaspace
// -XX:MaxMetaspaceSize=64M - слишком мало
// -XX:MaxMetaspaceSize=1G - слишком много
// Оптимально: 256M-512M для most applications

Мониторинг Metaspace

# 1. JVM flags для Metaspace
java -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M MyApp

# 2. Вывод информации о памяти
jcmd <pid> GC.class_histogram
# Показывает какие классы занимают место в Metaspace

# 3. GC логи
java -XX:+PrintGCDetails -XX:+PrintGCDateStamps MyApp
# Увидишь Full GC, которые чистят Metaspace

# 4. jstat
jstat -gcmetacapacity <pid>
# Информация о capacity Metaspace

# 5. JProfiler или YourKit
# Визуальный мониторинг Metaspace во время работы

Решение OutOfMemoryError: Metaspace

// 1. Увеличить MaxMetaspaceSize
// -XX:MaxMetaspaceSize=1G

// 2. Избежать утечек ClassLoader
public class SafeClassLoaderUsage {
    try (URLClassLoader loader = new URLClassLoader(urls)) {
        Class<?> clazz = loader.loadClass("com.example.MyClass");
        // Используем класс
    } catch (ClassNotFoundException e) {
        // loader.close() вызовется автоматически
    }
}

// 3. Избежать динамического создания классов
public class AvoidDynamicClasses {
    // Плохо: создание класса в loop
    // for (int i = 0; i < 1000; i++) {
    //     createDynamicClass();  // каждый класс в Metaspace
    // }
    
    // Хорошо: использовать обобщённые классы
    public <T> void process(Class<T> clazz) {
        // Один класс, переиспользуется
    }
}

Пример: Анализ Metaspace вашего приложения

public class MetaspaceAnalyzer {
    public static void main(String[] args) {
        // Получить MemoryMXBean
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        
        // Информация о Metaspace
        MemoryUsage metaspaceUsage = memoryBean.getNonHeapMemoryUsage();
        
        long used = metaspaceUsage.getUsed();
        long committed = metaspaceUsage.getCommitted();
        long max = metaspaceUsage.getMax();
        
        System.out.println("Metaspace Used: " + (used / 1_000_000) + " MB");
        System.out.println("Metaspace Committed: " + (committed / 1_000_000) + " MB");
        System.out.println("Metaspace Max: " + (max / 1_000_000) + " MB");
        
        // Получить все загруженные классы
        ClassLoadingMXBean classLoadingBean = ManagementFactory.getClassLoadingMXBean();
        System.out.println("Total Classes Loaded: " + classLoadingBean.getTotalLoadedClassCount());
        System.out.println("Current Classes Loaded: " + classLoadingBean.getLoadedClassCount());
    }
}

Best Practices

Делай так:

  • Понимай что в Metaspace: metadata классов, константы, методы
  • Устанавливай MaxMetaspaceSize явно (256M-512M для большинства приложений)
  • Мониторь Metaspace в production
  • Избегай утечек ClassLoader - используй try-with-resources
  • Переиспользуй классы вместо их динамического создания

Не делай так:

  • Не оставляй Metaspace без лимита
  • Не игнорируй ошибки OutOfMemoryError: Metaspace
  • Не создавай новый ClassLoader для каждого класса
  • Не используй reflection и bytecode generation без необходимости

Заключение

Metaspace — это критичная часть JVM памяти. Хотя разработчики редко работают с ней напрямую, понимание её структуры помогает избежать проблем в production. Помни: Metaspace растёт когда загружаются новые классы и НЕ сокращается пока не произойдёт Full GC с очисткой неиспользуемых классов.