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

В каких случаях может возникнуть утечка памяти

1.7 Middle🔥 211 комментариев
#JVM и память#Производительность и оптимизация

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

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

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

Утечки памяти в Android: основные причины и механизмы

Утечки памяти в Android возникают, когда объекты удерживаются в памяти дольше необходимого, препятствуя их сборке Garbage Collector'ом (GC). Это приводит к повышенному потреблению памяти, частым срабатываниям GC, "тормозам" в интерфейсе и, в худшем случае, к фатальной ошибке OutOfMemoryError.

Ключевые категории причин

1. Долгоживущие ссылки на контекст (Context)

Наиболее частая причина — удержание ссылки на Activity или Context после его уничтожения.

class LeakySingleton {
    companion object {
        private var activityContext: Context? = null
        
        fun setContext(context: Context) {
            activityContext = context // УТЕЧКА! Singleton переживает Activity
        }
    }
}

Правильно: хранить ApplicationContext или использовать слабые ссылки.

2. Неотменённые слушатели (Listeners) и колбэки

Регистрация слушателей без отмены при уничтожении компонента.

class MyActivity : AppCompatActivity() {
    private val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
    private val sensorListener = MySensorListener()
    
    override fun onResume() {
        super.onResume()
        sensorManager.registerListener(sensorListener, ...)
    }
    
    // УТЕЧКА! Не отменена регистрация в onPause()
}

3. Внутренние классы и анонимные классы

Нестатические внутренние классы неявно содержат ссылку на внешний класс.

public class OuterActivity extends Activity {
    private InnerHandler mHandler = new InnerHandler(); // УТЕЧКА!
    
    // Нестатический внутренний класс
    private class InnerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            // Имеет неявную ссылку на OuterActivity
        }
    }
}

Исправление: объявить класс как static и передавать слабую ссылку.

4. Колбэки в сторонних библиотеках

Библиотеки, требующие передачи this (Activity), часто сохраняют эту ссылку.

// Некоторые рекламные SDK или analytics-библиотеки
AdLibrary.getInstance().init(this) // Может удерживать Activity

5. Долгоживущие background-потоки и корутины

Фоновые задачи, работающие дольше жизненного цикла UI-компонента.

GlobalScope.launch { // Глобальная область видимости!
    delay(10000)
    updateUI() // Попытка обновить уничтоженную Activity
}

Решение: использовать lifecycleScope или viewModelScope.

6. Работа с View и Drawable

class LeakyAdapter : RecyclerView.Adapter<...>() {
    private val drawables = mutableListOf<Drawable>()
    
    fun addDrawable(drawable: Drawable) {
        drawables.add(drawable) // Drawable может содержать ссылку на View/Context
    }
}

7. Архитектурные компоненты без очистки

Использование ViewModel или других компонентов, сохраняющих ссылки на данные.

class MyViewModel : ViewModel() {
    private val largeDataList = mutableListOf<ByteArray>() // Большие данные
    
    fun clearData() {
        largeDataList.clear() // Если не вызывать, данные живут с ViewModel
    }
}

Техники обнаружения и предотвращения

Инструменты мониторинга:

  • Android Profiler в Android Studio (Memory tab)
  • LeakCanary — автоматическое обнаружение утечек
  • MAT (Memory Analyzer Tool) для глубокого анализа дампов памяти

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

  1. Используйте Application Context везде, где достаточно
  2. Отменяйте регистрацию слушателей в соответствующих методах жизненного цикла
  3. Используйте WeakReference для ссылок, которые могут пережить компонент
  4. Очищайте коллекции и кэши при необходимости
  5. Используйте правильные скоупы для корутин
  6. Избегайте статических полей, содержащих контекст
// Безопасная работа с контекстом
class SafeManager(private val appContext: Context) {
    companion object {
        private var instance: SafeManager? = null
        
        fun init(context: Context) {
            if (instance == null) {
                instance = SafeManager(context.applicationContext)
            }
        }
    }
}

Понимание этих паттернов помогает создавать стабильные приложения с предсказуемым потреблением памяти. Регулярный профилинг и использование автоматизированных инструментов обнаружения утечек должны быть частью процесса разработки каждого Android-приложения.

В каких случаях может возникнуть утечка памяти | PrepBro