2015-05-21 27 views
5

Tôi có một câu hỏi liên quan đến vòng đời của đậu CDI phiên scoped.
Theo như tôi hiểu, một phiên CDI phạm vi đậu được xây dựng bởi container khi phiên bắt đầu và hủy khi phiên kết thúc. Trước khi đậu bị phá hủy, phương thức @PreDestroy được gọi như được mô tả ở đây https://docs.oracle.com/javaee/6/tutorial/doc/gmgkd.html. Nó cũng nói để giải phóng tài nguyên trong phương pháp này.
Đậu phạm vi phiên CDI không bị phá hủy kết quả trong rò rỉ bộ nhớ

Trong một ứng dụng JSF tôi xây dựng tôi kinh nghiệm Memory Leak vì đậu dường như không thể bị phá hủy và do đó @PreDestroy Phương pháp không được viện dẫn để giải phóng một số tài liệu tham khảo cho các nhà sưu tập rác. Vì vậy, tôi đã xây dựng một Ứng dụng đơn giản để kiểm tra hành vi. Kinh nghiệm của tôi là bean phiên không bị hủy khi phiên kết thúc và hơn nữa nó thậm chí không bị phá hủy khi không gian bộ nhớ là cần thiết. Tôi không thể tin rằng tôi là người đầu tiên gặp này, nhưng tôi không tìm thấy bất kỳ thông tin về hành vi này ..

Vì vậy, câu hỏi của tôi là: nên một bean CDI không bị hủy diệt - và do đó @PreDestroy Phương thức được gọi - ngay lập tức sau khi bối cảnh của nó hết hạn? Và nếu không nên nó ít nhất bị phá hủy khi không gian là cần thiết?

ứng dụng thử nghiệm của tôi:

Tôi không được phép đăng một bức tranh, nhưng đề cương là webapp JSF rất cơ bản được tạo ra bởi nhật thực. Tôi cũng có tệp beans.xml.

Test.java:

package com.test; 

import java.io.Serializable; 
import java.util.ArrayList; 

import javax.annotation.PostConstruct; 
import javax.annotation.PreDestroy; 
import javax.enterprise.context.SessionScoped; 
import javax.inject.Named; 

@SessionScoped 
@Named 
public class Test implements Serializable { 

    /** 
    * 
    */ 
    private static final long serialVersionUID = 1L; 
    private String test; 
    private ArrayList<ComplexType> cps; 
    private ArrayList<ComplexType> cps_2; 

    @PostConstruct 
    public void init() { 
     System.out.println("test postconstruct.."); 
     test = "Cdi Test"; 
    } 

    @PreDestroy 
    public void cleanUp() { 
     cps = null; 
     cps_2 = null; 
     System.out.println("test cleanUp...."); 
    } 

    public void data_1() { 

     cps = new ArrayList<ComplexType>(); 

     for(int i = 0; i < 800; i++) { 
      String[] s = new String[100000]; 
      ComplexType cp = new ComplexType(i, s); 
      cps.add(cp); 
      System.out.println(i); 
     } 
     System.out.println("data_1"); 
    } 

    public void free_1() { 
     cps = null; 
     System.out.println("free_1"); 
    } 

    public void data_2() { 

     cps_2 = new ArrayList<ComplexType>(); 

     for(int i = 0; i < 800; i++) { 
      String[] s = new String[100000]; 
      ComplexType cp = new ComplexType(i, s); 
      cps_2.add(cp); 
      System.out.println(i); 
     } 
     System.out.println("data_1"); 
    } 

    public void free_2() { 
     cps_2 = null; 
     System.out.println("free_1"); 
    } 

    public String getTest() { 
     return test; 
    } 

    public void setTest(String test) { 
     this.test = test; 
    } 
} 

ComplexType.java:

package com.test; 

public class ComplexType { 

    private int id; 
    private String[] name; 

    public ComplexType(int id, String[] name) { 

     this.id = id; 
     this.name = name; 
    } 
    public int getId() { 
     return id; 
    } 
    public void setId(int id) { 
     this.id = id; 
    } 
    public String[] getName() { 
     return name; 
    } 
    public void setName(String[] name) { 
     this.name = name; 
    } 
} 

index.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml" 
xmlns:h="http://java.sun.com/jsf/html" 
xmlns:f="http://java.sun.com/jsf/core" 
> 

<h:head> 
    <title>Cdi test </title> 
</h:head> 

