【问题标题】:Memory leak with ViewScoped bean?ViewScoped bean 的内存泄漏?
【发布时间】:2012-08-24 08:28:32
【问题描述】:

JBoss 7.1.1 上的 JavaEE6 项目(EJB3JSF2)中,我们似乎有SeamFaces @ViewScoped 的内存泄漏。

我们制作了一个小原型来验证事实:

  • 我们使用 JMeter 调用一个页面 200 次;
  • 该页面包含并调用一个视图范围的 bean,该 bean 注入一个有状态的 EJB;
  • 我们将会话超时时间固定为 1 分钟。

在测试结束时,我们用 VisualVM 检查内存的内容,我们得到了什么:

  • 使用 @ViewScoped bean,我们仍然获得 200 个有状态的 MyController 实例 - 并且永远不会调用 @PreDestroy 方法;
  • 使用@ConversationScoped bean,@preDestroy 方法被称为会话结束,然后我们得到一个干净的内存。

我们是否错误地使用了视图范围,或者它真的是一个错误?


这是 XHTML 页面:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
   xmlns:f="http://java.sun.com/jsf/core"
   xmlns:h="http://java.sun.com/jsf/html"
   xmlns:ui="http://java.sun.com/jsf/facelets"   
   xmlns:s="http://jboss.org/seam/faces">
   <f:metadata>
        <f:viewParam name="u" value="#{myBean.uselessParam}" />
        <s:viewAction action="#{myBean.callService}" />
   </f:metadata>
   <h:body >
        <f:view>
        </f:view>
   </h:body>    
</html>

现在包含 bean myBean。对于@ConversationScoped 变体,所有已注释的部分均未注释。

@ViewScoped
// @ConversationScoped
@Named
public class MyBean implements Serializable 
{
    @Inject
    MyController myController;
    //@Inject
    //Conversation conversation;

    private String uselessParam;

    public void callService()
    {
        //if(conversation.isTransient())
        //{
        //            conversation.begin();
        //}
        myController.call();
    }

    public String getUselessParam() 
    {
        return uselessParam;
    }

    public void setUselessParam(String uselessParam) 
    {
        this.uselessParam = uselessParam;
    }
}

然后是注入的有状态beanMyController

@Stateful
@LocalBean
public class MyController
{
   public void call()
   {
         System.out.println("call ");
   }

   @PreDestroy
   public void destroy()
   {
         System.out.println("Destroy");
   }
}

