I'm having trouble creating a reproducable test case.

There seems to be more going on -- if I open and close the Pane involved several times, eventually some of them will start to be GC'd (but never all, unless I disable the animation). I've used VisualVM to inspect the Pane involved, and here is what it says is keeping it alive (clearly something animation related atleast):

this     - value: hs.mediasystem.screens.playback.PlaybackOverlayPane #1
<- this$0 - class: javafx.scene.Node$8, value: hs.mediasystem.screens.playback.PlaybackOverlayPane #1 <- target - class: com.sun.scenario.animation.shared.InterpolationInterval$DoubleInterpolationInterval, value: javafx.scene.Node$8 #149 <- [0] - class: com.sun.scenario.animation.shared.InterpolationInterval[], value: com.sun.scenario.animation.shared.InterpolationInterval$DoubleInterpolationInterval #28 <- [0] - class: com.sun.scenario.animation.shared.InterpolationInterval[][], value: com.sun.scenario.animation.shared.InterpolationInterval[] #10 (3 items) <- interval - class: com.sun.scenario.animation.shared.GeneralClipInterpolator, value: com.sun.scenario.animation.shared.InterpolationInterval[][] #16 (1 items) <- clipInterpolator - class: com.sun.scenario.animation.shared.TimelineClipCore, value: com.sun.scenario.animation.shared.GeneralClipInterpolator #16 <- clipCore - class: javafx.animation.Timeline, value: com.sun.scenario.animation.shared.TimelineClipCore #19 <- this$0 - class: javafx.animation.Animation$1, value: javafx.animation.Timeline #19 <- [5] - class: com.sun.scenario.animation.shared.PulseReceiver[], value: javafx.animation.Animation$1 #19 <- receivers - class: com.sun.javafx.tk.quantum.MasterTimer, value: com.sun.scenario.animation.shared.PulseReceiver[] #1 (7 items) <- this$0 - class: com.sun.scenario.animation.AbstractMasterTimer$MainLoop, value: com.sun.javafx.tk.quantum.MasterTimer #1 <- animationRunnable - class: com.sun.javafx.tk.quantum.QuantumToolkit, value: com.sun.scenario.animation.AbstractMasterTimer$MainLoop #1 <- this$0 (JNI global) - class: com.sun.javafx.tk.quantum.QuantumToolkit$14, value: com.sun.javafx.tk.quantum.QuantumToolkit #1

If that is enough for a bugreport, I'll create one. A naive simple test case which spawns several panes and animations was unable to reproduce this issue I'm having:

package hs.mediasystem;

import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WeakChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TimelineGCProblem extends Application {

  public static void main(String[] args) {
    Application.launch(args);
  }

  @Override
  public void start(Stage stage) throws Exception {
    StackPane rootPane = new StackPane();
    Scene scene = new Scene(rootPane);

    stage.setScene(scene);
    stage.show();

    new Thread() {
      @Override
      public void run() {
        for(;;) {
          Platform.runLater(new Runnable() {
            @Override
            public void run() {
              rootPane.getChildren().setAll(new FadeInPane());
            }
          });

          try {
            System.gc();
            Thread.sleep(1500);
            System.gc();
          }
          catch(InterruptedException e) {
            e.printStackTrace();
          }
        }
      }

    }.start();
  }

  public static class FadeInPane extends StackPane {
    private final Timeline fadeInSustainAndFadeOut = new Timeline(
      new KeyFrame(Duration.seconds(0)),
new KeyFrame(Duration.seconds(1), new KeyValue(opacityProperty(), 1.0)), new KeyFrame(Duration.seconds(6), new KeyValue(opacityProperty(), 1.0)), new KeyFrame(Duration.seconds(8), new KeyValue(opacityProperty(), 0.0))
    );

private final ChangeListener<Scene> sceneChangeListener = new ChangeListener<Scene>() {
      @Override
public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
        if(newValue != null) {
          System.out.println(">>> Starting fadeIn anim");
          fadeInSustainAndFadeOut.playFromStart();
        }
        else {
          System.out.println(">>> Stopping fadeIn anim");
          fadeInSustainAndFadeOut.stop();
        }
      }
    };

    public FadeInPane() {
sceneProperty().addListener(new WeakChangeListener<>(sceneChangeListener));

      setStyle("background-color: red");
      getChildren().add(new Button("Hi"));
    }

    @Override
    protected void finalize() throws Throwable {
      super.finalize();
      System.out.println("FadeInPane finalized");
    }
  }
}


--John

On 16/10/2013 00:47, Stephen F Northover wrote:
This looks like a bug. Timeline should not be holding on to references. Please enter a JIRA.

Steve

On 2013-10-15 6:35 PM, John Hendrikx wrote:
Hi List,

I just submitted https://javafx-jira.kenai.com/browse/RT-33600 which is asking for the Timeline JavaDocs to be more clear on when and where hard references are being created and how to properly clean up after oneself. Most of the docs hardly mention anything when it comes to references causing memory leaks, and Timeline is no exception.

However, apart from the docs not really discussing this topic, I actually cannot get proper cleanup to work at all when working with a Timeline.

If my Timeline is referencing properties from a StackPane and the Timeline was run atleast once, I cannot figure out how to get this Pane to be gc'd. Calling stop() and even doing getKeyFrames().clear() doesn't help. I'm sure it must be the Timeline because if I remove the KeyFrames that access the StackPane, it will get gc'd properly. If I never run the animation, it also gets gc'd properly.

See code below. The System.outs will occur exactly as expected when the StackPane becomes part of the Scene and when it is removed from the Scene. I call stop() on my Timeline and even clear its KeyFrames, but the StackPane refuses to be gc'd (even after hitting the GC button I created for this occasion dozens of times).

Not calling playFromStart() or not using the KeyFrames that refer to detailsOverlay.opacityProperty() will solve the problem, and the finalize() System.out will occur usually after the first or second time I press my GC button.

Anything else I can try or is this a bug?
--John

public class PlaybackOverlayPane extends StackPane {
  private final Timeline fadeInSustainAndFadeOut = new Timeline(
    new KeyFrame(Duration.seconds(0)),
new KeyFrame(Duration.seconds(1), new KeyValue(detailsOverlay.opacityProperty(), 1.0)), new KeyFrame(Duration.seconds(6), new KeyValue(detailsOverlay.opacityProperty(), 1.0)), new KeyFrame(Duration.seconds(9), new KeyValue(detailsOverlay.opacityProperty(), 0.0))
  );

private final ChangeListener<Scene> sceneChangeListener = new ChangeListener<Scene>() {
    @Override
public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
      if(newValue != null) {
        System.out.println(">>> Starting fadeIn anim");
        fadeInSustainAndFadeOut.playFromStart();
      }
      else {
        System.out.println(">>> Stopping fadeIn anim");
        fadeInSustainAndFadeOut.stop();
        fadeInSustainAndFadeOut.getKeyFrames().clear();
        fadeInSustainAndFadeOut.stop();
      }
    }
  };

private final GridPane detailsOverlay = GridPaneUtil.create(new double[] {5, 20, 5, 65, 5}, new double[] {45, 50, 5});

 ...

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println(">>> Finalized " + this);
  }
}


Reply via email to