Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8117342

Subclassing SimpleObjectProperty results in many listeners causing eventual high CPU use

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P4 P4
    • 8
    • 8
    • javafx
    • JavaFX 8b103

      I've encountered a wierd problem with JavaFX 8b103 -- just creating a subclass of SimpleObjectProperty and using it in a Bindings.select causes tons of listeners to be created each time the property changes -- these listeners donot disappear and eventually cause high CPU use resulting in complete unresponsiveness of the application.

      In the code below you can see I create a tiny subclass of SimpleObjectProperty that only logs when get() gets called. This method gets called more and more often. When adding a break point, I can see that ExpressionHelper.addListener is being called more and more often for this binding, resulting in even more calls to get().

      Just run the code below, and observe that get() gets called more and more often. Even with an empty subclass (get() not overriden), the CPU use will become ridiculous after a short while (16-20 seconds).

      package hs.mediasystem;

      import javafx.application.Application;
      import javafx.application.Platform;
      import javafx.beans.binding.Bindings;
      import javafx.beans.binding.ObjectBinding;
      import javafx.beans.property.ObjectProperty;
      import javafx.beans.property.SimpleObjectProperty;
      import javafx.beans.value.ChangeListener;
      import javafx.beans.value.ObservableValue;
      import javafx.stage.Stage;

      public class BindingBug extends Application {
        public static class MyPlayer {
          private final BeanObjectProperty<String> subtitle = new BeanObjectProperty<>();

          public String getSubtitle() { return subtitle.get(); }
          public void setSubtitle(String subtitle) { this.subtitle.set(subtitle); }
          public ObjectProperty<String> subtitleProperty() { return subtitle; }
        }

        private static MyPlayer player;
        private static final ObjectProperty<MyPlayer> playerProp = new SimpleObjectProperty<>();

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

        @Override
        public void start(Stage stage) {
          player = new MyPlayer();

          playerProp.set(player);

          ObjectBinding<String> subtitle = Bindings.select(playerProp, "subtitle");

          subtitle.addListener(new ChangeListener<String>() {
            int count = 0;

            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String value) {
              System.out.println(">>> Showing Subtitle OSD " + ++count);
            }
          });

          new Thread() {
            int id = 2;

            @Override
            public void run() {
              for(;;) {
                try {
                  Thread.sleep(1000);

                  Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                      player.setSubtitle("Desc" + ++id);
                    }
                  });
                }
                catch(Exception e) {
                  throw new RuntimeException(e);
                }

              }
            }
          }.start();
        }

        public static final class BeanObjectProperty<T> extends SimpleObjectProperty<T> {
          @Override
          public T get() {
            System.out.println(">>> Calling get");
            return super.get();
          }
        }
      }

            msladecek Martin Sládeček
            jhendrikx John Hendrikx
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported: