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

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

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

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

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

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

Основы PhantomReference в Java

PhantomReference — это один из четырёх типов ссылок в Java (наряду с Strong, Soft и Weak), который предоставляет наиболее гибкий контроль над процессом сборки мусора. Его ключевая особенность — фантомные ссылки не предоставляют доступ к объекту даже до его финализации.

Особенности PhantomReference

  • Не возвращают объект: Метод get() всегда возвращает null
  • Попадают в ReferenceQueue только после финализации объекта
  • Требуют явной очистки: Нужно вызывать clear() или enqueue()
  • Используются для пост-обработки ресурсов

Основное применение

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class PhantomReferenceExample {
    private static class ResourceHolder {
        private byte[] data = new byte[1024 * 1024]; // 1MB
        private int id;
        
        public ResourceHolder(int id) {
            this.id = id;
        }
        
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Финализация ResourceHolder " + id);
            super.finalize();
        }
    }
    
    public static void main(String[] args) {
        ReferenceQueue<ResourceHolder> queue = new ReferenceQueue<>();
        ResourceHolder holder = new ResourceHolder(1);
        PhantomReference<ResourceHolder> phantomRef = 
            new PhantomReference<>(holder, queue);
        
        // Теперь объект может быть собран
        holder = null;
        
        // Принудительная сборка мусора
        System.gc();
        
        try {
            // Ждем, пока ссылка появится в очереди
            PhantomReference<ResourceHolder> ref = 
                (PhantomReference<ResourceHolder>) queue.remove(1000);
            if (ref != null) {
                System.out.println("Объект был финализирован и готов к очистке");
                ref.clear(); // Явная очистка
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Практическое использование в Android

1. Мониторинг больших объектов

public class LargeObjectMonitor {
    private final ReferenceQueue<Bitmap> queue = new ReferenceQueue<>();
    private final Set<PhantomReference<Bitmap>> references = 
        new HashSet<>();
    
    public void trackBitmap(Bitmap bitmap) {
        PhantomReference<Bitmap> ref = 
            new PhantomReference<>(bitmap, queue);
        references.add(ref);
        cleanup(); // Очистка отслеживаемых ссылок
    }
    
    private void cleanup() {
        PhantomReference<? extends Bitmap> ref;
        while ((ref = (PhantomReference<? extends Bitmap>) queue.poll()) 
               != null) {
            references.remove(ref);
            // Выполнить дополнительную очистку ресурсов
            onBitmapFinalized();
        }
    }
    
    private void onBitmapFinalized() {
        // Логирование или уведомление о освобождении памяти
        Log.d("Memory", "Bitmap был финализирован");
    }
}

2. Управление нативными ресурсами

public class NativeResourceManager {
    static class NativeWrapper {
        private long nativePtr;
        
        public NativeWrapper(long ptr) {
            this.nativePtr = ptr;
        }
        
        public native void dispose(); // Нативный метод освобождения
        
        @Override
        protected void finalize() throws Throwable {
            dispose();
            super.finalize();
        }
    }
    
    private final ReferenceQueue<NativeWrapper> queue = 
        new ReferenceQueue<>();
    private final Map<PhantomReference<NativeWrapper>, Runnable> 
        cleanupActions = new HashMap<>();
    
    public void registerResource(NativeWrapper wrapper, Runnable cleanup) {
        PhantomReference<NativeWrapper> ref = 
            new PhantomReference<>(wrapper, queue);
        cleanupActions.put(ref, cleanup);
        
        // Запускаем поток для мониторинга очереди
        new CleanupThread().start();
    }
    
    private class CleanupThread extends Thread {
        @Override
        public void run() {
            while (!isInterrupted()) {
                try {
                    PhantomReference<?> ref = 
                        (PhantomReference<?>) queue.remove();
                    Runnable cleanup = cleanupActions.remove(ref);
                    if (cleanup != null) {
                        cleanup.run();
                    }
                    ref.clear();
                } catch (InterruptedException e) {
                    break;
                }
            }
        }
    }
}

Ключевые преимущества PhantomReference

  1. Предсказуемая очистка: Гарантированное выполнение после финализации
  2. Безопасность от воскрешения объектов: get() всегда возвращает null
  3. Возможность выполнения произвольного кода после сборки мусора
  4. Мониторинг утечек памяти без влияния на жизненный цикл объектов

Рекомендации по использованию

  • Всегда используйте ReferenceQueue для отслеживания фантомных ссылок
  • Явно очищайте ссылки через clear() после обработки
  • Избегайте создания ссылок в performance-critical коде
  • Используйте для нетривиальных ресурсов: файловые дескрипторы, нативная память, сетевые соединения

Сравнение с другими ссылками

Тип ссылкиget() возвращаетВремя уборкиИспользование
StrongОбъектНикогдаОбычные переменные
SoftОбъектПри нехватке памятиКэши
WeakОбъектСледующий GCСлабые коллекции
PhantomnullПосле финализацииПост-обработка

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