Какие техники и практики в Java помогают экономить память?

«Какие техники и практики в Java помогают экономить память?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

1. Использование примитивных типов вместо обёрток (Boxed Types)

  • int, long, double вместо Integer, Long, Double.
  • Почему: Избегаем накладных расходов на объект в куче и автоупаковку/распаковку.
  • Пример: Для больших коллекций используйте Trove, Eclipse Collections или массивы примитивов.

2. Осознанное использование строк (String Pool)

  • Литералы (String s = "text") используют пул строк.
  • Метод intern() помещает строку в пул, но может привести к утечке памяти, если используется бездумно.
  • Лучшая практика: Используйте StringBuilder для конкатенации в циклах.

3. Выбор правильных структур данных

  • ArrayList обычно эффективнее LinkedList по памяти (не хранит отдельные узлы с двумя ссылками).
  • Для enum-значений используйте EnumSet и EnumMap.
  • Устанавливайте начальную вместимость (initialCapacity) коллекций, чтобы избежать лишних ресайзов.

4. Паттерн "Приспособленец" (Flyweight)

  • Разделение общего, неизменяемого состояния между множеством объектов.
  • Пример: Кэширование экземпляров часто используемых неизменяемых классов (например, BigDecimal).

5. Своевременная очистка ссылок

  • Обнуляйте ссылки на большие объекты, когда они больше не нужны (largeList = null).
  • Используйте WeakReference для кэшей, которые можно очистить при нехватке памяти.
  • Избегайте утечек памяти в слушателях событий (event listeners) — не забывайте отписываться.

6. Ленивая инициализация (Lazy Initialization)

  • Создавайте ресурсоёмкие объекты только при первом обращении.

7. Потоковая обработка данных (Stream API)

  • Обрабатывайте данные потоково, не загружая весь набор в память.
    // Экономит память при работе с большими файлами
    Files.lines(path)
     .filter(line -> line.contains("error"))
     .forEach(System.out::println);