В Java: приводит ли взаимная блокировка (deadlock) потоков к утечке памяти?

Ответ

Нет, deadlock сам по себе не вызывает утечку памяти (memory leak), но приводит к зависанию программы (liveness failure).

Почему нет утечки? Утечка памяти — это ситуация, когда объекты больше не нужны, но остаются достижимыми для GC, занимая память. В deadlock'е потоки заблокированы навсегда, но ссылки на объекты (в том числе на объекты-мониторы) остаются активными и легитимными — они находятся в стеках заблокированных потоков. Эти объекты по-прежнему достижимы, поэтому GC их не соберет, но это не считается утечкой, так как они являются частью рабочего состояния приложения (хоть и некорректного).

Пример классического deadlock:

final Object lock1 = new Object();
final Object lock2 = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lock1) {      // t1 захватывает lock1
        Thread.sleep(100);
        synchronized (lock2) { } // t1 ждет lock2, который у t2
    }
});

Thread t2 = new Thread(() -> {
    synchronized (lock2) {      // t2 захватывает lock2
        Thread.sleep(100);
        synchronized (lock1) { } // t2 ждет lock1, который у t1
    }
});

t1.start();
t2.start();
// Оба потока заблокированы навсегда.

Основные проблемы deadlock:

  1. Отказ в обслуживании: Потоки не могут прогрессировать.
  2. Блокировка ресурсов: Захваченные ресурсы (соединения, файлы) не освобождаются.

Способы предотвращения:

  • Упорядочивание блокировок: Всегда захватывайте несколько блокировок в одном и том же глобальном порядке.
  • Использование tryLock с таймаутом из пакета java.util.concurrent.locks.
  • Статический анализ кода для выявления потенциальных deadlock'ов.

Ответ 18+ 🔞

А, слушай, вот эта тема про deadlock и утечки памяти — это вообще отдельная песня, блядь. Люди часто путают, думают, что если всё зависло, то память тут же потечёт, как из дырявого ведра. Ан нет, ёпта!

Так вот, deadlock — это не про утечку, это про то, что твоя программа превращается в красивый, но абсолютно мёртвый памятник самой себе. Утечка памяти — это когда объекты уже нахуй не нужны, но они, как назойливые мухи, болтаются в памяти, и сборщик мусора на них смотрит и рыдает, потому что они формально ещё «достижимы». А в deadlock'е всё иначе, блядь.

Почему утечки нет, объясняю на пальцах: Когда потоки повисли в мертвой хватке, они нихуя не делают, но они живые, сука! Они сидят в своих стеках и крепко держат в лапках ссылки на объекты-замки (lock1, lock2). Для сборщика мусора эти объекты — святые коровы, они нужны потокам (по крайней мере, потоки так думают). Поэтому GC пройдёт мимо, махнёт рукой: «Живите, блядь, сколько хотите». Память не освобождается, но и не течёт — она просто заморожена, как суп в холодильнике. Это не утечка, это отказ в обслуживании, ёперный театр!

Вот, смотри, классика жанра, как сами себе ногу прострелить:

final Object lock1 = new Object();
final Object lock2 = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lock1) {      // t1 схватил lock1
        Thread.sleep(100);
        synchronized (lock2) { } // t1 ждёт lock2, а он у t2, пиздец
    }
});

Thread t2 = new Thread(() -> {
    synchronized (lock2) {      // t2 схватил lock2
        Thread.sleep(100);
        synchronized (lock1) { } // t2 ждёт lock1, а он у t1, вот и повесились оба
    }
});

t1.start();
t2.start();
// Всё, приехали. Сидят два потока и смотрят друг на друга, как дураки.

Чем это всё пахнет, кроме жареного?

  1. Полный швах: Ни один поток никуда не двинется, программа — труп.
  2. Ресурсы в заложниках: Всё, что они захватили (соединения к базе, файлы), повисло намертво. Другие потоки тоже могут встать в очередь и подохнуть с голоду.

Как не наступить на эти грабли, а?

  • Порядок, блядь, порядок! Всегда хватай несколько замков в одном и том же порядке. Если все сначала берут lock1, а потом lock2, то deadlock'у просто неоткуда взяться, он обосрётся на подходе.
  • Не жди вечно, идиот! Используй tryLock с таймаутом из java.util.concurrent.locks. Не получилось взять замок за отведённое время — отступи, подумай, выпей чаю, попробуй снова. Не упорствуй, как Герасим с Муму.
  • Дай почитать код умным инструментам. Пусть статические анализаторы поковыряются в твоём творении, они такие паттерны чуют за версту.

Короче, deadlock — это про логику, про живность программы, а не про мусор в памяти. Запомни это, а то будешь искать утечку там, где её нет, и охуеешь от безысходности.