long standing issue(有人称之为 - 可以说是 - feature :)是所有fx绑定所安装的所有侦听器的弱点。因此,如果不对链中的每个环节进行强有力的引用,我们就无法构建属性的“链”。
这种链接的特定类型是JavaBeanProperty:其目的是使javabean属性适应fx属性。通常情况下,没有人对适配器感兴趣,因此它的使用会像
那样private Parent createContentBean() {
...
// local ref only
Property property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
.. wondering为什么标签没有更新。将属性更改为强引用将按预期工作(让我感到困惑的是谁负责提供假人,但这是另一个问题):
Property property;
private Parent createContentBean() {
...
// instantiate the field
property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
长介绍,但几乎在那里:jdk8以某种方式改变了实现,以便第一种方法现在正在工作,不再需要保持对JavaBeanProperty的强引用。另一方面,“链接链接”的自定义实现仍然需要强有力的参考。
问题:
一个完整的例子:
public class BeanAdapterExample extends Application {
private Counter counter;
public BeanAdapterExample() {
this.counter = new Counter();
}
Property property;
private Parent createContentBean() {
VBox content = new VBox();
Label label = new Label();
// strong ref
property = createJavaBeanProperty();
// local property
Property property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
Slider slider = new Slider();
slider.valueProperty().bindBidirectional(property);
Button button = new Button("increase");
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent paramT) {
counter.increase();
}
});
content.getChildren().add(label);
content.getChildren().add(slider);
content.getChildren().add(button);
return content;
}
protected JavaBeanDoubleProperty createJavaBeanProperty(){
try {
return JavaBeanDoublePropertyBuilder.create()
.bean(counter).name("count").build();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(createContentBean());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
public static class Counter {
private double count;
public Counter() {
this(0);
}
public Counter(double count) {
this.count = count;
}
/**
* Increases the counter by 1.
*/
public void increase() {
setCount(getCount()+ 1.);
}
/**
* @return the count
*/
public double getCount() {
return count;
}
/**
* @param count the count to set
*/
public void setCount(double count) {
double old = getCount();
this.count = count;
firePropertyChange("count", old, getCount());
}
PropertyChangeSupport support = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
protected void firePropertyChange(String name, Object oldValue,
Object newValue) {
support.firePropertyChange(name, oldValue, newValue);
}
}
}
BTW:添加了Swing标签,因为调整核心bean将是迁移中的常见任务
答案 0 :(得分:2)
提醒我issue去年我偶然发现 - 绑定不会创建强引用,因此如果属性是方法本地字段,属性将被垃圾收集。
答案 1 :(得分:1)
Gropingly试图回答我自己的部分回答:
属性的jdk8构造函数:
JavaBeanDoubleProperty(PropertyDescriptor descriptor, Object bean) {
this.descriptor = descriptor;
this.listener = descriptor.new Listener<Number>(bean, this);
descriptor.addListener(listener);
Cleaner.create(this, new Runnable() {
@Override
public void run() {
JavaBeanDoubleProperty.this.descriptor.removeListener(listener);
}
});
}
反过来说:如果我将这样的引用添加到任意自定义属性,那么它就像javabeanProperty一样卡在内存中:
protected SimpleDoubleProperty createPhantomedProperty(final boolean phantomed) {
SimpleDoubleProperty adapter = new SimpleDoubleProperty(){
{
// prevents the property from being garbage collected
// must be done here in the constructor
// otherwise reclaimed immediately
if (phantomed) {
Cleaner.create(this, new Runnable() {
@Override
public void run() {
// empty, could do what here?
LOG.info("runnable in cleaner");
}
});
}
}
};
return adapter;
}
要重现非集合,请将下面的代码片段添加到问题中的示例代码中,在jdk7 / 8中运行并使用您喜欢的工具(使用VisualVM)进行监视:在运行时,单击“创建”以创建100k自由飞行的JavaBeanProperties。在jdk7中,它们甚至从未出现在内存采样器中。在jdk8中,它们被创建(懒散!所以你可以减少数量)并建立起来。强制垃圾收集无效,即使在将它们绑定的底层bean归零后也是如此。
Button create100K = new Button("create 100k properties");
create100K.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent paramT) {
Property propertyFX;
/// can't measure any effect
for (int i = 0; i < 100000; i++) {
propertyFX = createCountProperty();
}
LOG.info("created 100k adapters");
}
});
Button releaseCounter = new Button("release counter");
releaseCounter.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent paramT) {
counter = null;
}
});
仅供参考:创建一个issue for the potential memory leak - 已标记为已修复,速度很快!不幸的是,修复版本是8u20,不知道该怎么办。我想到的唯一事情是c&amp; p所有JavaBeanXXProperty / Builders并添加修复。以安全限制环境中的严重警告和不可用为代价。此外,我们回到了jdk7的行为(本来是太幸运了,吃蛋糕还有它: - )