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

Что такое Shenandoah?

2.7 Senior🔥 71 комментариев
#JVM и управление памятью

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

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

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

Что такое Shenandoah?

Определение

Shenandoah — это низколатентный сборщик мусора (garbage collector), разработанный Red Hat для JVM. Он предназначен для приложений, требующих предсказуемых и минимальных пауз (pause times), особенно для больших куч памяти (от 10GB и выше).

Проблема, которую решает Shenandoah

Традиционные сборщики мусора (G1GC, Parallel GC) в определенный момент останавливают все потоки приложения (Stop The World пауза) для выполнения сборки мусора:

График типичного GC:
┌─────────────────────────────────────────────┐
│ Application работает                        │
└─────────────────────────────────────────────┘
              ↓
     ┌──────────────┐  ← STW пауза 500ms
     │ Full GC      │
     └──────────────┘
              ↓
┌─────────────────────────────────────────────┐
│ Application работает снова                  │
└─────────────────────────────────────────────┘

Для приложений с требованиями к задержкам (trading системы, игры, реал-тайм системы) 500ms пауза недопустима.

Как работает Shenandoah

Главное отличие: Shenandoah работает параллельно с потоками приложения, не требуя полной остановки:

График Shenandoah GC:
┌────────────────────────────────┐
│ Application               ▁▂▃  │  ← Мини-паузы (1-10ms)
│ Shenandoah GC ▓▓▓▓▓▓▓▓▓▓▓     │  ← Работает одновременно
└────────────────────────────────┘

Ключевые механизмы

1. Concurrent Mark

Отмечание живых объектов происходит параллельно с работой приложения:

// Приложение продолжает работать
Object obj = new Object();
obj.doSomething();  // ← Работает

// Одновременно Shenandoah отмечает живые объекты
// Mark phase работает в background потоках

2. Concurrent Compaction

Уплотнение памяти происходит параллельно, используя forwarding pointers:

До Concurrent Compaction:
┌────────────────────────────────────────┐
│ [Object A] [free] [Object B] [free]    │
└────────────────────────────────────────┘

Вовремя Concurrent Compaction:
┌────────────────────────────────────────┐
│ [Object A] [Object B] [free] [free]    │
│     ↑   ← forwarding pointer           │
└────────────────────────────────────────┘

После Concurrent Compaction:
┌────────────────────────────────────────┐
│ [Object A] [Object B] [free] [free]    │
└────────────────────────────────────────┘

3. Очень короткие STW паузы

Вместо 200-500ms паузы Full GC, Shenandoah имеет только 10-50ms паузы для:

  • Подтверждения завершения mark phase
  • Обновления корневых ссылок
  • Финализации процесса

Сравнение с G1GC

ПараметрG1GCShenandoah
STW пауза200-500ms10-50ms
Размер кучидо 8GB оптимально10GB+
Overhead~10%~20%
ThroughputЛучше (95%+)Хуже (~90%)
LatencyНеплохоОтличная
Версия JavaВездеJava 12+

Когда использовать Shenandoah

✅ Идеально для:

  • Trading системы — требуют задержки < 100ms
  • Игровые серверы — требуют smooth фреймрейта
  • Финансовые системы — чувствительны к паузам
  • Real-time системы — требуют гарантированной задержки
  • Big Data обработка с большой памятью — 16GB+ кучи

❌ Не подходит для:

  • Микросервисы с малой памятью (< 4GB)
  • Batch-системы (throughput важнее latency)
  • Системы, где 500ms пауза нормальна

Настройка Shenandoah

# Включить Shenandoah
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseShenandoahGC \
     -Xmx16g \
     MyApplication

# Дополнительные параметры
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseShenandoahGC \
     -XX:ShenandoahGCHeuristics=adaptive \
     -XX:ShenandoahTargetNumWorkerThreads=8 \
     -Xmx16g \
     MyApplication

Стратегии Shenandoah

1. adaptive (по умолчанию)

// Автоматически выбирает оптимальный момент для GC
// Динамически адаптируется к нагрузке
java -XX:ShenandoahGCHeuristics=adaptive ...

2. static

// Более предсказуемые циклы GC
java -XX:ShenandoahGCHeuristics=static ...

3. compact

// Для систем с очень большой памятью
java -XX:ShenandoahGCHeuristics=compact ...

Пример: влияние на production

import java.lang.management.*;

public class GCMetrics {
    
    public static void main(String[] args) throws InterruptedException {
        // С G1GC:
        // Pause Time: max 500ms, avg 250ms
        // Throughput: 99.5%
        
        // С Shenandoah:
        // Pause Time: max 50ms, avg 15ms
        // Throughput: 99.0%
        
        for (int i = 0; i < 1_000_000; i++) {
            // Работа с большим объемом памяти
            byte[] data = new byte[1024];  // Создание объектов
            processData(data);
            
            if (i % 100_000 == 0) {
                printGCStats();
            }
        }
    }
    
    static void printGCStats() {
        for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
            System.out.println(gc.getName());
            System.out.println("  Collections: " + gc.getCollectionCount());
            System.out.println("  Time (ms): " + gc.getCollectionTime());
        }
    }
    
    static void processData(byte[] data) {
        // Обработка данных
    }
}

// Результаты:
// Shenandoah: очень равномерные паузы, даже при large GC
// G1GC: иногда скачки до 500ms

Переход на Shenandoah

// Шаг 1: Тестирование
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseShenandoahGC \
     -XX:+PrintGCDetails \
     -XX:+PrintPauseTime \
     MyApp

// Шаг 2: Мониторинг паузы
// Убедитесь, что max pause < 100ms

// Шаг 3: Проверка throughput
// Убедитесь, что потеря перформанса приемлема

// Шаг 4: Production
java -XX:+UnlockExperimentalVMOptions \
     -XX:+UseShenandoahGC \
     MyApp

Альтернативы

  • ZGC — еще более низколатентный (100ms+ кучи, < 10ms паузы)
  • G1GC — универсальный выбор (лучше для большинства случаев)
  • Parallel GC — для batch-систем

Важные замечания

  1. Shenandoah экспериментальный в некоторых версиях Java (флаг UnlockExperimentalVMOptions)
  2. Требует больше CPU для background работы
  3. Может потребовать тюнинга под конкретное приложение
  4. Throughput немного ниже чем у G1GC

Итоговый вывод

Shenandoah — это revolution в управлении памятью для систем с жесткими требованиями к задержкам. Благодаря параллельной работе с потоками приложения, он обеспечивает очень короткие STW паузы (10-50ms вместо 200-500ms), что делает его идеальным выбором для low-latency систем. Однако он требует достаточно памяти (10GB+) и может иметь небольшой overhead, поэтому выбор GC всегда должен основываться на требованиях конкретного приложения.