2015-09-17 43 views
5

Tôi có một tình huống đã tra tấn tôi trong nhiều tháng: Tôi tiếp tục nhận ngoại lệ OOM (Heap Space) và kiểm tra đống đống tôi đã tìm thấy hàng triệu đối tượng mà tôi chưa từng phân bổ nhưng có khả năng được cấp phát trong thư viện cơ bản. Sau nhiều máu, mồ hôi và nước mắt tôi đã được quản lý để bản địa hoá mã tạo ra rò rỉ bộ nhớ và tôi đã sáng tác một mẫu mã tối thiểu, đầy đủ và có thể kiểm chứng để minh họa điều này:Rò rỉ bộ nhớ xpath Java?

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); 
      } 
     } 
    } 
} 

mã nào sau đây:

  • tải tài liệu bằng cách sử dụng JavaFXWebEngine.
  • không ngừng thực hiện truy vấn xpath trên tài liệu bằng cách sử dụng gói javax.xml, mà không lưu trữ kết quả hoặc con trỏ đến số.

Để chạy, hãy tạo ứng dụng JavaFX, thêm tệp có tên MVC.java vào gói mặc định, nhập mã và nhấn chạy. Bất kỳ công cụ lược tả nào (tôi sử dụng VisualVM) sẽ nhanh chóng cho bạn thấy rằng chỉ trong vài phút, đống phát triển không kiểm soát được. Các đối tượng sau đây dường như được phân bổ nhưng không bao giờ phát hành:

  • java.util.HashMap$Node
  • com.sun.webkit.Disposer$WeakDisposerRecord
  • com.sun.webkit.dom.NamedNodeMapImpl$SelfDisposer
  • java.util.concurrent.LinkedBlockingQueue$Node

Hành vi này xảy ra mỗi khi tôi chạy mã, không phụ thuộc vào url Tôi tải hoặc xpath tôi thực hiện trên tài liệu.

cài đặt mà tôi thử nghiệm:

  • MBP chạy OS X Yosemite (up-to-date)
  • JDK 1.8.0_60

thể bất cứ ai tạo lại vấn đề này? Nó là một rò rỉ bộ nhớ thực tế? Tôi có thể làm gì không?

chỉnh sửa

Một đồng nghiệp của tôi sao chép các vấn đề trên một máy w7 với JDK 1.8.0_45, và nó sẽ xảy ra trên một máy chủ Ubuntu là tốt.

chỉnh sửa 2

Tôi đã thử nghiệm jaxen như một thay thế cho gói javax.xml, nhưng kết quả đều giống nhau, dẫn tôi để tin rằng lỗi nằm sâu bên trong webkit nắng

+0

có thể có liên quan: http://stackoverflow.com/questions/6340802/java-xpath-apache-jaxp-implementation-performance – Warkst

+2

tôi có thể tái sản xuất này trên Windows 7 64 bit, Java 1.8.0_60. Nó dường như là một rò rỉ bộ nhớ. Tôi đã thử thực hiện cùng một vòng lặp trên một tệp XML tùy ý mà không cần liên quan đến JavaFX và có cùng kết quả. – VGR

+0

Cảm ơn bạn đã xem xét điều này! Tôi thậm chí đã không xem xét không sử dụng javafx nhưng bạn hoàn toàn đúng, lỗi nằm sâu hơn, và cách các tài liệu w3c được cung cấp là không quan trọng. – Warkst

Trả lời

7

Tôi sao chép rò rỉ với jdk1.8.60 trong Ubuntu. Tôi đã làm khá một số hồ sơ và gỡ lỗi và nguyên nhân cốt lõi là đơn giản và nó có thể được cố định dễ dàng. Không có rò rỉ bộ nhớ trong các công cụ XPath.

Có một lớp com.sun.webkit.Disposer, liên tục làm sạch một số cấu trúc bên trong được tạo trong quá trình đánh giá XPath.Disposer internaly gọi cleanup qua Invoker.getInvoker(). InvokeOnEventThread (điều này);. Bạn có thể thấy nó nếu bạn dịch ngược mã. Có những cách triển khai khác nhau của người gọi, sử dụng các luồng khác nhau. Nếu bạn làm việc trong JavaFX, Invoker sẽ thực hiện dọn dẹp định kỳ trong luồng JavaFX.

Tuy nhiên, phương thức nghe thay đổi cũng được gọi trong chuỗi JavaFX, và nó không bao giờ trả về, vì vậy việc dọn dẹp chưa bao giờ có cơ hội xảy ra.

Tôi đã sửa đổi mã của bạn, để phương thức thay đổi chỉ sinh ra chuỗi và trả về mới và quá trình xử lý được thực hiện không đồng bộ. Và đoán những gì - bộ nhớ không phát triển nữa:

@Override 
public void changed(ObservableValue<? extends Worker.State> observable, Worker.State oldValue, Worker.State newValue) { 
    if (newValue==Worker.State.SUCCEEDED) { 
     new Thread(() ->{ 
      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); 
      } 
     }).start(); 
    } 
} 
+0

Rất phát hiện. Tôi đã có thể áp dụng nguyên tắc này trong dự án chính của tôi (như đã nói, mã trong câu hỏi chỉ là một MVC) và bây giờ bộ nhớ không còn phát triển nữa. Trong dự án chính của tôi, tôi đã không làm bất cứ điều gì ngớ ngẩn như chặn hàng đợi sự kiện mãi mãi - hoặc ít nhất là không có mục đích, nhưng dường như tôi vô tình làm vậy (đó là một tổ gọi lại không đồng bộ lớn), nhưng chỉ đơn giản giới thiệu một chủ đề mới nhất crunching xảy ra giải phóng thread sự kiện để chạy disposers, giữ bộ nhớ trong kiểm tra. Cảm ơn và + đại diện. – Warkst