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

Как работает GarbageCollector?

3.0 Senior🔥 141 комментариев
#JVM и память#Производительность и оптимизация

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

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

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

Как работает GarbageCollector?

GarbageCollector (GC) — это механизм автоматического управления памятью в Java и Kotlin, который освобождает память, занятую объектами, на которые больше нет ссылок. Это критически важная система для стабильности приложений.

Базовый принцип работы

Метод Reference Counting:

GC отслеживает ссылки на объекты. Когда ссылок нет — объект удаляется:

val user = User("Alice")  // Объект создан
// Ссылка на объект: 1

val user2 = user  // Объект всё ещё существует
// Ссылок: 2

user = null  // Удалили ссылку
// Ссылок: 1

user2 = null  // Удалили последнюю ссылку
// Ссылок: 0 -> GC удалит объект

Поколения объектов (Generational GC)

JVM использует поколенческую модель — молодые объекты удаляются чаще:

Young Generation (молодое поколение):
├── Eden — где создаются новые объекты
├── Survivor Space 1 — пережившие первую GC
└── Survivor Space 2 — пережившие несколько GC

Old Generation (старое поколение):
└── Объекты, пережившие много сборок мусора

Типы сборок мусора

1. Minor GC (молодое поколение)

val objects = mutableListOf<String>()
for (i in 1..1000000) {
    objects.add("String_$i")  // Много объектов создаётся в Eden
}
// Minor GC удалит неиспользуемые строки из молодого поколения

Самая частая, самая быстрая.

2. Major GC (старое поколение)

var bigData: ByteArray? = ByteArray(10 * 1024 * 1024)  // 10 MB
// Используем данные...
bigData = null
// Major GC удалит большой объект из старого поколения

Реже, но медленнее (может заморозить приложение на 100+ мс).

3. Full GC

// Полная сборка мусора всей heap
// Самая долгая операция
// На Android это может привести к lag'am

Алгоритмы GC

Mark and Sweep (отметь и удали):

Шаг 1: Mark (отметить живые объекты)
├── GC начинает с корневых ссылок (переменные в stack)
├── Отмечает все объекты, которые доступны
└── Остальные объекты считаются мусором

Шаг 2: Sweep (удалить мусор)
├── Удаляет неотмеченные объекты
└── Освобождает память

Проблемы в Android

1. Jank (заикание) из-за GC

// Создание множества объектов в onDraw()
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    
    // ПЛОХО: GC может сработать и заморозить UI
    for (i in 0..1000) {
        val paint = Paint()  // Много объектов
        canvas.drawCircle(x, y, 10f, paint)
    }
}

// ХОРОШО: переиспользовать объекты
private val paint = Paint()

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    
    for (i in 0..1000) {
        canvas.drawCircle(x, y, 10f, paint)  // Один объект
    }
}

2. Утечка памяти

// ПЛОХО: утечка из-за сильной ссылки
class MyActivity : AppCompatActivity() {
    companion object {
        var instance: MyActivity? = null
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        instance = this  // Activity never GC'd!
    }
}

// ХОРОШО: используй WeakReference если нужно
import java.lang.ref.WeakReference

companion object {
    var instance: WeakReference<MyActivity>? = null
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    instance = WeakReference(this)  // Может быть собран
}

3. GC thrashing (частые сборки)

// ПЛОХО: частое создание объектов
fun processData(items: List<Item>) {
    items.forEach { item ->
        val temp = convertToString(item)  // Создание каждой итерации
        processString(temp)
    }
}

// ХОРОШО: переиспользовать StringBuilder
fun processData(items: List<Item>) {
    val builder = StringBuilder()
    items.forEach { item ->
        builder.clear()
        builder.append(item)
        processString(builder.toString())
    }
}

Виды ссылок в Java/Kotlin

1. Strong Reference (сильная ссылка)

val obj = MyObject()  // Сильная ссылка, объект не удалится

2. Weak Reference (слабая ссылка)

import java.lang.ref.WeakReference

var obj: MyObject? = MyObject()
val weak = WeakReference(obj)

obj = null
// GC может удалить объект
println(weak.get())  // null если объект был удалён

3. Soft Reference (мягкая ссылка)

import java.lang.ref.SoftReference

val soft = SoftReference(myBitmap)
// Удалится, если не хватает памяти

Оптимизация для Android

1. Профилирование с Profiler

Android Studio → Profiler → Memory tab показывает сборки мусора.

2. Минимизация объектов в критических местах

// В RecyclerView.Adapter
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val item = items[position]
    holder.bind(item)  // Не создавай новые объекты здесь!
}

3. Используй Object Pool для часто используемых объектов

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

GC Events в Logcat

// Примеры сообщений
I/GC: Explicit concurrent mark sweep GC freed 64MB
I/GC: Alloc concurrent mark sweep GC freed 20MB

Частые GC означают проблемы с памятью.

Best practices

  1. Минимизируй создание объектов в критических местах (onDraw, onScroll)
  2. Используй пулы объектов для частого переиспользования
  3. Избегай утечек памяти — отписывайся от событий
  4. Профилируй память регулярно
  5. Используй Weak/Soft References когда нужно
  6. Очищай ссылки в onDestroy()

Заключение

GarbageCollector — это критически важная система, которая управляет памятью автоматически. На Android нужно быть осторожным, чтобы избежать частых GC и утечек памяти, которые приводят к lag'ам и краш'ам. Понимание того, как работает GC, помогает написать более эффективный код.

Как работает GarbageCollector? | PrepBro