我正在使用Java 7上的GlassFish 4和Mojarra 2.2.4。
根据BalusC在How and when is a @ViewScoped bean destroyed in JSF?中的回答,@ViewScoped beans应该在以下三种情况下被销毁:
- 后退并且结果不为空
- 会话过期
- 会话中的逻辑视图达到最大值
我的bean在前两种情况下被销毁了,但是当逻辑视图的最大数量超过时,它们并没有被销毁。我已经验证了当达到最大值时,bean确实会过期(我会得到一个ViewExpiredException),但是直到会话本身过期它们才被销毁。
出于内存消耗的原因,我希望在第三种情况下销毁这些bean,特别是因为它们在过期后无法使用。
问题
- 为什么bean在过期时没有被销毁?
- 这是一个bug还是预期行为?
- 有什么干净的解决方法来确保bean被销毁?
- 更新:OmniFaces ViewScoped注释在bean过期后立即销毁它们。
最小示例
这是我的bean:
@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";
}
}
以及我的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>
以下是我的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>
如果我刷新sandbox.xhtml 50次,日志中会出现
INFO: Constructing SandboxController
的50个副本。无论我刷新多少次,这些Bean都不会被销毁。VisualVM证实这些Bean仍然被UIViewRoot的ViewMap引用。在我的完整大小的Bean中,它保留了相当多的状态,很快就会出现OutOfMemoryException。当我手动过期会话时,会出现50个
INFO: Destroying SandboxController
副本。如果我向sandbox.xhtml添加一个提交按钮并在4个不同的选项卡中加载它,然后尝试提交第一个选项卡,我会得到一个ViewExpiredException,正如预期的那样,但是这个Bean仍然没有被销毁。
如果我使用javax.faces.bean.ManagedBean和javax.faces.view.ViewScoped进行注释,行为是相同的。然而,OmniFaces注释org.omnifaces.cdi.ViewScoped可以正常工作。
澄清一下...
我的@ViewScoped Bean在会话过期时是会被销毁的,不像相关问题中描述的问题,例如Linked ViewScoped beans lead to memory leaks 我不是在问每个Bean为什么不会立即在后续刷新时被销毁,就像这里提出的问题一样:JSF 2.1 ViewScopedBean @PreDestroy method is not called。我想知道的是,即使它们过期了,并且不再有用,为什么它们仍然没有被销毁,因此继续消耗内存。