rajat agarwal rajat agarwal - 4 months ago 121
Java Question

How to release the consumed memory by the webEngine in javaFX browser

I am using this SimpleSwingBrowser. It consumes too much memory and doesn't release that memory.
I continuously see the consumed memory in task manager and it increases to 500MB in visiting of only three sites and it continuously increasing.

So i want to release all the memory when the browser switch to the next page. Means all the memory consumed by the previous page must be dispose or clear.

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.embed.swing.JFXPanel;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebEvent;
import javafx.scene.web.WebView;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.net.MalformedURLException;
import java.net.URL;

import static javafx.concurrent.Worker.State.FAILED;

public class SimpleSwingBrowser extends JFrame {

private final JFXPanel jfxPanel = new JFXPanel();
private WebEngine engine;

private final JPanel panel = new JPanel(new BorderLayout());
private final JLabel lblStatus = new JLabel();


private final JButton btnGo = new JButton("Go");
private final JTextField txtURL = new JTextField();
private final JProgressBar progressBar = new JProgressBar();

public SimpleSwingBrowser() {
super();
initComponents();
}


private void initComponents() {
createScene();

ActionListener al = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
loadURL(txtURL.getText());
}
};

btnGo.addActionListener(al);
txtURL.addActionListener(al);

progressBar.setPreferredSize(new Dimension(150, 18));
progressBar.setStringPainted(true);

JPanel topBar = new JPanel(new BorderLayout(5, 0));
topBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5));
topBar.add(txtURL, BorderLayout.CENTER);
topBar.add(btnGo, BorderLayout.EAST);

JPanel statusBar = new JPanel(new BorderLayout(5, 0));
statusBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5));
statusBar.add(lblStatus, BorderLayout.CENTER);
statusBar.add(progressBar, BorderLayout.EAST);

panel.add(topBar, BorderLayout.NORTH);
panel.add(jfxPanel, BorderLayout.CENTER);
panel.add(statusBar, BorderLayout.SOUTH);

getContentPane().add(panel);

setPreferredSize(new Dimension(1024, 600));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();

}

private void createScene() {

Platform.runLater(new Runnable() {
@Override
public void run() {

WebView view = new WebView();
engine = view.getEngine();

engine.titleProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, final String newValue) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
SimpleSwingBrowser.this.setTitle(newValue);
}
});
}
});

engine.setOnStatusChanged(new EventHandler<WebEvent<String>>() {
@Override
public void handle(final WebEvent<String> event) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
lblStatus.setText(event.getData());
}
});
}
});

engine.locationProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> ov, String oldValue, final String newValue) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
txtURL.setText(newValue);
}
});
}
});

engine.getLoadWorker().workDoneProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, final Number newValue) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
progressBar.setValue(newValue.intValue());
}
});
}
});

engine.getLoadWorker()
.exceptionProperty()
.addListener(new ChangeListener<Throwable>() {

public void changed(ObservableValue<? extends Throwable> o, Throwable old, final Throwable value) {
if (engine.getLoadWorker().getState() == FAILED) {
SwingUtilities.invokeLater(new Runnable() {
@Override public void run() {
JOptionPane.showMessageDialog(
panel,
(value != null) ?
engine.getLocation() + "\n" + value.getMessage() :
engine.getLocation() + "\nUnexpected error.",
"Loading error...",
JOptionPane.ERROR_MESSAGE);
}
});
}
}
});

jfxPanel.setScene(new Scene(view));
}
});
}

public void loadURL(final String url) {
Platform.runLater(new Runnable() {
@Override
public void run() {
String tmp = toURL(url);

if (tmp == null) {
tmp = toURL("http://" + url);
}

engine.load(tmp);
}
});
}

private static String toURL(String str) {
try {
return new URL(str).toExternalForm();
} catch (MalformedURLException exception) {
return null;
}
}



public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {

public void run() {
SimpleSwingBrowser browser = new SimpleSwingBrowser();
browser.setVisible(true);
browser.loadURL("http://oracle.com");
}
});
}
}

Answer

Thats an actuall 'problem-kind of restriction' with Java.Even if you call System.gc() which is not recommended,it is not soore that the garbage collector will release memory.One way is to set the Object to null,so it is marked to be cleared.

Even if you had a ton of elements and you delete them ,you may see that the ram consumption is the same.Sometimes Java uses by default more memory so your app will not lag.

You have wait carbage collector do the work and creating new objects with strategy.If an Object has stayed too long("mention that Java8 is different permgen not exists") you have a possibility that it will never been collected from gc.Sometimes it might take 1 minute to release for example 200 MiB of memory.

Some elements like WebViewer consumes a lot of RAM.You can see the same for Firefox but it clears it after a while.

Comments