2012-08-29 13 views
6

W naszej JavaEE6 projektu (EJB3, JSF2) na JBoss 7.1.1, wydaje się, że mamy przeciek pamięci z SeamFaces @ViewScoped.Przeciek pamięci z ziarnami ViewScoped?

Zrobiliśmy mały prototyp, aby sprawdzić fakt:

  • używamy JMeter zadzwonić A Page 200 razy;
  • strona zawiera i wywołuje bean viewcoped, który wstrzykuje stanowy EJB;
  • poprawiamy limit czasu sesji na 1 minutę.

Pod koniec badania, możemy sprawdzić zawartość pamięci z VisualVM, a tutaj to, co mamy:

  • z @ViewScoped fasoli, wciąż dostać 200 wystąpień stanowej MyController - a metoda @PreDestroy nigdy nie jest wywoływana;
  • z @ConversationScoped bean, @preDestroy metoda nazywa się koniec sesji, a następnie mamy czyste pamięci.

Czy źle wykorzystujemy zasięg widzenia, czy jest to naprawdę błąd?


Oto strona 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> 

Teraz zawarte fasola myBean. W przypadku wersji @ConversationScoped wszystkie skomentowane części są odkomentowane.

@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; 
    } 
} 

A potem wstrzykuje Stateful fasola MyController:

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

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

Odpowiedz

5

Widzę wielu programistów zadowolonych z @ViewAccessScoped w Myface CODI. Czy możesz spróbować i powiedzieć o tym opinii.

+2

Próbowaliśmy z CODI i test poszło dobrze. Miły! –

3

Są szanse, to jest błąd. Szczerze mówiąc, implementacja Seam 3 nie była najlepsza, a CODI (i to, co będzie w DeltaSpike) było znacznie lepsze.

5

Napotkałem wyżej wymieniony problem w zarządzanym przez JSF komponencie bean @ViewScoped. Po przeczytaniu kilku blogów zrozumiałem, że JSF zapisuje stany bean bean w sesji http i zostaje zniszczony tylko wtedy, gdy sesja jest unieważniona. Ilekroć klikamy stronę jsf za każdym razem, gdy tworzony jest nowy komponent bean widoku, o którym mowa na stronie. Zrobiłem pracę przy użyciu Spring Custom View Scope. To działa dobrze. Poniżej znajduje się szczegółowy kod.

Dla JSF 2.1:

Krok 1: Tworzenie widoku Zakres Bean post Construct Listener następująco.

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()); 
      } 
     } 
    } 

Krok 2: Zarejestruj detektor zdarzeń w face-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> 

Krok 3: Utwórz fasolę Zakres niestandardowego widoku w następujący sposób.

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; 
      } 

    } 

Uwaga: Inne zastąpione metody mogą być puste.

Dla JSF 2.2:

JSF 2.2 zapisuje nawigacja Mapy Widok w sesji HTTP w 'com.Sun.faces.application.view.activeViewMaps' jako klucza. Dodaj poniższy kod w Spring Custom View Scope. Nie potrzebujemy słuchaczy, jak w 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; 
     } 
} 
+0

czy możesz podzielić się importem LRUMap i innymi w swoim kodzie? Istnieje wiele opcji importu do wyboru i obecnie nie działa. – user1746582

+0

Witam @Sathish Kumar, wiem W tym poście jest znacznie starszy. Ale będzie bardzo pomocne, jeśli poprowadzisz mnie z tym samym problemem, z którym się spotykam. Nie jestem w stanie zastąpić publicznego Object get (nazwa ciągu, ObjectFactory objectFactory). Otrzymuję metodę publiczną TypeVariable odnośnik (nazwa String). Używam JSF-API w wersji 2.1.3. Czy możesz mi pomóc? –

Powiązane problemy