ADDITIONAL SYSTEM INFORMATION :
Windows 11, D3D Rendering Pipeliine
A DESCRIPTION OF THE PROBLEM :
When nodes with setCache(true) are used in the scene graph, the ImagePool will create a new Image via renderer.createCompatibleImage(w, h) and add it (with a SoftReferene) to its 'locked' list. This list can grow over time. But even if the original nodes are not available anymore and are GC'ed, the elements stay in that locked list.
This can lead to a severe performance degredation, showing up over time in form of reduced frames per second, as this list is beeing iterated over very often.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I put some debug outputs in the ImagePool class:
- in checkOut around line 165:
// get rid of expired entries from locked list
System.out.println("check OUT - locked size: " + this.locked.size());
entries = this.locked.iterator();
- in checkIn around line 200:
System.out.println("check IN - locked size: " + this.locked.size());
Iterator<SoftReference<PoolFilterable>> entries = this.locked.iterator();
Run Demo Application from below (Test Case Code).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The output showing at some point in time that the locked list has only a few elements in it.
ACTUAL -
The element size of the locked list grows indefinite. The system output shows:
check OUT - locked size: 0
check OUT - locked size: 1
check OUT - locked size: 2
check OUT - locked size: 3
check OUT - locked size: 4
cleared
check OUT - locked size: 5
check OUT - locked size: 6
check OUT - locked size: 7
check OUT - locked size: 8
check OUT - locked size: 9
cleared
check OUT - locked size: 10
check OUT - locked size: 11
.......
---------- BEGIN SOURCE ----------
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
final var root = new BorderPane();
final var scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
final var scheduledExecService = Executors.newScheduledThreadPool(1);
scheduledExecService.scheduleAtFixedRate(() -> {
final var region = new Region();
region.setStyle("-fx-background-color: red;");
region.setCache(true);
Platform.runLater(() -> root.setCenter(region));
}, 1L, 1L, TimeUnit.SECONDS);
scheduledExecService.scheduleAtFixedRate(() -> {
Platform.runLater(() -> {
root.getChildren().clear();
System.gc();
System.out.println("cleared");
});
}, 5500L, 5000L, TimeUnit.MILLISECONDS);
}
public static void main(String[] args) {
launch(args);
}
}
---------- END SOURCE ----------
Windows 11, D3D Rendering Pipeliine
A DESCRIPTION OF THE PROBLEM :
When nodes with setCache(true) are used in the scene graph, the ImagePool will create a new Image via renderer.createCompatibleImage(w, h) and add it (with a SoftReferene) to its 'locked' list. This list can grow over time. But even if the original nodes are not available anymore and are GC'ed, the elements stay in that locked list.
This can lead to a severe performance degredation, showing up over time in form of reduced frames per second, as this list is beeing iterated over very often.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I put some debug outputs in the ImagePool class:
- in checkOut around line 165:
// get rid of expired entries from locked list
System.out.println("check OUT - locked size: " + this.locked.size());
entries = this.locked.iterator();
- in checkIn around line 200:
System.out.println("check IN - locked size: " + this.locked.size());
Iterator<SoftReference<PoolFilterable>> entries = this.locked.iterator();
Run Demo Application from below (Test Case Code).
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The output showing at some point in time that the locked list has only a few elements in it.
ACTUAL -
The element size of the locked list grows indefinite. The system output shows:
check OUT - locked size: 0
check OUT - locked size: 1
check OUT - locked size: 2
check OUT - locked size: 3
check OUT - locked size: 4
cleared
check OUT - locked size: 5
check OUT - locked size: 6
check OUT - locked size: 7
check OUT - locked size: 8
check OUT - locked size: 9
cleared
check OUT - locked size: 10
check OUT - locked size: 11
.......
---------- BEGIN SOURCE ----------
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
final var root = new BorderPane();
final var scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
final var scheduledExecService = Executors.newScheduledThreadPool(1);
scheduledExecService.scheduleAtFixedRate(() -> {
final var region = new Region();
region.setStyle("-fx-background-color: red;");
region.setCache(true);
Platform.runLater(() -> root.setCenter(region));
}, 1L, 1L, TimeUnit.SECONDS);
scheduledExecService.scheduleAtFixedRate(() -> {
Platform.runLater(() -> {
root.getChildren().clear();
System.gc();
System.out.println("cleared");
});
}, 5500L, 5000L, TimeUnit.MILLISECONDS);
}
public static void main(String[] args) {
launch(args);
}
}
---------- END SOURCE ----------