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