Problem with Timeline keeping references around

John Hendrikx hjohn at xs4all.nl
Tue Oct 15 17:48:20 PDT 2013


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



More information about the openjfx-dev mailing list