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

Для чего нужен Heap?

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

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

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

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

Heap (куча) в Android

Определение

Heap — это область памяти в JVM, где выделяются объекты во время выполнения программы. Это один из самых критических ресурсов Android приложения, так как память телефона ограничена.

Назначение Heap

  • Хранение объектов — все Object'ы (классы) выделяются на Heap
  • Управление памятью — Garbage Collector автоматически удаляет неиспользуемые объекты
  • Динамическое выделение — размер объекта известен только во время выполнения

Отличие от Stack

fun example() {
    val primitiveInt = 42  // Stack: примитивный тип, быстро
    val list = mutableListOf(1, 2, 3)  // List объект на Heap
    // Stack содержит ссылку на объект в Heap
}

Heap в Android

Каждое приложение получает 限制 размер Heap:

  • Типичный размер: 128-512 MB (зависит от устройства)
  • Можно проверить:
val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory() / 1024 / 1024  // в МБ
val usedMemory = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
Log.d("Memory", "Max: $maxMemory MB, Used: $usedMemory MB")

Проблемы с Heap

1. OutOfMemoryError (OOM)

Самая частая ошибка в Android — когда Heap переполняется:

// ПЛОХО: создание большого списка
val images = mutableListOf<Bitmap>()
for (i in 0..10000) {
    images.add(loadImageAsBitmap("image_$i.jpg"))  // OOM!
}

// ХОРОШО: обработка по одной
for (i in 0..10000) {
    val bitmap = loadImageAsBitmap("image_$i.jpg")
    processImage(bitmap)
    bitmap.recycle()  // освобождаем память
}

2. Memory Leak

Основной источник утечек — неправильные ссылки на Activity/Fragment:

// УТЕЧКА: анонимный класс держит ссылку на Activity
class ImageLoader(private val activity: Activity) {
    fun loadImage(url: String) {
        thread {
            val bitmap = downloadImage(url)
            activity.runOnUiThread {  // если activity была destroyed?
                activity.imageView.setImageBitmap(bitmap)  // УТЕЧКА!
            }
        }
    }
}

// ПРАВИЛЬНО: используем WeakReference или Lifecycle
class ImageLoader(private val lifecycleOwner: LifecycleOwner) {
    fun loadImage(url: String) {
        lifecycleOwner.lifecycleScope.launch {
            val bitmap = withContext(Dispatchers.IO) {
                downloadImage(url)
            }
            imageView.setImageBitmap(bitmap)  // безопасно
        }
    }
}

GC (Garbage Collector)

Автоматически удаляет неиспользуемые объекты:

fun example() {
    val tempObject = HeavyObject()  // создание на Heap
    doSomething(tempObject)
}  // tempObject выходит из scope
// GC удалит tempObject, освободив память

Оптимизация использования Heap

1. Object Pooling (переиспользование)

class ObjectPool<T> {
    private val pool = mutableListOf<T>()
    
    fun acquire(factory: () -> T): T {
        return if (pool.isNotEmpty()) pool.removeAt(0) else factory()
    }
    
    fun release(obj: T) {
        pool.add(obj)
    }
}

// Использование
val stringBuilder = "StringBuilder".let { pool.acquire { StringBuilder() } }
try {
    stringBuilder.append("Hello")
} finally {
    pool.release(stringBuilder)
}

2. Lazy Loading

// ПЛОХО: загружаем всё сразу
val allUsers = repository.getAllUsers()  // может быть гигабайты данных

// ХОРОШО: загружаем по частям
val usersPaginated = repository.getUsers(page = 1, pageSize = 20)

3. Bitmap Optimization

fun loadImageOptimized(context: Context, resourceId: Int, targetWidth: Int): Bitmap {
    val options = BitmapFactory.Options().apply {
        inSampleSize = 2  // загружаем в 2 раза меньше
    }
    return BitmapFactory.decodeResource(context.resources, resourceId, options)
}

// или используем Glide/Picasso
Glide.with(context)
    .load(imageUrl)
    .override(targetWidth, targetHeight)
    .into(imageView)

4. Listener Management

// УТЕЧКА: забыли unsubscribe
class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        sharedViewModel.data.observe(this) { data ->  // подписались
            updateUI(data)
        }
        // при destroy фрагмента Observer удалится автоматически
    }
}

Инструменты профилирования

1. Android Studio Profiler

View → Tool Windows → Profiler
↓
Memory tab → Live Allocation
↓
Найти утечки и OOM

2. LeakCanary

dependencies {
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
// LeakCanary автоматически обнаружит утечки

3. MAT (Memory Analyzer Tool)

Анализ Heap dumps для поиска утечек

Практические советы

  • Всегда очищайте ресурсы — Bitmap.recycle(), closeables
  • Используйте Weak/Soft References для кэшей
  • Профилируйте память — не гадайте
  • Object pooling для частых аллокаций
  • Lazy initialization — создавайте объекты только при необходимости
  • Избегайте анонимных классов в тяжёлых операциях

Вывод

Heap — это ограниченный ресурс, требующий постоянного контроля. Правильное управление памятью — залог стабильного и быстрого приложения без крашей и утечек.