---------- BEGIN SOURCE ----------
test.html
======
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<div style="position:relative">
<h2>Controls</h2>
<button onclick="simulateRealTimeUpdates()">Start simulation</button>
<button onclick="stopRealtime()">Stop simulation</button>
<button onclick="openNewWindow()">Open new window</button>
</div>
<script>
function simulateRealTimeUpdates() {
window.service.data({
callback: function(dataStr) {
dataStr = null;
}
});
}
function openNewWindow() {
window.open('grid.html');
}
function stopRealtime() {
window.service.cancelTimer();
}
</script>
</body>
</html>
MemoryLeak2Service.java
=====================
package memoryLeak2;
import javafx.application.Platform;
import netscape.javascript.JSObject;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
public class MemoryLeak2Service {
private final Timer timer = new java.util.Timer();
private final int size = 1000000;
private final String strBuffer;
//private final char[] chrBuffer;
public MemoryLeak2Service()
{
StringBuffer buffer = new StringBuffer(size);
for (int i = 0; i < size; i++){
buffer.append("A");
}
strBuffer = buffer.toString();
//chrBuffer = strBuffer.toCharArray();
}
public void data(JSObject cb) {
TimerTask tt = new TimerTask() {
public void run() {
Platform.runLater(new Runnable() {
@Override
public void run() {
// strBuffer - LEAKS, chrBuffer isn't leaking.
cb.call("callback", strBuffer);
//cb.call("callback", chrBuffer);
}
});
}
};
timer.schedule(tt, 1000, 100);
}
/**
* Cancel timer
*/
public void cancelTimer() {
timer.cancel();
timer.purge();
}
}
MemoryLeak2.java
===============
package memoryLeak2;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.web.PopupFeatures;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import netscape.javascript.JSObject;
public class MemoryLeak2 extends Application {
public static final int WIDTH = 1000;
public static final int HEIGHT = 600;
@Override
public void start(Stage stage) {
create(stage);
}
public static void create(Stage stage) {
// create the scene
stage.setTitle("Original Web View");
Scene scene = new Scene(new Browser(), WIDTH, HEIGHT, Color.web("#666970"));
stage.setScene(scene);
stage.show();
stage.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
//Exit the application on first window close
System.exit(0);
}
});
}
public static void main(String[] args){
launch(args);
}
}
class Browser extends Region {
/**
* html file to load
*/
private String fileToLoad = "test.html";
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
private MemoryLeak2Service myService;
public Browser() {
//apply the styles
getStyleClass().add("browser");
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
@Override
public void changed(ObservableValue<? extends State> stateParam, State oldState, State newState) {
switch (newState) {
case READY:
break;
case SCHEDULED:
break;
case RUNNING:
break;
case CANCELLED:
break;
case SUCCEEDED:
if(!"about:blank".equals(webEngine.getLocation())) {
final JSObject window = (JSObject) webEngine.executeScript("window");
myService = new MemoryLeak2Service();
//set service as widow member
window.setMember("service", myService);
window.call("init");
}
break;
default:
break;
}
}
});
//alert to display some javascript logs
webEngine.setOnAlert(new EventHandler<WebEvent<String>>() {
@Override
public void handle(WebEvent<String> webEvent) {
System.out.println(webEvent.getData());
}
});
// load the web page
webEngine.load(Browser.class.getResource(fileToLoad).toExternalForm());
getChildren().add(browser);
webEngine.setCreatePopupHandler(new Callback<PopupFeatures, WebEngine>() {
@Override
public WebEngine call(PopupFeatures param) {
Stage stg = new Stage();
stg.setTitle("Popup View");
final Browser root = new Browser();
Scene scene = new Scene(root, 800, 800, Color.web("#666970"));
stg.setScene(scene);
stg.show();
stg.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
if(root != null) {
//On close cancel te timer
root.getMyService().cancelTimer();
//load null to free some memory
root.getWebEngine().load(null);
}
}
});
return root.getWebEngine();
}
});
}
public WebEngine getWebEngine() {
return webEngine;
}
public MemoryLeak2Service getMyService() {
return myService;
}
@Override protected void layoutChildren() {
double w = getWidth();
double h = getHeight();
layoutInArea(browser,0,0,w,h,0, HPos.CENTER, VPos.CENTER);
}
@Override protected double computePrefWidth(double height) {
return MemoryLeak2.HEIGHT;
}
@Override protected double computePrefHeight(double width) {
return MemoryLeak2.WIDTH;
}
}
---------- END SOURCE ----------
When we try to close the pop up window, below NPE observed:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at Browser$3$1.handle(MemoryLeak2.java:140)
at Browser$3$1.handle(MemoryLeak2.java:134)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at com.sun.javafx.stage.WindowPeerListener.closing(WindowPeerListener.java:93)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:146)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$4(GlassWindowEventHandler.java:175)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:173)
at com.sun.glass.ui.Window.handleWindowEvent(Window.java:1313)
at com.sun.glass.ui.Window.notifyClose(Window.java:1213)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:189)
at java.lang.Thread.run(Thread.java:804)
test.html
======
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
<div style="position:relative">
<h2>Controls</h2>
<button onclick="simulateRealTimeUpdates()">Start simulation</button>
<button onclick="stopRealtime()">Stop simulation</button>
<button onclick="openNewWindow()">Open new window</button>
</div>
<script>
function simulateRealTimeUpdates() {
window.service.data({
callback: function(dataStr) {
dataStr = null;
}
});
}
function openNewWindow() {
window.open('grid.html');
}
function stopRealtime() {
window.service.cancelTimer();
}
</script>
</body>
</html>
MemoryLeak2Service.java
=====================
package memoryLeak2;
import javafx.application.Platform;
import netscape.javascript.JSObject;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
public class MemoryLeak2Service {
private final Timer timer = new java.util.Timer();
private final int size = 1000000;
private final String strBuffer;
//private final char[] chrBuffer;
public MemoryLeak2Service()
{
StringBuffer buffer = new StringBuffer(size);
for (int i = 0; i < size; i++){
buffer.append("A");
}
strBuffer = buffer.toString();
//chrBuffer = strBuffer.toCharArray();
}
public void data(JSObject cb) {
TimerTask tt = new TimerTask() {
public void run() {
Platform.runLater(new Runnable() {
@Override
public void run() {
// strBuffer - LEAKS, chrBuffer isn't leaking.
cb.call("callback", strBuffer);
//cb.call("callback", chrBuffer);
}
});
}
};
timer.schedule(tt, 1000, 100);
}
/**
* Cancel timer
*/
public void cancelTimer() {
timer.cancel();
timer.purge();
}
}
MemoryLeak2.java
===============
package memoryLeak2;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.web.PopupFeatures;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
import netscape.javascript.JSObject;
public class MemoryLeak2 extends Application {
public static final int WIDTH = 1000;
public static final int HEIGHT = 600;
@Override
public void start(Stage stage) {
create(stage);
}
public static void create(Stage stage) {
// create the scene
stage.setTitle("Original Web View");
Scene scene = new Scene(new Browser(), WIDTH, HEIGHT, Color.web("#666970"));
stage.setScene(scene);
stage.show();
stage.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
//Exit the application on first window close
System.exit(0);
}
});
}
public static void main(String[] args){
launch(args);
}
}
class Browser extends Region {
/**
* html file to load
*/
private String fileToLoad = "test.html";
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
private MemoryLeak2Service myService;
public Browser() {
//apply the styles
getStyleClass().add("browser");
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
@Override
public void changed(ObservableValue<? extends State> stateParam, State oldState, State newState) {
switch (newState) {
case READY:
break;
case SCHEDULED:
break;
case RUNNING:
break;
case CANCELLED:
break;
case SUCCEEDED:
if(!"about:blank".equals(webEngine.getLocation())) {
final JSObject window = (JSObject) webEngine.executeScript("window");
myService = new MemoryLeak2Service();
//set service as widow member
window.setMember("service", myService);
window.call("init");
}
break;
default:
break;
}
}
});
//alert to display some javascript logs
webEngine.setOnAlert(new EventHandler<WebEvent<String>>() {
@Override
public void handle(WebEvent<String> webEvent) {
System.out.println(webEvent.getData());
}
});
// load the web page
webEngine.load(Browser.class.getResource(fileToLoad).toExternalForm());
getChildren().add(browser);
webEngine.setCreatePopupHandler(new Callback<PopupFeatures, WebEngine>() {
@Override
public WebEngine call(PopupFeatures param) {
Stage stg = new Stage();
stg.setTitle("Popup View");
final Browser root = new Browser();
Scene scene = new Scene(root, 800, 800, Color.web("#666970"));
stg.setScene(scene);
stg.show();
stg.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
if(root != null) {
//On close cancel te timer
root.getMyService().cancelTimer();
//load null to free some memory
root.getWebEngine().load(null);
}
}
});
return root.getWebEngine();
}
});
}
public WebEngine getWebEngine() {
return webEngine;
}
public MemoryLeak2Service getMyService() {
return myService;
}
@Override protected void layoutChildren() {
double w = getWidth();
double h = getHeight();
layoutInArea(browser,0,0,w,h,0, HPos.CENTER, VPos.CENTER);
}
@Override protected double computePrefWidth(double height) {
return MemoryLeak2.HEIGHT;
}
@Override protected double computePrefHeight(double width) {
return MemoryLeak2.WIDTH;
}
}
---------- END SOURCE ----------
When we try to close the pop up window, below NPE observed:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at Browser$3$1.handle(MemoryLeak2.java:140)
at Browser$3$1.handle(MemoryLeak2.java:134)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at com.sun.javafx.stage.WindowPeerListener.closing(WindowPeerListener.java:93)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:146)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.run(GlassWindowEventHandler.java:40)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.lambda$handleWindowEvent$4(GlassWindowEventHandler.java:175)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassWindowEventHandler.handleWindowEvent(GlassWindowEventHandler.java:173)
at com.sun.glass.ui.Window.handleWindowEvent(Window.java:1313)
at com.sun.glass.ui.Window.notifyClose(Window.java:1213)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:189)
at java.lang.Thread.run(Thread.java:804)
- relates to
-
JDK-8161053 Passing objects between JavaScript (JavaFX / WebKit) and Java causes a memory leak
-
- Resolved
-