Hi,
a part of our application has a visual "monitor" for real-time events. It is a simple AreaChart which continuously displays new events and drops old ones (run my simple sample application below to understand what I mean). It's running fine, but we are having trouble with memory leaks, because our app crashes due to OutOfMemoryError after some time.
A heap analysis shows especially two classes which have thousands of instances (and keep growing):
1. java.lang.ref.WeakReference
2. javafx.beans.property.BooleanPropertyBase$Listener
VisualVM tells me, that they are referenced by the static:
Platform.accessibilityActiveProperty()
I guess something inside JavaFX is continuously adding (weak) listeners to this property, which can't be GCed.
VisualVM Screenshots are attached, which show the GC root and the differences between the heap after running the below code for about 30 minutes. (it uses an update interval of 100ms to emphasize the effect, so that you see it faster)
(using Java 8u40)
If you have any suggestion or advice, please let me know, thanks.
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedAreaChart;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TestApp4 extends Application {
private static final int COUNT = 50;
private final ObservableList<XYChart.Data<Number, Number>> data = FXCollections.observableArrayList();
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.setPadding(new Insets(50, 50, 50, 50));
NumberAxis timeAxis = new NumberAxis();
timeAxis.setMinorTickVisible(false);
timeAxis.setTickLabelsVisible(false);
timeAxis.setLabel("t");
NumberAxis memoryAxis = new NumberAxis();
memoryAxis.setLabel("X");
StackedAreaChart<Number, Number> stackedAreaChart = new StackedAreaChart<>(timeAxis, memoryAxis);
final ObservableList<XYChart.Series<Number, Number>> data = FXCollections.observableArrayList();
XYChart.Series<Number, Number> series = new XYChart.Series<>(this.data);
data.add(series);
updateSeries();
stackedAreaChart.setData(data);
stackedAreaChart.setAnimated(false);
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100), actionEvent -> updateSeries()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
VBox.setVgrow(stackedAreaChart, Priority.ALWAYS);
root.getChildren().add(stackedAreaChart);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private void updateSeries() {
if (data.size() > COUNT) {
data.remove(0);
}
for (XYChart.Data<Number, Number> data : this.data) {
data.setXValue(data.getXValue().intValue() - 1);
}
data.add(new XYChart.Data<>(COUNT, Math.random()));
}
}