3 Replies Latest reply: Apr 24, 2013 3:02 AM by csh RSS

    WeakReferences in Custom Binding!?

    csh
      Hi,

      I noticed some memory leaks again in context of binding.

      The documentation for custom binding (http://docs.oracle.com/javafx/2/api/javafx/beans/binding/Binding.html) says:

      >
      All bindings in our implementation use instances of WeakInvalidationListener, which means usually a binding does not need to be disposed. But if you plan to use your application in environments that do not support WeakReferences you have to dispose unused Bindings to avoid memory leaks.
      >

      Nonetheless, I noticed leaks, e.g. with this sample application:
      import javafx.application.Application;
      import javafx.beans.binding.StringBinding;
      import javafx.beans.property.IntegerProperty;
      import javafx.beans.property.SimpleIntegerProperty;
      import javafx.scene.Group;
      import javafx.scene.Scene;
      import javafx.scene.control.Label;
      import javafx.stage.Stage;
      
      public class TestApp2 extends Application {
          public static void main(String[] args) {
              Application.launch(args);
          }
      
          private IntegerProperty integerProperty = new SimpleIntegerProperty();
      
          @Override
          public void start(Stage stage) throws Exception {
      
              for (int i = 0; i < 2000000; i++) {
                  Label label = new Label();
      
                  label.textProperty().bind(new StringBinding() {
                      {
                          super.bind(integerProperty);
                      }
      
                      @Override
                      protected String computeValue() {
                          return Integer.toString(integerProperty.get());
                      }
                  });
              }
      
              System.gc();
              System.out.println((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 + " KB");
      
      
              stage.setScene(new Scene(new Group(), 400, 400));
              stage.show();
          }
      }
      If I look through the code:
      http://hg.openjdk.java.net/openjfx/8/master/rt/file/16e4c07f8562/javafx-beans/src/javafx/beans/binding/StringBinding.java

      and

      http://hg.openjdk.java.net/openjfx/8/master/rt/file/16e4c07f8562/javafx-beans/src/com/sun/javafx/binding/BindingHelperObserver.java

      there is indeed a WeakReference used.

      But the binding itself (the listener) is only removed in the invalidated method:
          @Override
          public void invalidated(Observable observable) {
              final Binding<?> binding = ref.get();
              if (binding == null) {
                  observable.removeListener(this);
              } else {
                  binding.invalidate();
              }
          }
      So in my example, it seems the invalidated method is never called and therefore the listeners are never removed. Is my example still legit?
      Do I miss something? Can I rely on GC here?
      Is it a bug?


      I noticed, that if I use a WeakChangeListener in my example, the memory won't increase.
      Although the WeakChangeListener class uses the same concept (removes the listener in the change method).
      http://hg.openjdk.java.net/openjfx/8/master/rt/file/16e4c07f8562/javafx-beans/src/javafx/beans/value/WeakChangeListener.java
        • 1. Re: WeakReferences in Custom Binding!?
          MartinSladecek
          You are right, changing the integerProperty should free the objects so they can be gc-ed. This was done intentionally, so that we don't have to keep weak references to all (weak) listeners, which would bring some performance penalties (and to some lesser extent, means more complicated code).
          • 2. Re: WeakReferences in Custom Binding!?
            csh
            Ok, good to know.

            But, obviously when I use a WeakChangeListener, I don't get any leaks, although I don't change the observable. So I assume the change method is never called, and the listener is never removed. But it seems it is removed.

            Can you explain?
            import javafx.application.Application;
            import javafx.beans.property.IntegerProperty;
            import javafx.beans.property.SimpleIntegerProperty;
            import javafx.beans.value.ChangeListener;
            import javafx.beans.value.ObservableValue;
            import javafx.beans.value.WeakChangeListener;
            import javafx.scene.Group;
            import javafx.scene.Scene;
            import javafx.scene.control.Label;
            import javafx.stage.Stage;
            
            public class TestApp2 extends Application {
                public static void main(String[] args) {
                    Application.launch(args);
                }
            
                private IntegerProperty integerProperty = new SimpleIntegerProperty();
            
                @Override
                public void start(Stage stage) throws Exception {
            
                    for (int i = 0; i < 2000000; i++) {
                        final Label label = new Label();
                        ChangeListener<Number> changeListener = new ChangeListener<Number>() {
                            @Override
                            public void changed(ObservableValue<? extends Number> observableValue, Number number, Number number1) {
                                label.setText(Integer.toString(integerProperty.get()));
                            }
                        };
                        integerProperty.addListener(new WeakChangeListener<Number>(changeListener));
            
                    }
            
                    System.gc();
                    System.out.println((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 + " KB");
            
                    stage.setScene(new Scene(new Group(), 400, 400));
                    stage.show();
                }
            }
            • 3. Re: WeakReferences in Custom Binding!?
              csh
              Can anybody clarify my last question, please?

              I really like to know, why appearently I don't get leaks when I use WeakChangeListener, but I do get leaks when I use custom binding, although both use the same approach to remove the listeners from the observable. (namely when the observable changes).