Duplicate :
|
|
Duplicate :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
FULL PRODUCT VERSION : java version "1.8.0_92" Java(TM) SE Runtime Environment (build 1.8.0_92-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode) ADDITIONAL OS VERSION INFORMATION : Microsoft Windows [Version 6.1.7601] A DESCRIPTION OF THE PROBLEM : We have a JavaFX application that passes some data between Java and JavaScript, we have noticed that this application's process memory was increasing and eventually crashing. After running a JVM memory profiler we couldn't identify the problem with Java, but we were able to pinpoint the problem to Webkit. It seems that there is a memory leak outside of JVM's memory space. There are 2 cases: one is passing data from Java to JavaScript and the second is from JavaScript to Java. For this bug, we will focus on the case from Java to JavaScript. We're passing the data as strings (JSON in our app) and it seems that overtime something isn't getting released. The test has 2 payloads: a java string and a char array. The java string tests leaks, the char array isn't. STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : Run MemoryLeak2.java. Click on "Start Simulation" button Watch the process' memory in the task manager. EXPECTED VERSUS ACTUAL BEHAVIOR : EXPECTED - The process' memory shouldn't increase. ACTUAL - The process' memory was increasing non stop. REPRODUCIBILITY : This bug can be reproduced always. ---------- 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 ---------- CUSTOMER SUBMITTED WORKAROUND : Use char array instead: cb.call("callback", chrBuffer); This will not cause a memory leak.
|