<h:body> 

    <h:outputText value="#{test.test}"></h:outputText> 

    <h:form> 
     <h:commandButton value="cp_1 data" actionListener="#{test.data_1}"> 
      <f:ajax></f:ajax> 
     </h:commandButton> 
     <h:commandButton value="cp_1 Free" actionListener="#{test.free_1}"> 
      <f:ajax></f:ajax> 
     </h:commandButton> 

     <br></br> 
     <h:commandButton value="cp_2 data" actionListener="#{test.data_2}"> 
      <f:ajax></f:ajax> 
     </h:commandButton> 
     <h:commandButton value="cp_2 Free" actionListener="#{test.free_2}"> 
      <f:ajax></f:ajax> 
     </h:commandButton> 
    </h:form> 

</h:body> 
</html> 

tôi mở trang index.xhtml và @PostConstruct Phương thức được gọi như mong đợi. Không gian heap được vượt quá khi tôi gọi data_1 và data_2 cả mà không giải phóng ở giữa. Khi tôi giải phóng một trong các tài nguyên ở giữa hoặc tôi gọi một phương thức hai lần liên tiếp, không gian heap là đủ, vì bộ thu gom rác sẽ giải phóng bộ nhớ. Điều này hoạt động như tôi mong đợi nó hoạt động.

Nhưng khi tôi gọi một hàm dữ liệu, đóng trình duyệt và do đó phiên, mở trình duyệt mới và gọi lại một lần nữa, sau đó ứng dụng ngừng hoạt động như (tôi đoán) không gian bộ nhớ bị vượt quá . Vấn đề là: bean phiên đầu tiên không bị hủy và phương thức @PreDestroy của chúng ta không được gọi và do đó ArrayList vẫn còn trong bộ nhớ.

Ai đó có thể giải thích cho tôi điều gì đang xảy ra ở đây không? Không nên một hạt CDI bị phá hủy bởi container ngay sau khi bối cảnh của nó hết hạn để tham khảo có thể được thiết lập để null và thu gom rác có thể tài nguyên miễn phí?
Tôi đang sử dụng JBoss AS 7.1.1 và thực thi mặc định JSF Mojarra 2.1.

+0

JBoss AS 7.1.1 là cổ đại. Ít nhất hãy thử phiên bản Weld hiện tại để loại trừ lỗi cố định đã biết và đã tồn tại từ nguyên nhân. – BalusC

+0

Ok, cảm ơn bạn BalusC, tôi sẽ cố gắng và quay trở lại! –

+0

Tôi đã nâng cấp triển khai WELD lên 1.1.23, nhưng nó không giúp ích gì. –

Trả lời

2

Câu trả lời của @olexd về cơ bản giải thích những gì tôi đã nhận được sai trong tâm trí của tôi, cảm ơn bạn rất nhiều! Nhưng làm mất hiệu lực phiên sau một khoảng thời gian xác định không phải là một lựa chọn, vì vậy tôi đã phải sử dụng bình luận của @ geert3, cảm ơn bạn vì điều đó! Tôi trả lời câu hỏi của riêng mình để cho thấy cách tôi đã giải quyết vấn đề cụ thể của mình một cách chi tiết ở đây.

Điều tôi đã sai: Tôi nghĩ phiên sẽ hết hạn ngay khi trình duyệt bị đóng. Điều này là sai và nó có ý nghĩa. Người ta có thể muốn đóng trình duyệt và mở lại nó để làm việc trong cùng một phiên như trước.
Đối với tôi hành vi này không phù hợp vì tôi muốn phát hành tài nguyên ngay khi trình duyệt bị đóng. Vì vậy, câu trả lời là tự làm mất hiệu lực phiên như thế này:

FacesContext.getCurrentInstance().getExternalContext().invalidateSession(); 

Ngay sau khi phương pháp này được gọi, @PreDestroy Phương pháp được gọi, chính xác như tôi muốn nó. Bây giờ tôi phải xác định khi nào nên gọi hàm này. Tôi đã tìm kiếm một cách để nghe một nội dung như một sự kiện trình duyệtclose sự kiện. Có các sự kiện onbeforeunloadonunload. onunload dường như không hoạt động đối với tôi trong Chrome, nhưng onbeforeunload sẽ hoạt động. Xem thêm câu trả lời này: https://stackoverflow.com/a/16677225/1566562

Vì vậy, tôi đã viết một nút ẩn được nhấp bởi javascript trên beforeunload và gọi phương thức sao lưu thích hợp. Điều này hoạt động như tôi mong đợi nó hoạt động. Tôi đã thử nghiệm trên Chrome 43.0.2357.65 và IE 11, bây giờ tôi hài lòng với nó. Tuy nhiên, nó không hoạt động với onunload, nhưng điều này không phải là mối quan tâm đối với tôi ngay bây giờ.

