Что такое OutOfMemoryError в Java?

Ответ

OutOfMemoryError — это неконтролируемое исключение (java.lang.Error), возникающее, когда виртуальная машина Java (JVM) не может выделить память в куче (heap) для создания нового объекта, и сборщик мусора (Garbage Collector) не может освободить достаточное её количество.

Основные причины:

  • Утечки памяти: Объекты остаются достижимыми (например, через статические коллекции), хотя логически уже не нужны.
  • Недостаточный размер heap: Параметры -Xmx (максимальный размер) заданы слишком низко для рабочей нагрузки.
  • Большие/неограниченные структуры данных: Постоянный рост кэшей или коллекций без очистки.

Пример, вызывающий ошибку:

import java.util.ArrayList;
import java.util.List;

public class OOMExample {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        while (true) {
            // Бесконечно добавляем массив в 1 МБ
            list.add(new byte[1024 * 1024]);
        }
    }
}

Как диагностировать и предотвратить:

  1. Настройте размер heap: Укажите адекватные -Xms (начальный размер) и -Xmx через аргументы JVM.
  2. Анализируйте дамп памяти: Используйте -XX:+HeapDumpOnOutOfMemoryError, а затем анализируйте файл .hprof в Eclipse MAT или VisualVM.
  3. Используйте слабые ссылки: Для кэшей применяйте WeakHashMap или SoftReference, чтобы GC мог их очистить.
  4. Профилируйте приложение: Следите за потреблением памяти с помощью Java Flight Recorder (JFR) или JMX.

Ответ 18+ 🔞

Ага, вот эта самая ошибка, которая всех так веселит — OutOfMemoryError. Это, блядь, не просто какое-то исключение, которое можно поймать и тихо проигнорировать. Это, сука, полноценный java.lang.Error, крик души самой JVM, когда она уже не может, блядь, выделить память в куче под новый объект, а сборщик мусора, этот ленивый уёбок, разводит руками и говорит: «Ну я тут нихуя не могу поделать, всё чисто».

Из-за чего обычно случается этот пиздец:

  • Утечки памяти, мать их: Объекты, которые уже всем нахуй не сдались, но всё ещё болтаются в памяти, потому что их, например, какая-нибудь статическая коллекция держит за яйца. Мусорщик на них смотрит и такой: «Ну вы же достижимые, блядь, я вас трогать не могу».
  • Жмотство с настройками: Куча (heap) изначально задана размером с напёрсток. Параметры -Xmx (максимальный размер) выставлены так, будто программа будет считать два плюс два, а она, сука, данные целого завода грузит.
  • Ненасытные структуры: Кэши, которые растут как на дрожжах, или коллекции, в которые пихают всё подряд без ограничений. Пока не накроется всё медным тазом.

Вот, смотри, как это красиво устроить, чтоб у всех глаза на лоб полезли:

import java.util.ArrayList;
import java.util.List;

public class OOMExample {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        while (true) {
            // А давайте-ка бесконечно пихать в список мегабайтные массивы!
            // Что может пойти не так?
            list.add(new byte[1024 * 1024]);
        }
    }
}

Запустишь эту хуйню — и через пару секунд получишь тот самый салют. Ёперный театр.

Что делать, чтобы не оказаться в такой же жопе:

  1. Не жадничай с памятью: Выставь нормальные -Xms и -Xmx. Если приложению нужно 4 гига — дай 4 гига, а не 256 мегабайт, экономька ебаная.
  2. Заставь JVM настучать на саму себя: При запуске добавь волшебный флаг -XX:+HeapDumpOnOutOfMemoryError. Когда всё рухнет, JVM выплюнет файлик .hprof — дамп памяти в момент агонии. Засунь его потом в Eclipse MAT или VisualVM, и он тебе покажет, кто именно сожрал всю память, этот пидарас шерстяной.
  3. Используй правильные инструменты для кэшей: Не храни всё в обычных HashMap. Для кэшей есть WeakHashMap или SoftReference — это такие ссылки, которые сборщик мусора может спокойно выкинуть, если память на исходе. Хитрая жопа, но работает.
  4. Следи за базаром: Профилируй приложение. Включи Java Flight Recorder (JFR) или смотри через JMX, что там творится с памятью в реальном времени. Чтобы не охуеть в последний момент.