Por que os beans @ViewScoped expirados não são destruídos até que a sessão expire
Estou usando o Mojarra 2.2.4 no GlassFish 4 com Java 7.
Pelo que entendi da resposta da BalusC paraComo e quando um bean @ViewScoped é destruído no JSF?, Os beans @ViewScoped devem ser destruídos em três casos:
Post-back com resultado não nuloExpiração da sessãoNúmero máximo de visualizações lógicas na sessão excedidoMeus beans estão sendo destruídos nos dois primeiros casos, mas não quando o número máximo de visualizações lógicas é excedido. Eu verifiquei que os feijõesexpirar quando o máximo é excedido (recebo uma ViewExpiredException), mas eles ainda não sãodestruído até a própria sessão expirar.
Por razões de consumo de memória, eu gostaria que os beans fossem destruídos nesse terceiro caso, principalmente porque eles não são utilizáveis após a expiração.
QuestõesPor que os grãos não são destruídos quando expiram?Isso é um bug ou comportamento esperado?O que seria uma solução alternativa para garantir que os grãos sejam destruídos?Atualizar: Anotação do OmniFaces ViewScoped destrói os grãos assim que expiram.Exemplo MínimoAqui está o meu feijão:
@javax.inject.Named("sandboxController")
@javax.faces.view.ViewScoped
public class SandboxController implements Serializable {
private static final Logger log = Logger.getLogger(SandboxController.class.getName());
@PostConstruct
public void postConstruct() {
log.log(Level.INFO, "Constructing SandboxController");
}
@PreDestroy
public void preDestroy() {
log.log(Level.INFO, "Destroying SandboxController");
}
public String getData() {
return "abcdefg";
}
}
e minha sandbox.xhtml:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<body>
<h:form>
<h:outputText value="#{sandboxController.data}"/>
</h:form>
</body>
</html>
e parte do meu web.xml:
<context-param>
<param-name>com.sun.faces.numberOfLogicalViews</param-name>
<param-value>3</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.numberOfViewsInSession</param-name>
<param-value>3</param-value>
</context-param>
Se eu atualizar o sandbox.xhtml 50 vezes, recebo 50 cópias deINFO: Constructing SandboxController
no log. Os grãos não são destruídos, independentemente de quantas vezes eu atualizo. O VisualVM confirma que os beans ainda são referenciados pelo ViewMap do UIViewRoot. No meu bean de tamanho normal, que mantém um bom estado, recebo rapidamente uma OutOfMemoryException.
Quando expiro manualmente a sessão, recebo 50 cópias deINFO: Destroying SandboxController
.
Se eu adicionar um botão de envio ao sandbox.xhtml e carregá-lo em quatro guias diferentes, tente enviar a primeira, e recebo uma ViewExpiredException, conforme o esperado, mas o bean ainda não foi destruído.
O comportamento é o mesmo se eu usar as anotações javax.faces.bean.ManagedBean e javax.faces.view.ViewScoped. No entanto, a anotação do OmniFaces org.omnifaces.cdi.ViewScoped funciona corretamente.
Esclarecer...Meus beans @ViewScopedestão sendo destruídos na expiração da sessão, diferentemente dos problemas descritos em perguntas relacionadas, comoOs beans com escopo definido levam a vazamentos de memória
eu sounão perguntando por que cada feijão não é destruídoimediatamente na atualização subsequente, conforme questionado aqui:O método JSF 2.1 ViewScopedBean @PreDestroy não é chamado. Quero saber por que, mesmo quando eles expiram e não são mais úteis, eles ainda não são destruídos e, portanto, continuam consumindo memória.