Details
-
Bug
-
Resolution: Fixed
-
P2
-
8u40, 8u60
-
Windows XP(did the 1.8u40 GA test)
OS X 10.7.5 (did the 1.8u60 b10 EA test)
-
Not verified
Description
I've discovered what I believe is a memory leak in JavaFX (1.8u40 GA and 1.8u60 b10 EA) BarChart triggered by replacing all the data values in the series. This is exasperated by our application which does this several times a second.
Using jvisualvm shows an uncontrolled growth in the number of javafx.scene.layout.StackPane instances which eventually results in an OutOfMemoryError. Styled StackPane nodes are used internally by BarChart for the bars.
I've tried a different strategies to update the list. All exhibit the same issue.
// 1
series.getData().clear();
series.getData().addAll(list);
// 2
series.getData().setAll(list);
// 3
series.setData(list)
Interestingly the example in the Oracle BarChart tutorial updates values by adding all the bars/points first, and then mutating them using XYChart.Data.setYValue(). This could work, but is not convenient for us as the number of data points can vary dynamically.
Specific questions
Is there anyway to avoid this issue, other than a extra logic using the setYValue() approach above.
Have I stumbled across an actual memory leak in JavaFX? or just an artefact of my misuse of the API? Surely freeing internal nodes when data is updated is JavaFX responsibility
Example
public class ChartUpdate extends Application {
private int clock;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis(0, 100, 10);
yAxis.setAutoRanging(false);
BarChart<String, Number> graph = new BarChart<>(xAxis, yAxis);
graph.setAnimated(false);
Series<String, Number> series = new Series<>();
graph.getData().add(series);
stage.setScene(new Scene(graph));
stage.show();
Timeline timeLine = new Timeline();
timeLine.getKeyFrames().add(
new KeyFrame(Duration.millis(500),
(e) -> {
ObservableList<Data<String, Number>> list = FXCollections.observableArrayList();
for (int i = 0; i < 100; i++) {
list.add(new Data<>(String.valueOf(i), (clock + i) % 100));
}
series.setData(list);
clock++;
}));
timeLine.setCycleCount(Animation.INDEFINITE);
timeLine.play();
}
}
Using jvisualvm shows an uncontrolled growth in the number of javafx.scene.layout.StackPane instances which eventually results in an OutOfMemoryError. Styled StackPane nodes are used internally by BarChart for the bars.
I've tried a different strategies to update the list. All exhibit the same issue.
// 1
series.getData().clear();
series.getData().addAll(list);
// 2
series.getData().setAll(list);
// 3
series.setData(list)
Interestingly the example in the Oracle BarChart tutorial updates values by adding all the bars/points first, and then mutating them using XYChart.Data.setYValue(). This could work, but is not convenient for us as the number of data points can vary dynamically.
Specific questions
Is there anyway to avoid this issue, other than a extra logic using the setYValue() approach above.
Have I stumbled across an actual memory leak in JavaFX? or just an artefact of my misuse of the API? Surely freeing internal nodes when data is updated is JavaFX responsibility
Example
public class ChartUpdate extends Application {
private int clock;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis(0, 100, 10);
yAxis.setAutoRanging(false);
BarChart<String, Number> graph = new BarChart<>(xAxis, yAxis);
graph.setAnimated(false);
Series<String, Number> series = new Series<>();
graph.getData().add(series);
stage.setScene(new Scene(graph));
stage.show();
Timeline timeLine = new Timeline();
timeLine.getKeyFrames().add(
new KeyFrame(Duration.millis(500),
(e) -> {
ObservableList<Data<String, Number>> list = FXCollections.observableArrayList();
for (int i = 0; i < 100; i++) {
list.add(new Data<>(String.valueOf(i), (clock + i) % 100));
}
series.setData(list);
clock++;
}));
timeLine.setCycleCount(Animation.INDEFINITE);
timeLine.play();
}
}