-
Bug
-
Resolution: Won't Fix
-
P3
-
8u40, 9
-
Windows x64
Isssue Description:
It seems there's some rather big memory leaks in simply updating a label with new text, where resources are not released. We noticed a never-ending increase of heap size simply showing a clock in our application and then narrowed it down and created a very simple test case that shows the same behavior when attached to a Profiler.
How to test:
Run the test code, attach a Java Profiler (I used JProfiler and VisualVM, but any should be fine). When ready press the "Start" button in the test app. Let it run for a few 100 iterations then press stop. Hit "mark current" (or however your profiler helps you see deltas between runs), then press Start, Wait a bit, press Stop and then do a GC in the profiler. Do this a few times, and you'll see that a quite a few places are not letting go of resources (even if you wait a good long time after finishing the test to GC). The longer you wait when running the test, the higher the deltas. Hot spots in particular are:
com.sun.javafx.text.TextRun[]
com.sun.javafx.geom.Point2D
com.sun.javafx.text.LayoutCache
com.sun.javafx.text.TextLine
com.sun.javafx.text.TextLine[]
com.sun.javafx.text.TextRun
There also seems to be a never ending increase of "PseudoClassStates" that never get released, we noticed this across the board in any JFX control. Perhaps CSS plays a part.
I'll attach a screenshot of my debugger view after posting the initial bug into Jira.
Test Code:
---
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextMemoryLeak extends Application {
private static final String _StrHello = "Hello ";
private static int _iterations;
private static Label _target;
private static Task<Void> _everySecond;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
VBox content = new VBox(10);
_target = new Label("Testing");
Button start = new Button("Start");
start.setOnAction(e -> {
start();
});
Button stop = new Button("Stop");
stop.setOnAction(e -> {
_everySecond.cancel();
});
content.setPadding(new Insets(20));
content.getChildren().addAll(_target, start, stop);
Scene scene = new Scene(content, 600, 500);
primaryStage.setTitle("Text Memory Leak");
primaryStage.setScene(scene);
primaryStage.show();
}
private void start() {
_everySecond = new Task<Void>() {
@Override
protected Void call() throws Exception {
while (true) {
Thread.sleep(10);
Platform.runLater(() -> {
_target.setText(String.format("%s %s", _StrHello, _iterations));
_iterations++;
});
if (isCancelled()) {
return null;
}
}
}
};
new Thread(_everySecond).start();
}
}
It seems there's some rather big memory leaks in simply updating a label with new text, where resources are not released. We noticed a never-ending increase of heap size simply showing a clock in our application and then narrowed it down and created a very simple test case that shows the same behavior when attached to a Profiler.
How to test:
Run the test code, attach a Java Profiler (I used JProfiler and VisualVM, but any should be fine). When ready press the "Start" button in the test app. Let it run for a few 100 iterations then press stop. Hit "mark current" (or however your profiler helps you see deltas between runs), then press Start, Wait a bit, press Stop and then do a GC in the profiler. Do this a few times, and you'll see that a quite a few places are not letting go of resources (even if you wait a good long time after finishing the test to GC). The longer you wait when running the test, the higher the deltas. Hot spots in particular are:
com.sun.javafx.text.TextRun[]
com.sun.javafx.geom.Point2D
com.sun.javafx.text.LayoutCache
com.sun.javafx.text.TextLine
com.sun.javafx.text.TextLine[]
com.sun.javafx.text.TextRun
There also seems to be a never ending increase of "PseudoClassStates" that never get released, we noticed this across the board in any JFX control. Perhaps CSS plays a part.
I'll attach a screenshot of my debugger view after posting the initial bug into Jira.
Test Code:
---
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TextMemoryLeak extends Application {
private static final String _StrHello = "Hello ";
private static int _iterations;
private static Label _target;
private static Task<Void> _everySecond;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
VBox content = new VBox(10);
_target = new Label("Testing");
Button start = new Button("Start");
start.setOnAction(e -> {
start();
});
Button stop = new Button("Stop");
stop.setOnAction(e -> {
_everySecond.cancel();
});
content.setPadding(new Insets(20));
content.getChildren().addAll(_target, start, stop);
Scene scene = new Scene(content, 600, 500);
primaryStage.setTitle("Text Memory Leak");
primaryStage.setScene(scene);
primaryStage.show();
}
private void start() {
_everySecond = new Task<Void>() {
@Override
protected Void call() throws Exception {
while (true) {
Thread.sleep(10);
Platform.runLater(() -> {
_target.setText(String.format("%s %s", _StrHello, _iterations));
_iterations++;
});
if (isCancelled()) {
return null;
}
}
}
};
new Thread(_everySecond).start();
}
}