我有一个 Java EE 应用程序,如下所示:
- 服务器在 Amazon 上(大型实例,2 CPU @ 2.27GHz,8GB RAM) - 由 Apache 直接提供的静态内容
- JSF2 (Mojarra 2.1.3) 和 JPA 2 (Eclipselink 2.3.0 ) 在 Glassfish 3.1.1 上运行
- Facelets/XHTML 从 ViewScoped 托管 bean 获取内容,这些 bean 连接到执行所有处理的 @Local Stateless EJB,包括从使用 JPA 的其他 @Local Stateless EJB 获取数据
所以通常:
XHTML --> ViewScoped Managed Bean --> Service EJB --> Data EJB --> JPA
我知道我可以/应该将 2 层 EJB 删除为一层或什至没有,因为我只运行 Glassfish 的一个实例,但现在我认为这不是问题。
应用程序的性能还可以(2.2MB,包括 < 5 秒内的图像)。问题是当我们有 > 90 个用户在线时,系统变得非常慢(每页 > 30 秒,即使其中大部分被缓存)。
那时 CPU 使用了 50%,RAM 使用了 100%。
所以我运行了 JProfiler,但我不确定如何处理一种类型的结果。
在主页中,我们有一个类别列表,并且每个类别关联了许多产品(一个告诉每个类别中有多少产品的购物网站)。获取类别列表的代码是:
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 Memory View 显示,在主页上的每个请求(即使对于同一个用户),都会将新一批 Category 添加到内存中(准确地说是 43,这是显示的类别数)。类别不由 JPA 管理(来自 JPA 的列表用于“手动”创建 POJO)。
我怎样才能从内存中释放这些实体。当视图消失时,我希望它们成为 GC。但是 ViewScoped bean 本身不是 GC'd,有一堆实例留在内存中。
我应该寻找什么来释放这些对象?
- 是否@ManagedProperty
在 ViewScoped bean 中使用 a 来获取 SessionScoped bean 的实例,从而阻止 ViewScoped 被 GC?
- 我应该寻找其他错误吗?
我确实检查了有关 JSF 最佳实践和性能指南的其他线程,但它没有帮助。