Vì vậy, mã cuối cùng của tôi thích này:

index.xhtml

<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:f="http://java.sun.com/jsf/core"> 

<h:head> 
    <title>Cdi test</title> 
    <h:outputScript library="default" name="js/jquery-1.11.3.min.js" 
     target="head"></h:outputScript> 
</h:head> 

<h:body> 

    <h:outputText value="#{test.test}"></h:outputText> 

    <h:form id="overall"> 
     <h:commandButton value="cp_1 data" actionListener="#{test.data_1}"> 
      <f:ajax></f:ajax> 
     </h:commandButton> 
     <h:commandButton value="cp_1 Free" actionListener="#{test.free_1}"> 
      <f:ajax></f:ajax> 
     </h:commandButton> 

     <br></br> 
     <h:commandButton value="cp_2 data" actionListener="#{test.data_2}"> 
      <f:ajax></f:ajax> 
     </h:commandButton> 
     <h:commandButton value="cp_2 Free" actionListener="#{test.free_2}"> 
      <f:ajax></f:ajax> 
     </h:commandButton> 

     <br></br> 

     <h:commandButton id="b" style="display:none" 
      actionListener="#{test.invalidate}"></h:commandButton> 

    </h:form> 

    <script type="text/javascript"> 
     $(window).on('beforeunload', function() { 
      $('#overall\\:b').click(); 
     }); 
    </script> 
</h:body> 
</html> 

Test.java

package com.test; 

import java.io.Serializable; 
import java.util.ArrayList; 

import javax.annotation.PostConstruct; 
import javax.annotation.PreDestroy; 
import javax.enterprise.context.SessionScoped; 
import javax.faces.context.FacesContext; 
import javax.inject.Named; 

@SessionScoped 
@Named 
public class Test implements Serializable { 

    /** 
    * 
    */ 
    private static final long serialVersionUID = 1L; 
    private String test; 
    private ArrayList<ComplexType> cps; 
    private ArrayList<ComplexType> cps_2; 

    @PostConstruct 
    public void init() { 
     System.out.println("test postconstruct.."); 
     test = "Cdi Test"; 
    } 

    @PreDestroy 
    public void cleanUp() { 
     cps = null; 
     cps_2 = null; 
     System.out.println("test cleanUp...."); 
    } 

    public void data_1() { 

     cps = new ArrayList<ComplexType>(); 

     for (int i = 0; i < 800; i++) { 
      String[] s = new String[100000]; 
      ComplexType cp = new ComplexType(i, s); 
      cps.add(cp); 
      System.out.println(i); 
     } 
     System.out.println("data_1"); 
    } 

    public void free_1() { 
     cps = null; 
     System.out.println("free_1"); 
    } 

    public void data_2() { 

     cps_2 = new ArrayList<ComplexType>(); 

     for (int i = 0; i < 800; i++) { 
      String[] s = new String[100000]; 
      ComplexType cp = new ComplexType(i, s); 
      cps_2.add(cp); 
      System.out.println(i); 
     } 
     System.out.println("data_2"); 
    } 

    public void free_2() { 
     cps_2 = null; 
     System.out.println("free_2"); 
    } 

    public void invalidate() { 
     FacesContext.getCurrentInstance().getExternalContext().invalidateSession(); 
     System.out.println("invalidate"); 
    } 

    public String getTest() { 
     return test; 
    } 

    public void setTest(String test) { 
     this.test = test; 
    } 

} 

Lưu ý rằng tôi đã sử dụng JQuery. Điều này làm việc với JBoss AS 7.1.1 và cài đặt Weld mặc định.
Một điều cần thêm: người ta không phải đặt tất cả các tham chiếu theo cách thủ công thành null. Điều này có ý nghĩa là tốt, vì nó sẽ tẻ nhạt ..

6

Đậu phiên (bất kể CDI hoặc JSF được quản lý) vẫn hoạt động cho đến khi hết thời gian chờ (thường là 30 phút, phụ thuộc vào máy chủ ứng dụng), bạn có thể chỉ định trong web.xml. Chỉ cần đóng trình duyệt không làm mất hiệu lực phiên và nó chờ đợi để bị phá hủy bởi thùng chứa servlet sau khi hết thời gian chờ.Vì vậy, giả định của tôi, hành vi như vậy là tốt, phương pháp @PreDestroy sẽ được gọi sau.

+0

Cảm ơn bạn olexd, bạn đã đúng. Tôi đặt thông tin sau trong trang web của mình.xml: ' 1 ' và sau một phút bean bị hủy và phương thức _ @ PreDestroy_ đã được gọi –

Các vấn đề liên quan