【问题讨论】:

    标签: jsf-2 memory-leaks cdi seam3 view-scope


    【解决方案1】:

    我看到许多开发人员对 Myface CODI 中的 @ViewAccessScoped 感到满意。 请您试一试并告诉反馈。

    【讨论】:

      【解决方案2】:

      我在 JSF 管理的 @ViewScoped bean 中遇到了上述问题。在参考了几篇博客后,我了解到 JSF 将视图 bean 状态保存在 http 会话中,并且只有在会话无效时才会被销毁。每当我们在每次创建页面中引用的新视图范围 bean 时单击 jsf 页面。我做了一个使用 Spring Custom View Scope 的工作。它工作正常。下面是详细代码。

      对于 JSF 2.1:

      第 1 步:创建一个 View Scope Bean Post Construct Listener,如下所示。

          public class ViewScopeBeanConstructListener implements ViewMapListener {
      
           @SuppressWarnings("unchecked")
           @Override
           public void processEvent(SystemEvent event) throws AbortProcessingException {
              if (event instanceof PostConstructViewMapEvent) {
                  PostConstructViewMapEvent viewMapEvent = (PostConstructViewMapEvent) event;
                  UIViewRoot viewRoot = (UIViewRoot) viewMapEvent.getComponent();
                  List<Map<String, Object>> activeViews = (List<Map<String, Object>>) 
                       FacesContext.getCurrentInstance().getExternalContext().getSessionMap(). get("com.org.jsf.activeViewMaps");
                  if (activeViews == null) {
                      activeViews = new ArrayList<Map<String, Object>>();
                      activeViews.add(viewRoot.getViewMap());
                      FacesContext.getCurrentInstance().getExternalContext().getSessionMap(). put("com.org.jsf.activeViewMaps", activeViews);
                  } else {
                      activeViews.add(viewRoot.getViewMap());
                  }
              }
          }
      

      第二步:在 faces-config.xml 中注册事件监听器

      <system-event-listener>
          <system-event-listener-class>
               com.org.framework.custom.scope.ViewScopeBeanConstructListener
          </system-event-listener-class>
         <system-event-class>javax.faces.event.PostConstructViewMapEvent</system-event-class>
          <source-class>javax.faces.component.UIViewRoot</source-class>
      </system-event-listener>
      

      第 3 步:创建自定义视图范围 bean,如下所示。

       public class ViewScope implements Scope {
      
          @Override
          public Object get(String name, ObjectFactory objectFactory) {
              Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
                  if (viewMap.containsKey(name)) {
                          return viewMap.get(name);
                  } else {
                       List<Map<String, Object>> activeViewMaps = (List<Map<String, Object>>)
                       FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("com.org.jsf.activeViewMaps");
                       if (activeViewMaps != null && !activeViewMaps.isEmpty() 
                           && activeViewMaps.size() > 1) {
                              Iterator iterator = activeViewMaps.iterator();
                           if (iterator.hasNext()) {
                                   Map<String, Object> oldViewMap = (Map<String, Object>)
                                   iterator.next();
                                   oldViewMap.clear();
                                   iterator.remove();
                           }
                        }
                      Object object = objectFactory.getObject();
                      viewMap.put(name, object);
                      return object;
                 }
      
          }
      

      注意:其他被覆盖的方法可以为空。

      对于 JSF 2.2:

      JSF 2.2 将“com.Sun.faces.application.view.activeViewMaps”中 http 会话中的导航视图映射保存为键。因此,在 Spring Custom View Scope 中添加以下代码。不需要像 JSF 2.1 中那样的监听器

      public class ViewScope implements Scope {
      
          public Object get(String name, ObjectFactory objectFactory) {
               Map<String, Object> viewMap =  
                 FacesContext.getCurrentInstance().getViewRoot().getViewMap();
                    if (viewMap.containsKey(name)) {
                           return viewMap.get(name);
                    } else {
                             LRUMap lruMap = (LRUMap) FacesContext.getCurrentInstance().
           getExternalContext().getSessionMap().get("com.sun.faces.application.view.activeViewMaps");
                      if (lruMap != null && !lruMap.isEmpty() && lruMap.size() > 1) {
                         Iterator itr = lruMap.entrySet().iterator();
                         while (itr.hasNext()) {//Not req
                           Entry entry = (Entry) itr.next();
                           Map<String, Object> map = (Map<String, Object>) entry.getValue();
                           map.clear();
                           itr.remove();
                           break;
                         }
                      }
                      Object object = objectFactory.getObject();
                      viewMap.put(name, object);
                      return object;
              }
       }
      

      【讨论】:

      • 你能在你的代码中分享 LRUMap 和其他人的导入吗?有很多导入选项可供选择,目前都失败了。
      • 嗨@Sathish Kumar,我知道这篇文章已经很老了。但是,如果您指导我解决我面临的同样问题,那将非常有帮助。我无法覆盖 public Object get(String name, ObjectFactory objectFactory)。我得到的方法是 public TypeVariable> 查找(字符串名称)。我正在使用 JSF-API 版本 2.1.3。你能帮帮我吗?
      【解决方案3】:

      这很可能是一个错误。老实说,Seam 3 的实施并不是那么好,而 CODI 的实施(以及 DeltaSpike 中的实施)要好得多。

      【讨论】:

        猜你喜欢
        • 2012-10-17
        • 1970-01-01
        • 2014-02-09
        • 1970-01-01
        • 1970-01-01
        • 2012-07-06
        • 1970-01-01
        • 1970-01-01
        • 2019-07-05
        相关资源
        最近更新 更多