Ho una domanda riguardante il ciclo di vita dei bean CDI con scope di sessione.
Per quanto ho capito, un bean CDI con scope di sessione viene costruito dal contenitore quando la sessione viene avviata e distrutta al termine della sessione. Prima che il bean venga distrutto, il metodo @PreDestroy viene richiamato come descritto qui https://docs.oracle.com/javaee/6/tutorial/doc/gmgkd.html. Dice anche di rilasciare risorse in questo metodo.
Il bean con scope sessione CDI non distrutto provoca perdite di memoria
In un'applicazione JSF costruisco io sperimento Perdita di memoria, perché il fagiolo non sembra essere distrutti e quindi il @PreDestroy metodo non viene richiamato per liberare alcuni riferimenti per il garbage collector. Così ho creato una semplice applicazione per testare il comportamento. La mia esperienza è che il bean di sessione non viene distrutto quando la sessione è finita e inoltre non viene nemmeno distrutto quando è necessario lo spazio di memoria. Non posso credere che io sono il primo a incontrare questo, ma non trovo alcuna informazione su questo comportamento ..
Quindi la mia domanda è: Non deve essere distrutto un fagiolo CDI - e quindi il @PreDestroy Il metodo deve essere invocato immediatamente dopo la scadenza del suo contesto? E se no, non dovrebbe essere almeno distrutto quando lo spazio è necessario?
Il mio test di applicazione:
io non sono autorizzato a postare una foto, ma il contorno è la webapp JSF molto di base generata da Eclipse. Ho anche il file 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>
apro la pagina di index.xhtml e la @PostConstruct Il metodo viene richiamato come previsto. Lo spazio heap viene superato quando invoco data_1 e data_2 entrambi senza liberare in mezzo. Quando svuoto una delle risorse in mezzo o invoco un metodo due volte di seguito, lo spazio heap è sufficiente, poiché il garbage collector libera la memoria. Funziona come mi aspetterei che funzioni.
Ma quando invoco una funzione di dati, chiudere il browser e quindi la sessione, aprire un nuovo browser e richiamare una delle funzioni di dati ancora una volta, l'applicazione smette di funzionare come (credo) lo spazio di memoria viene superata . Il punto è: il primo bean di sessione non viene distrutto e il suo metodo @PreDestroy non viene richiamato e quindi ArrayList è ancora in memoria.
Qualcuno può spiegarmi che cosa sta succedendo qui? Non dovrebbe un bean CDI essere distrutto dal contenitore non appena il suo contesto scade in modo che i riferimenti possano essere impostati su null e il garbage collector possa liberare risorse?
Sto usando JBoss AS 7.1.1 e la sua implementazione predefinita JSF Mojarra 2.1.
JBoss AS 7.1.1 è antico. Almeno prova la versione corrente di Weld per escludere un bug noto e già lungo fisso dalla causa. – BalusC
Ok, grazie BalusC, ci proverò e tornerò! –
Ho aggiornato l'implementazione di WELD alla versione 1.1.23, ma non è stato di aiuto. –