jsf – 未被破坏的CDI会话范围bean导致内存泄漏

我对会话作用域CDI bean的生命周期有疑问.
据我所知,会话范围内的CDI bean是在会话开始时由容器构造的,并在会话结束时销毁.在销毁bean之前,调用@PreDestroy方法,如https://docs.oracle.com/javaee/6/tutorial/doc/gmgkd.html所述.它还说要在此方法中释放资源.

在我构建的JSF应用程序中,我遇到内存泄漏,因为bean似乎没有被销毁,因此不会调用@PreDestroy方法来释放垃圾收集器的一些引用.所以我构建了一个简单的应用程序来测试行为.我的经验是会话bean在会话结束时不会被破坏,而且当需要内存空间时它甚至不会被破坏.我不敢相信我是第一个遇到这种情况的人,但我没有找到任何有关此行为的信息.

所以我的问题是:在上下文过期之后,不应该销毁CDI bean – 因此调用@PreDestroy方法吗?如果不是,在需要空间时,它至少应该被破坏吗?

我的测试应用:

我不允许发布图片,但大纲是eclipse生成的非常基本的jsf webapp.我也有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>

我打开index.xhtml页面,并按预期调用@PostConstruct方法.当我调用data_1和data_2时两者都没有释放,超出了堆空间.当我释放其中一个资源或者我连续两次调用一个方法时,堆空间就足够了,因为垃圾收集器会释放内存.这是我希望它工作的工作原理.

但是当我调用一个数据函数时,关闭浏览器,然后关闭会话,打开一个新的浏览器并再次调用其中一个数据函数,然后应用程序停止工作(我猜)超出了内存空间.关键是:第一个会话bean没有被销毁,而且@PreDestroy方法没有被调用,因此ArrayList仍然在内存中.

有人可以向我解释这里发生了什么吗?如果CDI bean上下文过期,它不应该被容器销毁,以便引用可以设置为null并且垃圾收集器可以释放资源吗?
我正在使用JBoss AS 7.1.1及其默认实现JSF Mojarra 2.1.

最佳答案
会话bean(无论CDI或JSF托管)保持活动状态,直到某个会话超时(通常默认为30分钟,取决于应用程序服务器),您可以在web.xml中指定.关闭浏览器不会使会话无效,并且等待超时到期后servlet容器将其销毁.所以,我的假设,这样的行为就好了,以后会调用@PreDestroy方法.

转载注明原文:jsf – 未被破坏的CDI会话范围bean导致内存泄漏 - 代码日志