2012-08-29 14 views
6

Nel nostro JavaEE6 progetto (EJB3, JSF2) su JBoss 7.1.1, sembra che abbiamo una perdita di memoria con SeamFaces @ViewScoped.Perdita di memoria con bean ViewScoped?

Abbiamo fatto un po 'il prototipo per verificare il fatto:

  • usiamo JMeter per chiamare una pagina di 200 volte;
  • la pagina contiene e chiama un bean a pila di viste che inietta un EJB con stato;
  • fissiamo il timeout della sessione a 1 minuto.

Alla fine del test, controlliamo il contenuto della memoria con VisualVM, ed ecco quello che abbiamo ottenuto:

  • con un fagiolo @ViewScoped, ancora otteniamo 200 istanze del stateful MyController - e il metodo @PreDestroy non viene mai chiamato;
  • con un bean @ConversationScoped, il metodo @preDestroy si chiama un fine sessione e quindi abbiamo una memoria pulita.

Usiamo male l'ambito di visualizzazione o è davvero un bug?


Ecco la pagina 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> 

Ora il fagiolo incluso myBean. Per la variante @ConversationScoped, tutte le parti commentate non sono commentate.

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

E poi il fagiolo stateful iniettato MyController:

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

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

risposta

5

Vedo molti sviluppatori soddisfatti di @ViewAccessScoped in Myface CODI. La prego di fare un tentativo e di dire il feedback.

+2

Abbiamo provato con CODI e il test è andato bene. Bello! –

3

È probabile che questo problema si verifica. Sinceramente l'implementazione di Seam 3 non è stata eccezionale e il CODI (e anche quello che sarà in DeltaSpike) è molto meglio.

5

Ho affrontato il problema sopra menzionato in JSF gestito da @ViewScoped bean. Dopo aver fatto riferimento a pochi blog, ho capito che JSF salva gli stati dei bean di visualizzazione nella sessione http e viene distrutto solo quando la sessione viene invalidata. Ogni volta che clicchiamo sulla pagina jsf ogni volta che viene creato un nuovo scope scope di riferimento nella pagina. Ho fatto un lavoro con Spring Custom View Scope. Funziona bene. Di seguito è riportato il codice di dettaglio.

Per JSF 2.1:

Passaggio 1: Creare una vista oscilloscopio Bean Messaggio Construct Listener come segue.

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

Passaggio 2: registrare il listener di eventi in 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> 

Passaggio 3: Creare un bean di ambito Vista personalizzata come segue.

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

    } 

Nota: altri metodi sovrascritti possono essere vuoti.

Per JSF 2.2:

JSF 2.2 salva le mappe dall'alto in una navigazione nella sessione HTTP in 'com.Sun.faces.application.view.activeViewMaps' come chiave. Quindi aggiungi il codice seguente in Spring Custom View Scope. Non c'è bisogno di ascoltatori come in 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

puoi condividere le importazioni per LRUMap e altre nel tuo codice? Ci sono molte opzioni di importazione tra cui scegliere e al momento non funziona. – user1746582

+0

Ciao @Sathish Kumar, lo so È molto più vecchio di questo post. Ma sarà davvero utile se mi guidi con lo stesso problema che sto affrontando. Non riesco a sovrascrivere l'oggetto pubblico get (nome stringa, ObjectFactory objectFactory). Sto ottenendo il metodo è la ricerca pubblica di TypeVariable (nome di stringa). Sto usando la versione JSF-API 2.1.3. Potresti per favore darmi una mano. –