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

Будет ли утекать память при блокировке двух потоков, ожидающих мониторы друг друга?

3.0 Senior🔥 61 комментариев
#Многопоточность

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

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

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

Утечка памяти при deadlock двух потоков

Это интересный вопрос о взаимоблокировке (deadlock) и управлении памятью в Java. Давайте разберемся подробно.

Ответ: Нет, память не будет утекать

Когда два потока находятся в состоянии deadlock, ожидая мониторы друг друга, они не создают утечку памяти в классическом смысле. Однако есть важные нюансы.

Как выглядит deadlock

public class DeadlockExample {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    
    public static void main(String[] args) {
        // Поток 1: заблокирует lock1, затем будет ждать lock2
        Thread thread1 = new Thread(() -> {
            synchronized(lock1) {
                System.out.println("Поток 1: захватил lock1");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized(lock2) {
                    System.out.println("Поток 1: захватил lock2");
                }
            }
        });
        
        // Поток 2: заблокирует lock2, затем будет ждать lock1
        Thread thread2 = new Thread(() -> {
            synchronized(lock2) {
                System.out.println("Поток 2: захватил lock2");
                try { Thread.sleep(100); } catch (InterruptedException e) {}
                synchronized(lock1) {
                    System.out.println("Поток 2: захватил lock1");
                }
            }
        });
        
        thread1.start();
        thread2.start();
        // Здесь будет deadlock - потоки зависнут навсегда
    }
}

Почему нет утечки памяти

1. Объекты остаются доступны из корней памяти

Даже при deadlock потоки остаются в памяти и доступны:

// Потоки все еще существуют в памяти
Thread thread1; // Referencing in JVM
Thread thread2; // Referencing in JVM
Object lock1;   // Referenced by потоков
Object lock2;   // Referenced by потоков

Монитор (встроенный в каждый объект в Java) не выделяет дополнительную память при блокировке. Это встроенная структура.

2. Память не освобождается, но это не утечка

Технически это скорее зависание ресурсов, а не утечка:

Утечка памяти    = объект недостижим из корней, но не удален
Зависание ресурс = объект доступен, но не используется

Разница между утечкой и зависанием

public class MemoryLeakVsDeadlock {
    
    // УТЕЧКА ПАМЯТИ - объект потерял все ссылки но не удалён
    static class LeakExample {
        static List<byte[]> cache = new ArrayList<>();
        
        public void addToCache(byte[] data) {
            cache.add(data); // объект добавлен и никогда не удалится
        }
    }
    
    // DEADLOCK - объект доступен, но потоки зависли
    static class DeadlockExample {
        Object monitor1 = new Object();
        Object monitor2 = new Object();
        
        void methodA() {
            synchronized(monitor1) {
                synchronized(monitor2) { // Может deadlock
                }
            }
        }
        
        void methodB() {
            synchronized(monitor2) {
                synchronized(monitor1) { // Может deadlock
                }
            }
        }
    }
}

Ресурсы, которые МОГУТ утечь при deadlock

Хотя сама память не утекает, могут утечь другие ресурсы:

public class DeadlockWithResourceLeak {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();
    
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized(lock1) {
                // Открыли файл или соединение
                FileInputStream fis = null;
                try {
                    fis = new FileInputStream("file.txt");
                    Thread.sleep(1000);
                    synchronized(lock2) { // DEADLOCK случится здесь
                        fis.read();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // Это никогда не выполнится при deadlock!
                    try { fis.close(); } catch (Exception e) {}
                }
            }
        });
        
        Thread thread2 = new Thread(() -> {
            synchronized(lock2) {
                Thread.sleep(500);
                synchronized(lock1) { // DEADLOCK
                }
            }
        });
        
        thread1.start();
        thread2.start();
        // Файл останется открытым - утечка РЕСУРСОВ, не памяти!
    }
}

Как обнаружить deadlock

# JDK 19+
jps -l                      # Найди PID приложения
jstack <PID> | grep -i dead # Поищи deadlock информацию
// Программно проверить deadlock
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] deadlocked = bean.findDeadlockedThreads();

if (deadlocked != null && deadlocked.length > 0) {
    System.out.println("DEADLOCK ОБНАРУЖЕН!");
    for (ThreadInfo info : bean.getThreadInfo(deadlocked)) {
        System.out.println("Заблокированный поток: " + info.getThreadName());
    }
}

Итог

  1. Память НЕ утечет при deadlock - объекты и потоки остаются в памяти и доступны для GC
  2. Ресурсы МОГУТ утечь - файлы, соединения, если они открыты до deadlock
  3. Процесс зависнет - будет занимать CPU и памяти, но это не утечка
  4. Используй try-with-resources для предотвращения утечки ресурсов
  5. Избегай nested synchronized - используй Lock или ReentrantLock с timeout