是有一个安全的方式使用更清洁的注销一个监听器?

0

的问题

我有一个摇摆的动作类,其工作原理如下:

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

FinalizeGuardian 这里提到的是目前实施的使用 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();
        }
    }
}

因此,对于明显的原因,我想切换到使用 Cleaner 这一点。

第一次尝试是这样的:

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

问题是,现在的目永远不会变幻不到,因为:

  • Cleaner 本身持有强烈的参考 cleanupLogic
  • cleanupLogic 拥有一个参考 listener 为了删除该监听
  • listener 拥有基准的行动可以呼叫 updateEnabled 上它
  • 该行动类拥有一个参考 FinalizeGuardian 因此,它没有得到收集过早

因为 FinalizeGuardian 自己永远不变幻不到,清洁将永远不会被称为。

所以我想知道的是,是否有一种方式调整这种遵循规则的要求做 Cleaner 正常工作, 涉及破封装的通过移动侦听到外面我的动作课?

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

最好的答案

3

只要的 FlavorListener 登记的一个事件的来源,它将永远不会变得遥不可及(只要的事件源仍然是可到达). 这意味着 PasteAction 实例,其听众更新也将永远不会成为遥不可及,作为听众具有很强的参考。

唯一的办法来解除他们的可达性是改变的听众,仅保持一个软弱的参照对象它的更新。 注意,当你使用 Cleaner 而不是的 finalize()FinalizeGuardian 已经过时了。

代码看起来像

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

…

注意的重要部件已被放入 static 方法,使意外捕获 this 参考是不可能的。 这些方法,获得最低限度的必要做他们的工作, createListener 仅收到微弱的参考和行动 prepareCleanup 得到的指称作 Object,作为清洁的行动不得进入任何成员的行动,但得到必要的价值观作为独立的参数。

但是之后表示,如何更清洁的使用可能看起来像,我必须强烈反对使用这种机制,特别是作为唯一的清理机构。 在这里,它不仅影响到存储的消耗,而且 行为 的程序,因为,只要引用没有被清除,听众就会继续得到通知并保持更新过时的对象。

由于垃圾回收只是触发记忆的需求,这是完全可能的,它不能运行或不在乎这些对象,因为没有足够的免费存储器,同时CPU是在沉重的负担,因为大量过时的听众正在忙于更新过时的对象(我已经看到这种情况在实践).

更糟糕的是,并发垃圾收集器,它甚至有可能是他们收集周期反复重叠的一个实际上已过时的执行 updateEnabled() 引发通过的监听器(因为参照是不清)。 这将积极防止垃圾收集的这些对象,甚至当垃圾收集运行,并将收集他们。

总之,这种清理,不应依靠垃圾的收集器。

2021-11-26 15:49:36

其他语言

此页面有其他语言版本

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