-
Bug
-
Resolution: Duplicate
-
P4
-
8, 9
-
Windows and Linux Java 1.8.0ea b117
-
Verified
I have noticed that when a Java callback is set on the JavaScript window object within a WebView, the view will not be gc'd unless the callback class is static and has no outside references. This situation is easier to demonstrate than fully describe.
Please run the provided test case and use the 2 top buttons to open and then close 4 windows (2 with each button). After ensuring that these stages are closed, click on the "Print References" button and watch the console.
The console will show that 2 of the stages were gc'd and 2 were not. The 2 that won't collect are the ones where the callback has a reference to the stage it is in. Since nothing else has a strong reference to that stage it should be able to be collected.
I have noticed that if you use JSObject.removeMember() for the callback then everything will get GC'd. If this is necessary because of the way the WebEngine operates then the docs should be updated to mention it's a necessity.
+++++++++++++++++++++ Test Class +++++++++++++++++++++++++++++++
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
import netscape.javascript.JSObject;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewTest extends Application {
@Override public void start(final Stage primaryStage) throws Exception {
primaryStage.centerOnScreen();
primaryStage.setHeight(350);
primaryStage.setWidth(500);
final Set<WeakReference<Object>> refs = new HashSet<>();
Button showButton = new Button("New View (will collect)...");
showButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
Stage newStage = createNewView(false);
newStage.show();
refs.add( new WeakReference<Object>(newStage) );
}
});
Button showButton2 = new Button("New View (won't collect)...");
showButton2.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
Stage newStage = createNewView(true);
newStage.show();
refs.add( new WeakReference<Object>(newStage) );
}
});
Button printButton = new Button("Print References");
printButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
Runtime.getRuntime().gc();
Set<WeakReference<Object>> collected = new HashSet<>();
for ( WeakReference<Object> ref: refs ) {
if ( ref.get() == null ) {
collected.add(ref);
}
}
refs.removeAll(collected);
System.out.println("*********************************************");
System.out.println(String.format("%d objects collected.",
collected.size()));
System.out.println(String.format("%d objects remain.",
refs.size()));
System.out.println("*********************************************");
}
});
VBox box = new VBox(20, new HBox(10, showButton, showButton2), printButton );
box.setMaxHeight(Region.USE_PREF_SIZE);
box.setMaxWidth(Region.USE_PREF_SIZE);
box.setAlignment(Pos.TOP_CENTER);
primaryStage.setScene(new Scene( new StackPane(box) ));
primaryStage.show();
}
private Stage createNewView(boolean backReference) {
Stage stage = new Stage();
WebView webview = new WebView();
WebEngine engine = webview.getEngine();
engine.load("http://google.com");
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("callback", new Callback(backReference ? stage: null));
stage.setScene(new Scene(webview, 800, 600));
return stage;
}
public static void main(String[] args) throws Exception {
launch(args);
}
// example javascript callback
public static class Callback {
private final Stage stageReference_;
public Callback( Stage stageReference ) {
stageReference_ = stageReference;
}
// example callback method
public void exit() {
if ( stageReference_ != null ) {
stageReference_.close();
}
}
}
}
Please run the provided test case and use the 2 top buttons to open and then close 4 windows (2 with each button). After ensuring that these stages are closed, click on the "Print References" button and watch the console.
The console will show that 2 of the stages were gc'd and 2 were not. The 2 that won't collect are the ones where the callback has a reference to the stage it is in. Since nothing else has a strong reference to that stage it should be able to be collected.
I have noticed that if you use JSObject.removeMember() for the callback then everything will get GC'd. If this is necessary because of the way the WebEngine operates then the docs should be updated to mention it's a necessity.
+++++++++++++++++++++ Test Class +++++++++++++++++++++++++++++++
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Set;
import netscape.javascript.JSObject;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewTest extends Application {
@Override public void start(final Stage primaryStage) throws Exception {
primaryStage.centerOnScreen();
primaryStage.setHeight(350);
primaryStage.setWidth(500);
final Set<WeakReference<Object>> refs = new HashSet<>();
Button showButton = new Button("New View (will collect)...");
showButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
Stage newStage = createNewView(false);
newStage.show();
refs.add( new WeakReference<Object>(newStage) );
}
});
Button showButton2 = new Button("New View (won't collect)...");
showButton2.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
Stage newStage = createNewView(true);
newStage.show();
refs.add( new WeakReference<Object>(newStage) );
}
});
Button printButton = new Button("Print References");
printButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
Runtime.getRuntime().gc();
Set<WeakReference<Object>> collected = new HashSet<>();
for ( WeakReference<Object> ref: refs ) {
if ( ref.get() == null ) {
collected.add(ref);
}
}
refs.removeAll(collected);
System.out.println("*********************************************");
System.out.println(String.format("%d objects collected.",
collected.size()));
System.out.println(String.format("%d objects remain.",
refs.size()));
System.out.println("*********************************************");
}
});
VBox box = new VBox(20, new HBox(10, showButton, showButton2), printButton );
box.setMaxHeight(Region.USE_PREF_SIZE);
box.setMaxWidth(Region.USE_PREF_SIZE);
box.setAlignment(Pos.TOP_CENTER);
primaryStage.setScene(new Scene( new StackPane(box) ));
primaryStage.show();
}
private Stage createNewView(boolean backReference) {
Stage stage = new Stage();
WebView webview = new WebView();
WebEngine engine = webview.getEngine();
engine.load("http://google.com");
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("callback", new Callback(backReference ? stage: null));
stage.setScene(new Scene(webview, 800, 600));
return stage;
}
public static void main(String[] args) throws Exception {
launch(args);
}
// example javascript callback
public static class Callback {
private final Stage stageReference_;
public Callback( Stage stageReference ) {
stageReference_ = stageReference;
}
// example callback method
public void exit() {
if ( stageReference_ != null ) {
stageReference_.close();
}
}
}
}
- duplicates
-
JDK-8089681 WebView leaks memory when containing object acts as javascript callback handler
- Resolved
- relates to
-
JDK-8171401 Release Note: Fixed memory leak when Java objects are passed into JavaScript
- Closed
-
JDK-8149707 WebView leaks memory when containing object acts as javascript callback handler
- Closed