JSF 2.2内存消耗:为什么Mojarra保留了内存中最后25个视图的ViewScoped Beans?

fis*_*tte 23 jsf mojarra jsf-2.2

每个会话的内存增长

使用JSF 2.2(2.2.12)和Mojarra,我们正在经历高内存消耗.在研究了我们的负载测试之后,我们发现ViewScoped Beans中的数据大小非常高(有时超过1MB).无论如何 - 当从视图导航到视图时,会话内存大小会增长和增长.我们不能在短期内减少bean的大小,因此这种行为会产生相当大的影响.

解决方案1 ​​ - 更改上下文参数(不工作)

现在 - 我们使用了Mojarra的官方上下文参数,默认情况下设置为15:

com.sun.faces.numberOfLogicalViews
com.sun.faces.numberOfViewsInSession
Run Code Online (Sandbox Code Playgroud)

将这些参数更改为较低的值对我们的负载测试中的内存消耗没有任何影响.

解决方案2 - 更改activeViewMapsSize(工作)

我们正在调试Mojarra并在以下代码中找到以下代码ViewScopeManager:

Integer size = (Integer) sessionMap.get(ACTIVE_VIEW_MAPS_SIZE);
if (size == null) {
    size = 25;
}
Run Code Online (Sandbox Code Playgroud)

保留上次访问的视图的默认大小似乎是25.看到这一点,我们实现了一个Session Listener,它将此值设置为1:

public class SetActiveViewMapsSizeSessionListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent event) {
        event.getSession().setAttribute(ViewScopeManager.ACTIVE_VIEW_MAPS_SIZE, 1);
    }
}
Run Code Online (Sandbox Code Playgroud)

这显然有效.由于只保留了1个视图,因此内存停止增长.

为什么25个内存中的观点?

因此,如果Session中没有定义不同的值,Mojarra会保留25个内存视图的历史记录.我找不到任何关于此的文档.有人可以解释这是为了什么?它是针对浏览器的吗?我们在JSF页面上禁用了缓存.因此浏览器将始终创建一个新视图.这不应成为我们的问题.

解决方案2是有效的方法吗?有人可以解释这种方法的缺点吗?

更新1

经过各种评论和更深入的调试后,结果证明:

  • com.sun.faces.numberOfLogicalViews 定义logicalViewMap大小,该大小仅存储(!)ui组件树的状态
  • com.sun.faces.application.view.activeViewMapsSize 定义activeViewMap的大小,它包含ViewScoped bean

当更改numberOfLogicalViews为1时,mojarra仍会跟踪过去25个视图中的所有视图范围内的bean.当你以相反的方式配置它 - numberOfLogicalViews从15 activeViewMapsSize到1时 - 由于我猜想缺少数据,因此无法正确初始化视图.我们甚至没有例外.我想理解,为什么mojarra选择设置activeViewMapsSize高于numberOfLogicalViews和不高,因为我们想调整内存消耗而不会产生不可预测的行为.

更新2

我们在Mojarra JAVASERVERFACES-4015创建了一个问题

Bal*_*usC 2

为什么 Mojarra 将最后 25 个视图的 ViewScoped Bean 保留在内存中?

因为网页也可以在新的浏览器选项卡中打开,而不是在当前浏览器选项卡中打开。不幸的是,没有一种万无一失的方法可以根据普通的 HTTP GET 请求来确定视图是在现有浏览器选项卡中还是在新浏览器选项卡中打开。因此,无论网页是否在同一浏览器选项卡中打开,所有关联的 Bean 都会保留在内存中。

无论如何 - 当从一个视图导航到另一个视图时,会话内存大小会不断增加。

如果您在同一浏览器选项卡中从一个视图导航到另一个视图,这确实毫无意义。但是,当您在新的浏览器选项卡中打开下一个视图时,这确实有意义。这样,当您切换回上一个浏览器选项卡并继续与那里的视图交互时,上一个浏览器选项卡中的视图将保持正常工作。

我们无法在短期内减小豆子的大小,因此这种行为会产生相当大的影响。

从技术上讲,可以在客户端检测当前页面是否已卸载,并将此情况通知服务器。如今,pagehide事件可用于检查客户端中当前视图是否已被销毁,并且可navigator.sendBeacon用于以可靠的方式通知服务器有关此情况的信息(使用egunload和的组合XMLHttpRequest不太可靠,因为有不能保证它是否会真正按时到达服务器)。

自 OmniFaces 2.2(2015 年 11 月)以来,这一切都在OmniFaces@ViewScoped背后的逻辑中实现,并自 OmniFaces 2.7.3(2019 年 11 月)以来多年来具体化为当前的形式。如果您已经使用 CDI 来管理 bean,那么为了利用它,应该将import javax.faces.view.ViewScoped;源代码中的行替换为 by 。import org.omnifaces.cdi.ViewScoped;在我参与过的一个项目中,自从将本机 JSF 视图作用域 bean 迁移到 OmniFaces 视图作用域 bean 以来,内存使用量减少了 70%。

也可以看看: