Android OOMКритическая

OutOfMemoryError в Android: причины и способы исправления

OutOfMemoryError возникает, когда приложение исчерпает лимит памяти. В статье разберем основные причины и дадим проверенные способы исправления, включая использование Memory Profiler и оптимизацию загрузки изображений.

Обновлено 15 февраля 2026 г.
15-30 мин
Средняя
FixPedia Team
Применимо к:Android 5.0 (API 21) и вышеПриложения на Java/Kotlin

Что означает ошибка OutOfMemoryError

OutOfMemoryError (часто сокращенно OOM) — это критическая ошибка времени выполнения в Java и Android, которая возникает, когда виртуальная машина (ART на Android) не может выделить объект в куче (heap) из-за нехватки памяти. Ошибка обычно выглядит так:

java.lang.OutOfMemoryError: Failed to allocate a 123456-byte allocation with 12345 free bytes and 12MB until OOM

Она приводит к немедленному падению приложения. В Android эта ошибка особенно распространена на устройствах с ограниченной памятью (например, бюджетные модели) или при работе с большими ресурсами (изображения, JSON-ответы, кэши).

Причины возникновения

OutOfMemoryError не случается случайно. Вот наиболее частые причины:

  1. Утечки памяти (memory leaks) — объекты, которые больше не нужны, но продолжают удерживаться в памяти (например, через статические ссылки, незакрытые слушатели, контекст активности в фоновых задачах).
  2. Загрузка слишком больших изображений — без должного масштабирования или сжатия, особенно при использовании BitmapFactory.decode* без указания inSampleSize.
  3. Слишком большой размер кучи (heap) — приложение пытается создать объект, который физически не помещается в доступную память (например, огромный массив или список).
  4. Неэффективные структуры данных — использование HashMap или ArrayList без ограничения роста, хранение дублирующих данных.
  5. Неосвобождаемые ресурсы — незакрытые Cursor, InputStream, OutputStream, Socket, которые держат память и файловые дескрипторы.
  6. Избыточное кэширование — хранение в кэше (например, LruCache) слишком много объектов или объектов большого размера.
  7. Работа с большими JSON/XML — парсинг огромных строк в объекты без потокового подхода.

Способы решения

Способ 1: Анализ памяти и обнаружение утечек

Первым делом нужно понять, что именно потребляет память. Android Studio предоставляет мощный инструмент — Memory Profiler.

  1. Откройте проект в Android Studio.
  2. Запустите приложение на устройстве или эмуляторе.
  3. В меню выберите View → Tool Windows → Profiler (или нажмите Alt+Shift+F10).
  4. В окне Profiler выберите ваше устройство и приложение, затем перейдите на вкладку Memory.
  5. Нажмите кнопку Record (кружок) и выполните действия, которые обычно приводят к падению (например, открыть экран с изображениями, прокрутить список).
  6. После появления роста памяти нажмите Pause, затем Dump Java heap (значок камеры). Откроется окно с анализом кучи.
  7. В левой панели найдите подозрительные классы (например, ваши Activity, Fragment, Bitmap) и проверьте количество экземпляров. Если объекты не освобождаются после выхода из экрана — это утечка.
  8. Для детального анализа используйте Analyzer TasksFind Leaks. Профилировщик покажет цепочки ссылок, удерживающие объекты.

💡 Совет: Обратите внимание на Activity и Context — самые частые виновники утечек. Если в статическом поле или в долгоживущем объекте (например, синглтоне) хранится ссылка на Activity, она не будет собрана сборщиком мусора.

Способ 2: Оптимизация работы с изображениями

Изображения — самая частая причина OOM. Даже небольшое Bitmap в памяти занимает в 4-8 раз больше места, чем на диске (из-за ARGB_8888).

Используйте библиотеки для загрузки изображений (Glide, Picasso, Coil). Они автоматически:

  • Масштабируют изображение под размер ImageView.
  • Кэшируют в памяти и на диске.
  • Управляют жизненным циклом (отменяют загрузку при уничтожении Activity).

Пример с Glide:

// build.gradle (Module)
dependencies {
    implementation 'com.github.bumptech.glide:glide:4.15.1'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
}
// Загрузка с автоматическим масштабированием
Glide.with(context)
    .load(imageUrl)
    .override(targetWidth, targetHeight) // опционально: явное указание размера
    .into(imageView)

Если библиотеки не используются:

  • Всегда указывайте inSampleSize при декодировании:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
