我们正在 Tomcat 5.5 中使用 StandardManager 进行 session (在内存中)运行供应商提供的 Web 应用程序。由于 session 可能变得相当大(20M+),堆空间耗尽是一个严重的问题。如果可能的话,用户希望将 session 保留几个小时,但宁愿驱逐 session 也不愿耗尽堆空间。供应商似乎没有在 session 对象中正确实现可序列化,因此切换到持久 session 管理器实现不是一个选项。
Tomcat 允许设置 maxActiveSessions 属性,该属性将限制管理器中的 session 总数。但是,当达到该限制时,在某些现有 session 到期之前,无法创建新 session 。我们希望首先破坏最近最少使用的 session 。
理想情况下,当堆使用量接近“Xmx”设置时,我们希望使一些最近未使用的 session 过期,即使它们还不够老,无法无条件过期。一个非常古老的 Tomcat 开发人员邮件列表线程建议这可能会允许拒绝服务攻击*,但是,由于此应用程序仅在公司网络内可用,因此我们并不担心。
我考虑过扩展 StandardManager 来覆盖 processExpires() 并在堆使用率大于最大值的 85% 时破坏额外的 session 。然而,这在实践中似乎有点问题。如果堆的大部分未被引用,并且垃圾收集器将能够收集大量对象(如果它不厌其烦地运行)以将堆减少到最大值的 50%,该怎么办?我会不必要地使 session 过期。我想我们可以通过一些积极的垃圾收集设置来减轻这种风险。另外,我如何知道 session 过期节省了多少内存?我必须等待几个 GC 周期才能确定。也许我可以采取保守的方法,在每个后台进程周期删除最多 N 个 session ,直到内存降至可接受的阈值以下。我可以序列化 session 作为估计有多少内容将被 GC 处理的方法,但这依赖于供应商实现 Serialized 并将实例变量适本地标记为 transient 。
有人解决这个问题了吗?作为短期解决方案,我们正在增加堆大小,但这种创可贴也有其缺点。
- 他们指的是一个公共(public)网站,其中将在登录前创建 session 。有人可能会导致创建许多新 session 并排挤实际使用的 session 。
更新:我们确实对系统架构没有太多控制权,而且我们特别无法减少 session 使用。然而,我们可以按照自己的意愿对容器进行修改。然而,Tomcat 是唯一受供应商支持的开源 servlet 容器。
最佳答案
您的选择似乎是:
- 减少空闲 session 超时,
- 使 session 持久化(可能仅在用户登录后),
- 减少每个 session 对象使用的内存,
- 增加 Tomcat 实例的内存,
- 运行服务的多个实例,并在其前面放置一个负载均衡器。
从技术角度来看,3 是最好的解决方案……如果可行的话。其他都有缺点。
用内存来做聪明的事情只是一个创可贴。从用户的角度来看,它使您的网站的行为更难以理解。此外,如果您的用户群/流量呈上升趋势,那么您只是在推迟寻找可持续解决方案的问题。
关于java - Tomcat session 驱逐以避免内存不足错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1197530/