I have a question regarding the CDI beans life cycle for a session.
As I understand it, a session-processed CDI bean is created by the container when the session starts and is destroyed when the session ends. Before the bean is destroyed, the @PreDestroy method is called as described here https://docs.oracle.com/javaee/6/tutorial/doc/gmgkd.html . He also says to free up resources in this method.
In the JSF application I built, I experience a memory leak because the bean does not seem to be destroyed, and therefore the @PreDestroy method is not called to free some links to the garbage collector. Therefore, I created a simple behavior testing application. My experience is that a bean session is not destroyed when the session is completed, and in addition, it is not even destroyed when memory space is required. I cannot believe that I am the first to come across this, but I have not found any information about this behavior.
So my question is: shouldn't the CDI bean be destroyed - and therefore the @PreDestroy method is called - right after its expiration? And if it should not be at least destroyed when space is needed?
My test application:
I am not allowed to post a picture, but the outline is the easiest jsf webapp created by eclipse. I also have a beans.xml file.
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>
I open the index.xhtml page and the @PostConstruct method is called as expected. Heap space is exceeded when I call data_1 and data_2 without freeing in between. When I free one of the resources between them or I call one method twice in a row, there is enough heap space because the garbage collector frees memory. This works the way I expect it to work.
But when I call one data function, close the browser and, therefore, the session, open a new browser and call one of the data functions again, then the application stops working (I think), the memory space is exceeded . The fact is that the first bean session is not destroyed, and its @PreDestroy method is not called, and therefore the ArrayList is still in memory.
Can someone explain to me what is going on here? Should the CDI bean be destroyed by the container as soon as its context expires so that the links can be set to zero and the garbage collector can free up resources?
I am using JBoss AS 7.1.1 and its default implementation is JSF Mojarra 2.1.