删除froom场景后,JavaFX自定义对话框有引用

时间:2015-10-21 07:33:12

标签: java javafx profiling visualvm

我有一个自定义对话框,添加到场景中,然后再次删除。使用VisualVM进行性能分析时,我注意到即使在GC运行之后,仍然会保留此对话框的实例。

我知道这意味着必须在某个地方引用该对象,所以我查看了引用:

enter image description here

如图所示,this$有很多引用,这意味着内部类,在这种情况下,它们是绑定或ChangeListener。更改侦听器可以替换为WeakChangeListener。我不太确定我应该如何处理Bindings。

此外,有些参考文献乍一看没有多大意义:

    类型为beanSimpleStringProperty
  • SimpleObjectProperty 类型oldParent
  • valueNode$1

以下是具体问题: 如何绕过这些强引用,所以对象实际上可以被垃圾收集?在这方面,使用lambda表达式而不是匿名内部类会有什么影响吗?如何通过beanoldParentvalue确定对象的引用位置。

EDIT1: 类型bean的{​​{1}}引用在超类中使用,因此我不应该在这里引起问题。一个SimpleStringProperty SimpleObjectProperty引用来自提供EventHandler的实用程序方法。我如何解决这个问题,beanEventHandler s类似吗?

EDIT2: 我试图想出一个简单的应用程序来重现同样的事情。我可以管理它,看到我在堆转储中列出了基本相同的字段,但后来发现我保留了对应用程序中场景中删除的组件的引用。一旦我放弃了那个参考,它就被清理干净了。唯一明显的区别是在我的小例子中,Object数组中没有引用。

EDIT3: 我做了一些挖掘,发现代码中有两个地方,当注释掉或不使用时,不会导致该对象符合垃圾回收的条件。第一个是ChangeListener

ChangeListener

第二个更复杂一点。该组件是一个具有关闭按钮的对话框。按下该对话框关闭。这是按钮的代码,我不认为这部分是问题所在,但为了完整起见:

sailorState.numberOfSailorsProperty().addListener(new ChangeListener<Number>() {
    @Override
    public void changed(ObservableValue<? extends Number> observableValue,
            Number oldValue, Number newValue) {
    int inTavern = newValue.intValue()-sailorsAdditionalOnShip.get();
    if (inTavern < 0) {
        sailorsAdditionalOnShip.set(Math.max(sailorsAdditionalOnShip.get() + inTavern, 0));
        inTavern = 0;
    }
    sailorsInTavern.set(inTavern);
    }
});

以下是实例化按钮的代码片段:

public class OpenPatricianButton extends Control {

    protected final StringProperty text;
    protected final ReadOnlyObjectProperty<Font> currentFont;
    protected final ObjectProperty<EventHandler<MouseEvent>> onAction;

    public OpenPatricianButton(String text,
            final Font font) {
        super();
        this.text = new SimpleStringProperty(this, "text", text);
        this.currentFont = new ReadOnlyObjectPropertyBase<Font>() {

            @Override
            public Object getBean() {
                return this;
            }

            @Override
            public String getName() {
                return "currentFont";
            }

            @Override
            public Font get() {
                return font;
            }
        };
        this.onAction = new SimpleObjectProperty<EventHandler<MouseEvent>>(this, "onAction");
        this.getStyleClass().add(this.getClass().getSimpleName());
    }

    @Override
    public String getUserAgentStylesheet() {
        URL cssURL = getClass().getResource("/ch/sahits/game/javafx/control/"+getClass().getSimpleName()+".css");
        return cssURL.toExternalForm();
    }

    public StringProperty textProperty() {
        return text;
    }

    public String getText() {
        return text.get();
    }

    public void setText(String text) {
        this.text.set(text);
    }

    public Font getFont() {
        return currentFont.get();
    }

    public ObjectProperty<EventHandler<MouseEvent>> onActionProperty() {
        return onAction;
    }

    public EventHandler<MouseEvent> getOnAction() {
        return onAction.get();
    }

    public void setOnAction(EventHandler<MouseEvent> onAction) {
        this.onAction.set(onAction);
    }

}

public class OpenPatricianSmallWaxButton extends OpenPatricianButton {

    public OpenPatricianSmallWaxButton(String text,
            final Font font) {
        super(text, font);
    }

    @Override
    protected Skin<?> createDefaultSkin() {
        return new OpenPatricianSmallWaxButtonSkin(this);
    }

    public OpenPatricianSmallWaxButton(String text) {
        this(text, Font.getDefault());
    }
}

public class OpenPatricianSmallWaxButtonSkin extends SkinBase<OpenPatricianSmallWaxButton> {

    public OpenPatricianSmallWaxButtonSkin(final OpenPatricianSmallWaxButton button) {
        super(button);

        InputStream is = getClass().getResourceAsStream("sealingWaxFlattend.png");
        Image img = new Image(is);
        final ImageView imageView = new ImageView(img);

        final Label label = new Label();
        label.textProperty().bind(button.textProperty());
        label.getStyleClass().add("OpenPatricianSmallWaxButtonLabeled");
        label.setFont(button.getFont());
        label.onMouseClickedProperty().bind(button.onActionProperty());
        label.textProperty().bind(button.textProperty());
        imageView.onMouseReleasedProperty().bind(button.onActionProperty());

        StackPane stack = new StackPane();
        stack.getChildren().addAll(imageView, label);
        Group group = new Group(stack);

        group.setManaged(false);

        button.setPrefHeight(img.getHeight());
        button.setPrefWidth(img.getWidth());

        getChildren().add(group);
    }
}

删除按钮的调用是通过Guava closeButton = new OpenPatricianSmallWaxButton("X", font); closeButton.setLayoutX(WIDTH - CLOSE_BUTTON_WIDTH - CLOSE_BUTTON_PADDING); closeButton.setLayoutY(CLOSE_BTN_Y_POS); closeButton.setOnAction(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { executeOnCloseButtonClicked(); } }); closeButton.getStyleClass().add("buttonLabel"); getContent().add(closeButton); 完成的。因此代码有点长。它从Application线程开始,然后被发布到事件总线线程,然后最终必须调用AsyncEventBus

Platform.runLater

真正奇怪的是,当我从计时器调用protected void executeOnCloseButtonClicked() { ViewChangeEvent event = new ViewChangeEvent(MainGameView.class, EViewChangeEvent.CLOSE_DIALOG); clientEventBus.post(event); } public void handleViewChange(ViewChangeEvent event) { if (event.getAddresse().equals(MainGameView.class)) { if (event.getEventNotice() instanceof DialogTemplate) { setNewDialog((DialogTemplate) event.getEventNotice()); } else { sceneEventHandlerFactory.getSceneEventHandler().handleEvent(event.getEventNotice()); } } } public void handleEvent(Object eventNotice) { Preconditions.checkNotNull(dialogContoller, "Dialog controller must be initialized first"); if (eventNotice == EViewChangeEvent.CLOSE_DIALOG) { dialogContoller.closeDialog(); } .... public void closeDialog() { if (Platform.isFxApplicationThread()) { closeDialogUnwrapped(); } else { Platform.runLater(() -> closeDialogUnwrapped()); } } private void closeDialogUnwrapped() { if (dialog != null) { new Exception("Close dialog").printStackTrace(); getChildren().remove(dialog); dialog = null; dialogScope.closeScope(); } } 时,GC可以清除对话框(前提是ChangeListener的第一个问题已被注释掉)。换句话说,只有在用鼠标单击关闭对话框时才会发生此行为。

0 个答案:

没有答案