// Вычисляем inSampleSize на основе требуемых размеров
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
  • Используйте Bitmap.Config.RGB_565 (2 байта на пиксель вместо 4), если альфа-канал не нужен.
  • Явно вызывайте bitmap.recycle() (для API < 28) или присваивайте null и вызывайте System.gc() (не гарантирует, но иногда помогает).

Способ 3: Правильное управление ресурсами и жизненным циклом

Многие утечки возникают из-за того, что ресурсы не закрываются, или фоновые задачи держат ссылку на Activity.

  1. Закрывайте все ресурсы в finally-блоках или используйте try-with-resources (Java 7+):
try (Cursor cursor = db.rawQuery(...);
     InputStream is = ...) {
    // работа
} // автоматически закроется
  1. Отменяйте фоновые задачи в onDestroy() или onStop():
override fun onDestroy() {
    super.onDestroy()
    job.cancel() // для корутин
    call.cancel() // для Retrofit
    handler.removeCallbacksAndMessages(null)
}
  1. Избегайте неявных ссылок на Context:
  • Не храните Context в статических полях.
  • Используйте applicationContext там, где не нужен UI-контекст.
  • В фрагментах и активностях используйте viewLifecycleOwner для жизненного цикла.
  1. Используйте слабые ссылки (WeakReference) для кэшей, которые могут содержать контекст.

Способ 4: Увеличение размера кучи (временное решение)

Если оптимизации не помогают, а приложение критически важно, можно временно увеличить лимит памяти.

В AndroidManifest.xml в теге <application> добавьте:

<application
    android:largeHeap="true"
    ... >

⚠️ Важно: Это не панацея. largeHeap увеличивает лимит только на некоторых устройствах (обычно в 2-3 раза), но не снимает проблему утечек. Используйте только как временную меру, пока не исправите коренную причину. На новых версиях Android (особенно с ограничениями в фоновом режиме) этот атрибут может игнорироваться.

Профилактика

Чтобы избежать OutOfMemoryError в будущем:

  1. Регулярно профилируйте память — добавьте Memory Profiler в процесс разработки, особенно перед releasing.
  2. Используйте статический анализ — инструменты вроде Android Lint и SonarQube могут выявить потенциальные утечки.
  3. Внедрите LeakCanary — библиотека автоматически обнаруживает утечки в debug-сборках:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
  1. Ограничивайте размеры коллекций — используйте LruCache для кэширования, задавайте лимиты спискам.
  2. Обрабатывайте большие данные потоково — не загружайте весь JSON/файл в память, используйте JsonReader или XmlPullParser.
  3. Тестируйте на устройствах с малым объемом RAM — emulateur с 512MB или реальные бюджетные телефоны.

Способ N: Использование статических анализаторов и CI

Для сложных проектов автоматизируйте поиск утечек:

  1. Интегрируйте LeakCanary в CI (например, с Firebase Test Lab) — запускайте тесты на устройствах и проверяйте отчеты.
  2. Используйте Perfetto — системный трейсер Android, который может выявить проблемы с памятью на уровне системы.
  3. Проводите регулярный аудит кода на предмет:
    • Статических ссылок на Context/View.
    • Незакрытых ресурсов в finally.
    • Регистрации слушателей без соответствующих unregister.

Профилактика (дополнительно)

Помимо вышеперечисленного, следите за:

  • Размером APK — большие ресурсы (未 сжатые изображения, аудио) могут привести к OOM при первом запуске.
  • Количеством процессов — на устройствах с Android 8+ ограничено количество фоновых процессов. Избыточные сервисы могут привести к нехватке памяти.
  • Адаптацией под разные плотности экрана — загружайте только нужные ресурсы (drawable-hdpi, xhdpi и т.д.) через Resources.getIdentifier() или CDN.

Заключительный совет: Начните с профилирования — 90% проблем OOM находятся в первые 10 минут анализа Memory Profiler. Не игнорируйте предупреждения Lint о утечках, они часто точечны.

Часто задаваемые вопросы

Что вызывает OutOfMemoryError в Android?
Как предотвратить OutOfMemoryError?
Почему приложение падает с OutOfMemoryError, хотя свободной памяти на устройстве много?
Как измерить использование памяти в приложении?

Полезное

Анализ памяти с помощью Memory Profiler
Оптимизация загрузки изображений
Обнаружение утечек памяти
Освобождение ресурсов
Увеличение размера кучи (временное решение)