Czy istnieje bezpieczny sposób korzystać z czyszczenia wypisują detektora?

0

Pytanie

Mam klasę działań Swing, który działa w następujący sposób:

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)));
    }
}

To FinalizeGuardian wspomniany tu obecnie realizowany z wykorzystaniem 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();
        }
    }
}

Tak więc, z oczywistych względów chciałbym przełączyć się na korzystanie z Cleaner za to.

Pierwsza próba była mniej więcej taki:

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);
    }
}

Problem w tym, że obiekt nie będzie widoczny dla phantom, bo:

  • Cleaner sama w sobie zawiera silną link do cleanupLogic
  • cleanupLogic zawiera link do listener aby usunąć słuchacza
  • listener zawiera odwołanie do klasy działań w celu połączenia updateEnabled na nim
  • klasa akcji zawiera link do FinalizeGuardian aby go nie zabrał przedwcześnie

Bo FinalizeGuardian samo w sobie nigdy nie będzie osiągalne ułudą, pługa nigdy nie wywołają.

Dlatego chciałbym wiedzieć, czy istnieje sposób, aby odbudować to, aby przestrzegać zasad, niezbędnych do Cleaner poprawnie pracować, aby nie naruszać enkapsulacji, przesuwając detektor za granice mojej klasy działań?

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

Najlepsza odpowiedź

3

FlavorListener zarejestrowany w źródle zdarzeń, nigdy nie stanie się nieosiągalne (gdy źródło zdarzeń nadal jest dostępny). Oznacza to, że PasteAction egzemplarz, który detektor aktualizuje, również nigdy nie stanie się niedostępny, tak jak u detektora jest silny link do niego.

Jedynym sposobem, aby oddzielić ich достижимость to zmienić detektor, aby utrzymać tylko słabą odwołanie do obiektu, który aktualizuje. Należy pamiętać, że kiedy używasz Cleaner zamiast finalize()w FinalizeGuardian jest nieaktualny.

Kod będzie wyglądał tak

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);
    }

…

Należy pamiętać, że krytyczne części zostały umieszczone w static metody pozwalające przypadkowo uchwycić this link nie jest możliwe. Metody te otrzymują minimum, wymaganego do wykonywania pracy, createListener dostaje tylko słabą link do akcji i prepareCleanup zwraca referent jak Objectponieważ akcja czyszczenia nie musi zwracać się do jakichkolwiek elementów działania, ale otrzymywać żądane wartości w postaci poszczególnych parametrów.

Ale po tym, jak pokazałem, jak może wyglądać użycie środka czyszczącego, muszę stanowczo nie polecam korzystać z tego mechanizmu, zwłaszcza jako jedynego mechanizmu czyszczenia. Tutaj ma to wpływ nie tylko na zużycie pamięci, ale i na zachowanie programu, bo aż linki nie będą usunięte, słuchacz będzie nadal otrzymywać informacje i aktualizować przestarzały obiekt.

Ponieważ garbage collector uruchamia się tylko z powodu wymagań w pamięci, możliwe jest, że on nie działa lub jest mu obojętne te kilka obiektów, bo wystarczająco dużo wolnej pamięci, podczas gdy procesor jest pod dużym obciążeniem, z powodu dużej liczby starszych detektorów pracujących aktualizacji starszych obiektów (widziałem takie scenariusze w praktyce).

Co gorsza, przy jednoczesnych сборщиках śmieci być może nawet, że ich cykl zbierania niejednokrotnie pokrywa się z faktycznie przestarzałe wykonaniem updateEnabled() rozpoczyna słuchaczem (bo link nie była czyszczona). Co będzie aktywnie utrudniać odbioru śmieci z tych obiektów, nawet jeśli garbage collector uruchamia się i w przeciwnym razie zbierał je.

Krótko mówiąc, takie czyszczenie nie powinno zależeć od garbage collector.

2021-11-26 15:49:36

W innych językach

Ta strona jest w innych językach

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