每个会话的内存增长
我们正在使用JSF 2.2(2.2.12)与Mojarra进行高内存消耗。在研究了我们的负载测试之后,我们发现ViewScoped Beans中的数据大小非常高(有时超过1MB)。无论如何 - 当从视图导航到视图时,会话内存大小会增长和增长。我们不能在短期内减少bean的大小,因此这种行为会产生相当大的影响。
解决方案1 - 更改上下文参数(不工作)
现在 - 我们使用了Mojarra的官方上下文参数,默认设置为15:
com.sun.faces.numberOfLogicalViews
com.sun.faces.numberOfViewsInSession
将这些参数更改为较低的值对我们的负载测试中的内存消耗没有任何影响。
解决方案2 - 更改activeViewMapsSize(正常工作)
我们正在调试Mojarra并在ViewScopeManager
中找到以下代码:
Integer size = (Integer) sessionMap.get(ACTIVE_VIEW_MAPS_SIZE);
if (size == null) {
size = 25;
}
保留上次访问过的视图的默认大小似乎是25.看到这个,我们实现了一个Session Listener,它将此值设置为1:
public class SetActiveViewMapsSizeSessionListener implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent event) {
event.getSession().setAttribute(ViewScopeManager.ACTIVE_VIEW_MAPS_SIZE, 1);
}
}
这显然有效。由于只保留了1个视图,因此内存停止增长。
为什么25个内存中的观点?
如果没有在Session中定义不同的值,那么Mojarra会在内存中保留25个视图的历史记录。我找不到任何关于此的文档。有人可以解释这是为了什么?它是针对浏览器的吗?我们在JSF页面上禁用了缓存。因此浏览器将始终创建一个新视图。这对我们来说应该不是问题。
解决方案2是一种有效的方法吗?有人可以解释这种方法的缺点吗?
更新1
经过各种评论和更深入的调试后,结果证明:
com.sun.faces.numberOfLogicalViews
定义logicalViewMap大小,该大小仅存储(!)ui组件树的状态com.sun.faces.application.view.activeViewMapsSize
定义了包含ViewScoped bean的activeViewMap的大小将numberOfLogicalViews
更改为1时,mojarra仍会跟踪最近25个视图的所有视图范围内的bean。如果您以相反的方式配置它 - numberOfLogicalViews
到15和activeViewMapsSize
到1 - 由于我猜测数据丢失,无法正确初始化视图。我们甚至没有例外。我想理解,为什么mojarra选择将activeViewMapsSize
设置为高于numberOfLogicalViews
而不是相同,因为我们想调整内存消耗而不会产生不可预测的行为。
更新2
我们在Mojarra创建了一个问题:JAVASERVERFACES-4015。
答案 0 :(得分:2)
为什么 Mojarra 将最后 25 个视图的 ViewScoped Bean 保留在内存中?
因为网页也可以在新的浏览器标签而不是当前浏览器标签中打开。遗憾的是,没有一种万无一失的方法可以根据普通的 HTTP GET 请求来确定视图是在现有浏览器选项卡中打开还是在新浏览器选项卡中打开。因此,无论网页是否在同一浏览器选项卡中打开,所有关联的 bean 都保存在内存中。
<块引用>无论如何 - 在视图之间导航时,会话内存大小会不断增长。
如果您在同一个浏览器选项卡中从一个视图导航到另一个视图,这确实没有意义。但是当您在新的浏览器选项卡中打开下一个视图时,这确实有意义。这样,当您切换回上一个浏览器选项卡并继续与那里的视图交互时,上一个浏览器选项卡中的视图可以继续正常工作。
<块引用>我们无法在短期内减小 bean 的大小,因此这种行为具有相当大的影响。
技术上可以在客户端检测当前页面是否已卸载并通知服务器这种情况。现在,pagehide
事件可用于在客户端检查当前视图是否已被销毁,而 navigator.sendBeacon
可用于以可靠的方式通知服务器此情况(使用例如 unload
和 XMLHttpRequest
的组合不太可靠,因为无法保证它是否会真正按时命中服务器)。
自 OmniFaces 2.2(2015 年 11 月)以来,这一切都在 OmniFaces @ViewScoped
背后的逻辑中实现,并且自 OmniFaces 2.7.3(2019 年 11 月)以来经过多年具体化为目前的形态。如果您已经在使用 CDI 来管理 bean,那么应该通过 import javax.faces.view.ViewScoped;
换出源代码中的 import org.omnifaces.cdi.ViewScoped;
行以便利用它。在我参与过的一个项目中,自从本机 JSF 视图范围 bean 迁移到 OmniFaces 视图范围 bean 以来,内存使用量减少了 70%。