Что такое утечка памяти в Java

Ответ

Утечка памяти (memory leak) в Java — это ситуация, когда объекты больше не используются приложением, но не могут быть собраны сборщиком мусора (GC) из-за сохраненных ссылок на них.

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

  • Статические поля (живут пока жив класс)
  • Неотписанные слушатели (например, OnClickListener)
  • Незакрытые ресурсы (Cursor, InputStream)
  • Внутренние классы, хранящие ссылку на внешний (Handler в Activity)

Пример утечки:

public class LeakyActivity extends Activity {
    private static Context sContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sContext = this; // Утечка! Activity не сможет быть собрана GC
    }
}

Как избежать:

  • Используйте WeakReference для ссылок на контекст
  • Всегда отписывайтесь от событий в onDestroy()
  • Используйте try-with-resources для AutoCloseable объектов
  • Для Handler используйте статический внутренний класс + WeakReference

Ответ 18+ 🔞

Давай разберём эту хрень про утечки памяти в Java, а то народ потом охуевает, почему приложение тормозит и вылетает. Представь, что твоя программа — это комната, а объекты — это хлам, который ты туда накидал. Сборщик мусора (GC) — это твоя бабка, которая приходит и выкидывает всё, чем ты не пользуешься. Утечка памяти — это когда ты, ёпта, прибил этот хлам гвоздями к стене, и бабка его выкинуть не может, хотя он тебе уже нахуй не нужен. Комната забивается, и жить там невозможно.

Основные причины, из-за которых эта пиздопроебибна случается:

  • Статические поля. Это как повесить фотографию своей бывшей на самом видном месте и забыть её там. Она будет висеть, пока дом стоит (то есть пока жив класс). Объект, на который ссылается статика, никогда не умрёт.
  • Неотписанные слушатели (типа OnClickListener). Ты подписался на рассылку, а отписаться забыл. Тебе каждый день приходит спам, а ты удивляешься, почему почтовый ящик трещит по швам. Активность хочет умереть, а какой-нибудь менеджер держит на неё ссылку через этот слушатель.
  • Незакрытые ресурсы (Cursor, InputStream). Это как оставить кран открытым в ванной и уйти в запой на неделю. Вода (память) льётся, пока всё не затопит. Терпения ноль ебать на таких разработчиков.
  • Внутренние классы, хранящие ссылку на внешний. Классика — Handler в Activity. Внутренний класс знает про своего родителя и держит его за руку, даже когда того уже пора хоронить. Activity не может собраться, и у тебя волнение ебать, откуда утечка.

Вот тебе наглядный пример, как самому себе ногу прострелить:

public class LeakyActivity extends Activity {
    private static Context sContext; // Статическое поле — наш главный враг

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sContext = this; // Вот она, утечка! Activity теперь пригвождена к жизни навечно.
    }
}

Что происходит? Ты присвоил статическому полю sContext ссылку на текущую Activity (this). Активность завершилась, но статическая переменная, которая живёт вечно, продолжает на неё ссылаться. GC смотрит на это и говорит: «Ну, раз на неё ещё кто-то ссылается, значит, нужна». И не трогает. Активность не уничтожается, память не освобождается. Повтори это 10 раз — и приложение накрылось медным тазом.

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

  • Используй WeakReference для ссылок на контекст. Это как дать бабке записку с адресом хлама, а не сам гвоздь. Если объект станет не нужен, GC его спокойно выкинет, а твоя слабая ссылка просто обнулится.
  • Всегда отписывайся от событий в onDestroy(). Закрывай за собой дверь. Ушёл из активности — отпишись от всех слушателей, наблюдателей и прочей хуйни, которая может держать тебя в памяти.
  • Используй try-with-resources для AutoCloseable объектов. Это магия, которая сама закроет твой InputStream или Cursor, даже если посередине операции вылетит исключение. Красота, а не жизнь.
  • Для Handler используй статический внутренний класс + WeakReference. Сделай хендлер статическим, чтобы он не знал про внешний класс, а если нужно достучаться до активности — передавай её через WeakReference. Тогда, когда активность умрёт, хендлер не будет мешать GC её прибрать.

Короче, чувак, суть в том, чтобы не создавать цепочек, которые мешают мусорщику работать. Дай ему возможность выкинуть то, что тебе уже не нужно, и твоё приложение не будет жрать память, как не в себя. Иначе пользователи тебе такого наговорят, что мало не покажется.