【问题标题】:JSF Enterprise application memory usageJSF Enterprise 应用程序内存使用情况
【发布时间】:2012-05-22 08:15:43
【问题描述】:

我有一个 Java EE 应用程序如下:
- 服务器在亚马逊上(大型实例,2 CPU @ 2.27GHz,8GB RAM) - 由 Apache 直接提供的静态内容
- 在 Glassfish 3.1.1 上运行的 JSF2 (Mojarra 2.1.3) 和 JPA 2 (Eclipselink 2.3.0)
- Facelets/XHTML 从 ViewScoped 托管 bean 获取内容,这些 bean 连接到执行所有处理的 @Local Stateless EJB,包括从使用 JPA 的其他 @Local Stateless EJB 获取数据
所以通常:

XHTML --> ViewScoped Managed Bean --> Service EJB --> Data EJB --> JPA

鉴于我只运行一个 Glassfish 实例,我知道我可以/应该将 2 层 EJB 移除为一层,甚至不移除,但目前我认为这不是问题。

应用程序的性能还可以(2.2MB,包括 那时 CPU 使用了 50%,RAM 使用了 100%。

所以我运行了 JProfiler,但我不确定如何处理一种类型的结果。
在主页中,我们有一个类别列表,并且每个类别关联了许多产品(一个告诉每个类别中有多少产品的购物网站)。获取Category列表的代码是:

ViewScoped Bean

public List<Category> getLiveCategoriesInfo() {
    if (liveCategories == null) {
        liveCategories = liveCategoryService.getLiveCategories(getLocale().getLang().getLanguageId());
    }
    return liveCategories;
}

getLocale() 是从使用 ManagedProperty 注入的 SessionScoped Bean 中检索到的

服务 EJB:

public List<Category> getLiveCategories(final Integer langId) {
  List<LiveCategory> lives = categoryBean.getLiveCategories(langId);
  // ... some processing involving looping through the list above
  return livesCategories;
}

数据 EJB:

public List<LiveCategory> getLiveCategories(final Integer langId) {
  List<LiveCategory> categories = new ArrayList<LiveCategory>();
  Query cq = getEntityManager().createNamedQuery(Category.FIND_LIVE);
  try {
    categories = cq.getResultList();
  } catch (NullPointerException npe) {
     // ...
  }
  return categories;
}

JProfiler 内存视图显示,在主页上的每个请求(即使对于同一用户),都会向内存添加一批新的类别(准确地说是 43,即显示的类别数)。类别不由 JPA 管理(来自 JPA 的列表用于“手动”创建 POJO)。
我怎样才能从内存中释放这些实体。当视图消失时,我希望它们成为 GC。但是 ViewScoped bean 本身不是 GC'd,有一堆实例留在内存中。

我应该寻找什么来释放这些对象?
- 是否在 ViewScoped bean 中使用 @ManagedProperty 来获取 SessionScoped bean 的实例以防止 ViewScoped 被 GC?
- 我应该寻找其他错误吗?

我确实查看了有关 JSF 最佳实践和性能指南的其他线程,但没有帮助。

【问题讨论】:

    标签: jsf-2 memory-leaks jprofiler


    【解决方案1】:

    我认为您的问题与您如何使用视图范围和会话范围有关。简而言之,您正在用在页面生命周期内不会改变的对象填充内存。相反,您应该仅在处理请求时使用请求范围 bean 来缓存这些结果,并使用 @ManagedProperty 注释或其他(创建值表达式或调用 Application.evaluateExpressionGet 从视图范围或会话范围 bean 获取参数。在这样,当请求结束时,对实体的引用将被释放,并被 GC 收集。

    如果您真的对性能部分感兴趣,请查看此博客:

    Understanding JSF 2 and Wicket: Performance Comparison

    测试代码已经为获得 JSF 和 Wicket 的最佳性能而进行了调整。调优 JSF 非常简单,因此您通常应该关注您的 ORM 工具以获取性能提示。

    【讨论】:

    • 谢谢你,我会检查这个页面。
    • 我用探查器做了一些更长的测试,我可以看到 ViewScoped bean 被 GC'd 但不太频繁。我将一个更改为 RequestScoped(它实际上是有道理的,因为它是只读的东西,并且需要在每次请求时从 DB 获取新数据)并且它被更频繁地收集。我将优化转向 DB SQL,可以将数据库查询的 nb 减少 30%,这对于现在来说已经足够了
    【解决方案2】:

    也许您可以在用户注销时尝试释放 FacesContext。

    【讨论】:

    • 这可能会有所帮助,但我不认为所有用户都会退出。感谢您的建议
    猜你喜欢
    • 2011-03-09
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2013-05-11
    • 2014-11-08
    • 2015-01-13
    • 1970-01-01
    • 2013-07-19
    相关资源
    最近更新 更多