Bir dinleyicinin kaydını silmek için Cleaner'ı kullanmanın güvenli bir yolu var mı?

0

Soru

Aşağıdaki gibi çalışan bir Swing eylem sınıfım var:

package org.trypticon.hex.gui.datatransfer;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.FlavorListener;
import java.awt.event.ActionEvent;
import javax.annotation.Nonnull;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.TransferHandler;

import org.trypticon.hex.gui.Resources;
import org.trypticon.hex.gui.util.FinalizeGuardian;
import org.trypticon.hex.gui.util.FocusedComponentAction;

public class PasteAction extends FocusedComponentAction {
    private final FlavorListener listener = (event) -> {
        // this method in the superclass calls back `shouldBeEnabled`
        updateEnabled();
    };

    @SuppressWarnings({"UnusedDeclaration"})
    private final Object finalizeGuardian = new FinalizeGuardian(() -> {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.removeFlavorListener(listener);
    });

    public PasteAction() {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.addFlavorListener(listener);
    }

    @Override
    protected boolean shouldBeEnabled(@Nonnull JComponent focusOwner) {
        TransferHandler transferHandler = focusOwner.getTransferHandler();
        if (transferHandler == null) {
            return false;
        }

        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        DataFlavor[] flavorsInClipboard = clipboard.getAvailableDataFlavors();
        return transferHandler.canImport(focusOwner, flavorsInClipboard);
    }

    @Override
    protected void doAction(@Nonnull JComponent focusOwner) throws Exception {
        Action action = TransferHandler.getPasteAction();
        action.actionPerformed(new ActionEvent(
            focusOwner, ActionEvent.ACTION_PERFORMED, (String) action.getValue(Action.NAME)));
    }
}

Bu FinalizeGuardian burada belirtilen şu anda kullanılarak uygulanmaktadır finalize():

package org.trypticon.hex.gui.util;

public final class FinalizeGuardian {
    private final Runnable cleanupLogic;

    public FinalizeGuardian(Runnable cleanupLogic) {
        this.cleanupLogic = cleanupLogic;
    }

    @Override
    protected final void finalize() throws Throwable {
        try {
            cleanupLogic.run();
        } finally {
            super.finalize();
        }
    }
}

Bu yüzden, bariz nedenlerden dolayı, kullanmaya geçmek istiyorum Cleaner bunun için.

İlk deneme böyle bir şeydi:

package org.trypticon.hex.gui.util;

import java.lang.ref.Cleaner;

public final class FinalizeGuardian {
    private static final Cleaner cleaner = Cleaner.create();

    public FinalizeGuardian(Runnable cleanupLogic) {
        cleaner.register(this, cleanupLogic);
    }
}

Sorun şu ki, nesne hiçbir zaman hayalete ulaşılamaz hale gelmiyor, çünkü:

  • Cleaner kendisi güçlü bir referansa sahiptir cleanupLogic
  • cleanupLogic bir başvuru tutar listener dinleyiciyi kaldırmak için
  • listener çağırmak için action sınıfına bir başvuru tutar updateEnabled üzerine
  • action sınıfı aşağıdakilere bir başvuru içerir: FinalizeGuardian böylece erken toplanmaz.

Çünkü FinalizeGuardian hiç hayalet erişilebilir olur kendisi, temiz hiç denecek.

Yani bilmek istediğim, bunu yapmak için gereken kurallara uyacak şekilde yeniden yapılandırmanın bir yolu var mı Cleaner dinleyiciyi eylem sınıfımın dışına taşıyarak kapsüllemeyi kırmayı içermeyen doğru çalışma mı?

garbage-collection java swing
2021-11-24 01:39:09
1

En iyi cevabı

3

Sürece FlavorListener bir olay kaynağında kayıtlıysa, hiçbir zaman erişilemez hale gelmez (olay kaynağına hala erişilebilir olduğu sürece). Bu ima PasteAction dinleyicinin güncellediği örnek, dinleyicinin kendisine güçlü bir referansı olduğu için asla erişilemez hale gelmez.

Erişilebilirliklerini ayırmanın tek yolu, dinleyiciyi değiştirmek, yalnızca güncelleştirdiği nesneye zayıf bir başvuru sağlamaktır. Bunu kullanırken dikkat edin Cleaner yerine finalize(), bu FinalizeGuardian kullanılmıyor.

Kod şöyle görünürdü:

public class PasteAction extends FocusedComponentAction {

    static FlavorListener createListener(WeakReference<PasteAction> r) {
        return event -> {
            PasteAction pa = r.get();
            if(pa != null) pa.updateEnabled();
        };
    }

    private static final Cleaner CLEANER = Cleaner.create();

    static void prepareCleanup(
                       Object referent, Clipboard clipboard, FlavorListener listener) {

        CLEANER.register(referent, () -> clipboard.removeFlavorListener(listener));
    }

    public PasteAction() {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        FlavorListener listener = createListener(new WeakReference<>(this));
        clipboard.addFlavorListener(listener);
        prepareCleanup(this, clipboard, listener);
    }

…

Kritik parçaların içine yerleştirildiğini unutmayın static yöntemleri, kazara yakalanmasını sağlamak için this referans imkansız. Bu yöntemler işlerini yapmak için gereken minimum miktarı alır, createListener sadece eylem için zayıf bir referans alır ve prepareCleanup başvuruyu şu şekilde alır Object temizleme eyleminin eylemin herhangi bir üyesine erişmemesi, ancak gerekli değerleri ayrı parametreler olarak alması gerektiğinden.

Ancak, daha temiz bir kullanımın nasıl görünebileceğini gösterdikten sonra, özellikle tek temizleme mekanizması olarak bu mekanizmayı kullanmaktan vazgeçmem gerekiyor. Burada, yalnızca bellek tüketimini değil, aynı zamanda programın davranışını da etkiliyor, çünkü referanslar temizlenmediği sürece dinleyici bilgilendirilmeye devam edecek ve eski bir nesneyi güncellemeye devam edecektir.

Çöp toplayıcı yalnızca bellek ihtiyaçları tarafından tetiklendiğinden, bu birkaç nesneyi çalıştırmaması veya önemsememesi tamamen mümkündür, çünkü CPU ağır yük altındayken yeterli boş bellek vardır, çünkü çok sayıda eski dinleyicinin eski nesneleri güncellemekle meşgul olması nedeniyle (bu tür senaryoları pratikte gördüm).

Daha da kötüsü, eşzamanlı çöp toplayıcılarla, toplama döngülerinin gerçekte eski bir yürütme ile tekrar tekrar çakışması bile mümkündür updateEnabled() dinleyici tarafından tetiklenir (başvuru henüz temizlenmediğinden). Bu, çöp toplayıcı çalışırken bile bu nesnelerin çöp toplanmasını aktif olarak engelleyecek ve aksi takdirde bunları toplayacaktır.

Kısacası, böyle bir temizlik çöp toplayıcısına dayanmamalıdır.

2021-11-26 15:49:36

Diğer dillerde

Bu sayfa diğer dillerde

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................