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

Как используется WeakReference

2.2 Middle🔥 151 комментариев
#JVM и память

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

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

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

Как используется WeakReference

WeakReference — это специальный вид ссылки в Java, который позволяет сборщику мусора (GC) удалить объект, даже если на него есть ссылка. В отличие от обычных (сильных) ссылок, WeakReference не препятствует garbage collection. Это особенно полезно в Android для предотвращения утечек памяти.

Основной концепт

// Обычная (strong) ссылка — объект НЕ может быть собран GC
val user: User = User("Alice")

// WeakReference — объект МОЖЕТ быть собран GC
val userRef: WeakReference<User> = WeakReference(user)

// Получить объект
val userOrNull = userRef.get()  // может быть null!
if (userOrNull != null) {
    println(userOrNull.name)
}

Когда WeakReference становится null

WeakReference обнуляется в следующих сценариях:

  1. Нет больше strong ссылок — если единственная strong ссылка удалена
  2. Произошла сборка мусора — после GC объект с WeakReference может быть удалён
  3. Нет более срочных ссылок — если есть PhantomReference, он имеет приоритет
class Cache {
    private val weakCache = WeakHashMap<String, User>()
    
    fun putUser(key: String, user: User) {
        weakCache[key] = user
    }
    
    fun getUser(key: String): User? {
        return weakCache[key]  // может вернуть null после GC
    }
}

Практический пример: предотвращение утечки памяти

Проблема: Если передать Context в AsyncTask или Handler, может быть утечка памяти:

// ПЛОХО — утечка памяти!
class LoadUserTask(private val activity: MainActivity) : AsyncTask<String, Void, User>() {
    override fun doInBackground(vararg params: String?): User {
        // долгая операция
        Thread.sleep(5000)
        return fetchUserFromServer(params[0])
    }
    
    override fun onPostExecute(result: User) {
        // Activity может быть удалена из памяти, но AsyncTask всё ещё держит ссылку!
        activity.updateUI(result)
    }
}

Если пользователь закроет Activity во время выполнения AsyncTask, Activity остаётся в памяти.

Решение с WeakReference:

// ХОРОШО — используем WeakReference
class LoadUserTask(activity: MainActivity) : AsyncTask<String, Void, User>() {
    private val activityRef = WeakReference(activity)
    
    override fun doInBackground(vararg params: String?): User {
        Thread.sleep(5000)
        return fetchUserFromServer(params[0])
    }
    
    override fun onPostExecute(result: User) {
        val activity = activityRef.get()
        if (activity != null && !activity.isFinishing) {
            activity.updateUI(result)
        }
        // Если Activity был удалён GC, просто ничего не делаем
    }
}

WeakReference vs другие типы ссылок

// StrongReference (обычная переменная)
val user = User("Bob")  // Объект НЕ может быть удалён

// WeakReference
val userWeak = WeakReference(User("Bob"))  // Объект МОЖЕТ быть удалён при GC

// SoftReference
val userSoft = SoftReference(User("Bob"))  // Удаляется при нехватке памяти

// PhantomReference
val userPhantom = PhantomReference(User("Bob"), queue)  // Всегда удаляется, для очистки ресурсов

// Пример с иерархией
var user: User? = User("Charlie")  // Strong reference
val weakRef = WeakReference(user)
val softRef = SoftReference(user)

user = null  // Удаляем strong reference

// Теперь объект может быть собран GC
// weakRef.get() может вернуть null
// softRef.get() вернёт null при нехватке памяти

WeakHashMap в Android

WeakHashMap очень полезен для кэширования, когда ключи должны быть удаляемы:

class ImageCache {
    // Изображения удаляются автоматически при GC
    private val cache = WeakHashMap<String, Bitmap>()
    
    fun put(key: String, bitmap: Bitmap) {
        cache[key] = bitmap
    }
    
    fun get(key: String): Bitmap? {
        return cache[key]  // может быть null после GC
    }
    
    fun size(): Int {
        return cache.size  // size меняется после GC!
    }
}

// Использование
val imageCache = ImageCache()
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image)
imageCache.put("avatar", bitmap)

// Позже
val cachedBitmap = imageCache.get("avatar")  // может быть null

Handler и Runnable с WeakReference

Проблема: Handler может содержать ссылку на Activity:

// ПЛОХО
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        Handler(Looper.getMainLooper()).postDelayed({
            // Если Activity удалена, это может вызвать crash
            updateUI()
        }, 5000)
    }
}

// ХОРОШО — используем WeakReference
class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val handler = WeakHandler(this)
        handler.postDelayed({ updateUI() }, 5000)
    }
}

class WeakHandler<T>(target: T) : Handler(Looper.getMainLooper()) {
    private val targetRef = WeakReference(target)
    
    fun postDelayed(action: () -> Unit, delayMillis: Long) {
        postDelayed({
            val target = targetRef.get()
            if (target != null) {
                action.invoke()
            }
        }, delayMillis)
    }
}

Listener callbacks с WeakReference

class LocationManager {
    private val listeners = mutableSetOf<WeakReference<LocationListener>>()
    
    fun addListener(listener: LocationListener) {
        listeners.add(WeakReference(listener))
    }
    
    fun removeListener(listener: LocationListener) {
        listeners.removeAll { it.get() == null || it.get() == listener }
    }
    
    fun notifyListeners(location: Location) {
        // Очищаем мёртвые ссылки
        listeners.removeAll { it.get() == null }
        
        // Уведомляем живых слушателей
        for (ref in listeners) {
            ref.get()?.onLocationChanged(location)
        }
    }
}

interface LocationListener {
    fun onLocationChanged(location: Location)
}

Правила использования WeakReference

  1. Не используй для критичных данных — объект может быть удалён в любой момент
  2. Всегда проверяй null — перед использованием вызови get()
  3. Используй для кэшей — WeakHashMap идеален для кэширования
  4. Используй для обратных вызовов — Listener, Callback, Handler
  5. Не переусложняй — если не нужно, не используй

Когда НЕ использовать WeakReference

// ПЛОХО — критичные данные
val userRef = WeakReference(currentUser)  // может быть null в любой момент

// ХОРОШО — используй strong reference
val user: User = currentUser

Производительность

  • WeakReference немного медленнее обычных ссылок (нужно дополнительное отслеживание)
  • В большинстве случаев разница незаметна
  • Главный плюс — предотвращение утечек памяти

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

Как используется WeakReference | PrepBro