我遇到了一个困扰我数月的问题:我一直遇到OOM异常(堆空间),在检查堆转储时,我发现了数百万个对象实例,这些对象实例我从未分配过,但很可能是在底层库中分配的。经过大量的努力和汗水,我成功地定位了生成内存泄漏的代码,并编写了一个最小、完整和可验证的代码示例来说明:
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.scene.web.WebEngine;
import javafx.stage.Stage;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class MVC extends Application implements ChangeListener<Worker.State>{
private final WebEngine engine = new WebEngine();
private final String url = "https://biblio.ugent.be/publication?sort=publicationstatus.desc&sort=year.desc&limit=250&start=197000";
private final XPath x = XPathFactory.newInstance().newXPath();
@Override
public void start(Stage primaryStage) throws Exception {
System.setProperty("jsse.enableSNIExtension", "false");
engine.getLoadWorker().stateProperty().addListener(this);
engine.load(url);
}
public static void main(String[] args) {
launch(args);
}
private NodeList eval(Node context, String xpath) throws XPathExpressionException{
return (NodeList)x.evaluate(xpath, context, XPathConstants.NODESET);
}
@Override
public void changed(ObservableValue<? extends Worker.State> observable, Worker.State oldValue, Worker.State newValue) {
if (newValue==Worker.State.SUCCEEDED) {
try {
while(true){
NodeList eval = eval(engine.getDocument(), "//span[@class='title']");
int s = eval.getLength();
}
} catch (XPathExpressionException ex) {
Logger.getLogger(MVC.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
该代码执行以下操作:
- 使用
JavaFX
WebEngine
加载文档。 - 无限期地使用
javax.xml
包在文档上执行xpath查询,不存储结果或指针。
java.util.HashMap$Node
com.sun.webkit.Disposer$WeakDisposerRecord
com.sun.webkit.dom.NamedNodeMapImpl$SelfDisposer
java.util.concurrent.LinkedBlockingQueue$Node
我测试的设置如下:
- MBP运行OS X Yosemite(最新版本)
- JDK 1.8.0_60