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

Какие знаешь причины утечки памяти?

1.8 Middle🔥 241 комментариев
#JVM и управление памятью#Многопоточность

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

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

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

Причины утечки памяти в Java

Утечка памяти (Memory Leak) — это накопление объектов в памяти, которые больше не используются приложением, но на них остаются ссылки, поэтому Garbage Collector не может их удалить. Вот основные причины:

1. Статические коллекции

Статические поля, содержащие коллекции, никогда не очищаются:

public class CacheManager {
    private static List<String> cache = new ArrayList<>();
    
    public void addToCache(String data) {
        cache.add(data);  // Каждый вызов добавляет данные
        // cache.clear() никогда не вызывается — утечка!
    }
}

Решение: используй LinkedHashMap с лимитом размера или явно очищай кэш

public class CacheManager {
    private static final int MAX_SIZE = 1000;
    private static Map<String, String> cache = new LinkedHashMap<String, String>() {
        protected boolean removeEldestEntry(Map.Entry eldest) {
            return size() > MAX_SIZE;
        }
    };
}

2. Слушатели и callback-и

Когда регистрируешь слушателей, но забываешь их отписывать:

public class EventService {
    public void subscribe(EventListener listener) {
        listeners.add(listener);  // Слушатель остаётся в памяти
    }
    // Нет метода unsubscribe — утечка!
}

Решение: всегда удаляй слушателей

public void unsubscribe(EventListener listener) {
    listeners.remove(listener);
}

3. Незакрытые ресурсы

Потоки, соединения с БД, файлы — всё это должно закрываться:

public void readFile(String path) {
    FileInputStream fis = new FileInputStream(path);
    BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
    // Если исключение — reader и fis остаются открытыми
    String line = reader.readLine();
}

Решение: используй try-with-resources

public void readFile(String path) {
    try (FileInputStream fis = new FileInputStream(path);
         BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
        String line = reader.readLine();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4. Циклические ссылки

Когда объекты ссылаются друг на друга:

public class User {
    public Post post;
}

public class Post {
    public User author;
}

User user = new User();
Post post = new Post();
user.post = post;
post.author = user;  // Циклическая ссылка
user = null;  // user недостижим, но остаётся в памяти!

Решение: используй WeakReference для обратных ссылок

public class Post {
    private WeakReference<User> author;
    
    public Post(User author) {
        this.author = new WeakReference<>(author);
    }
    
    public User getAuthor() {
        return author.get();
    }
}

5. Статические коллекции с объектами

public class Registry {
    private static Set<Object> registry = new HashSet<>();
    
    public void register(Object obj) {
        registry.add(obj);  // Объект в памяти навсегда
    }
}

6. Переопределённый finalize()

Внутренняя утечка в garbage collector:

public class Resource {
    public void finalize() throws Throwable {
        // Если здесь ошибка — объект остаётся в памяти
    }
}

7. ThreadLocal

Если не очищаешь ThreadLocal — объекты остаются в потоке:

public class ThreadLocalExample {
    private static ThreadLocal<List<String>> cache = ThreadLocal.withInitial(ArrayList::new);
    
    public void processRequest() {
        cache.get().add("data");  // После запроса cache.remove() не вызовут
    }
}

Решение:

public void processRequest() {
    try {
        cache.get().add("data");
    } finally {
        cache.remove();  // ВСЕГДА удаляй ThreadLocal!
    }
}

8. Бесконечные потоки

Демон-потоки без условия завершения:

new Thread(() -> {
    while (true) {  // Бесконечный цикл
        // Поток никогда не заканчивается
    }
}).start();

Инструменты для обнаружения утечек

  • JProfiler — визуализация heap, поиск утечек
  • YourKit Java Profiler — анализ памяти
  • Eclipse Memory Analyzer (MAT) — анализ heap dump
  • jmap и jhat — из JDK
jmap -dump:live,format=b,file=heap.bin <pid>
jhat heap.bin

Лучшие практики

  • Закрывай ресурсы — используй try-with-resources
  • Удаляй слушателей — всегда отписывай
  • Избегай статических коллекций — или задавай лимиты
  • Используй WeakReference для кэшей
  • Профилируй регулярно — не жди катастрофы
  • Логируй память — отслеживай рост heap

Итог

Утечки памяти в Java часто вызваны забывчивостью программиста, а не ошибками самого языка. Ключ — внимательное управление ресурсами, особенно при работе с коллекциями, слушателями и потоками.