Există o modalitate sigură de a folosi Aspiratorul pentru a retrage un ascultător?

0

Problema

Am un Leagăn de acțiune de clasă care funcționează după cum urmează:

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

La FinalizeGuardian menționată aici este în prezent implementat folosind 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();
        }
    }
}

Deci, din motive evidente, aș dori pentru a comuta la utilizarea Cleaner pentru aceasta.

Prima încercare a fost ceva de genul:

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

Problema este că acum obiectul nu devine fantomă accesibil, pentru că:

  • Cleaner în sine deține o puternică trimitere la cleanupLogic
  • cleanupLogic deține o trimitere la listener în scopul de a elimina ascultător
  • listener deține o trimitere la o acțiune de clasă, în scopul de a suna updateEnabled pe ea
  • acțiunea clasa deține o trimitere la FinalizeGuardian așa că nu te colectate prematur

Pentru că FinalizeGuardian în sine nu devine fantomă accesibil, curat nu va fi numit.

Deci, ce aș vrea să știu, există o modalitate de a restructura aceasta să respecte regulile necesare pentru a face Cleaner funcționeze corect, care nu se implică de rupere de încapsulare prin mutarea ascultător în afara mea acțiune de clasă?

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

Cel mai bun răspuns

3

Atâta timp cât FlavorListener este înregistrată la un eveniment sursă, niciodată nu va deveni inaccesibil (atâta timp cât sursa evenimentului este încă accesibil). Acest lucru implică faptul că PasteAction exemplu care ascultătorul actualizări, de asemenea, va deveni niciodată de neatins, ca ascultător are o puternică trimitere la acesta.

Singura modalitate de a decupla lor de accesibilitate este de a schimba ascultător, pentru a menține numai o slabă referință la obiectul se actualizează. Rețineți că atunci când utilizați un Cleaner în loc de finalize(), la FinalizeGuardian este depășită.

Codul ar arata ca

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

…

Rețineți că părțile critice au fost plasate în static metode, pentru a face captura accidentală a this referință imposibil. Aceste metode se obține minimul necesar pentru a face treaba lor, createListener primește doar o slabă referință la acțiune și prepareCleanup devine referent ca Object, ca acțiune de curățare nu trebuie să acceseze oricare dintre membrii acțiune, dar primi valorile necesare ca parametrii separate.

Dar după ce arată cum un aspirator de utilizare poate arata ca, trebuie sa descurajeze puternic de folosind acest mecanism, mai ales ca singurul mecanism de curățare. Aici, este nu numai afectează consumul de memorie, dar, de asemenea, comportamentul de program, pentru că, atâta timp cât referințele nu au fost eliminate, ascultatorul va ține obtinerea informat și să păstreze actualizarea unui obiect învechit.

Deoarece colectorul de gunoaie este doar declanșate de memorie are nevoie, este perfect posibil ca nu merge sau nu-i pasă de aceste câteva obiecte, pentru că nu există suficientă memorie liberă, în timp ce PROCESORUL este sub sarcină grea, pentru că de o mulțime de învechit ascultătorii fiind ocupat pentru a actualiza învechite obiecte (am vazut astfel de scenarii în practică).

Pentru a face lucrurile mai rău, concomitent cu colecționari de gunoi, este chiar posibil ca lor ciclu de colectare în mod repetat se suprapune cu un fapt învechite executarea updateEnabled() declanșat de către ascultător (pentru referință, nu a fost eliminat încă). Care va preveni în mod activ de colectare a gunoiului de aceste obiecte, chiar si atunci cand colectorul de gunoi conduce și care, altfel, le colecta.

Pe scurt, o astfel de curățire ar trebui să nu se bazeze pe colectorul de gunoi.

2021-11-26 15:49:36

În alte limbi

Această pagină este în alte limbi

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