jsf-2 memory-leaks cdi seam3 view-scope

jsf 2 - ¿Fuga de memoria con ViewScoped Bean?



jsf-2 memory-leaks (3)

En nuestro proyecto JavaEE6 ( EJB3 , JSF2 ) en JBoss 7.1.1 , parece que tenemos una pérdida de memoria con SeamFaces @ViewScoped .

Hicimos un pequeño prototipo para verificar el hecho:

  • usamos JMeter para llamar a una página 200 veces;
  • la página contiene y llama a un bean viewscoped que inyecta un EJB con estado;
  • arreglamos el tiempo de espera de la sesión en 1 minuto.

Al final de la prueba, verificamos el contenido de la memoria con VisualVM, y aquí tenemos lo que obtuvimos:

  • con un bean @ViewScoped , todavía obtenemos 200 instancias del MyController estado, y nunca se llama al método @PreDestroy ;
  • con un @ConversationScoped bean, el método @preDestroy se llama el final de la sesión y luego tenemos una memoria limpia.

¿Utilizamos mal el alcance de la vista, o es realmente un error?

Aquí está la página 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>

Ahora el bean incluido myBean . Para la variante @ConversationScoped , todas las partes comentadas no están comentadas.

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

Y luego el MyController inyectado con estado MyController :

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


Es probable que esto sea un error. Honestamente, la implementación de Seam 3 no fue tan buena y la de CODI (y también lo que será en DeltaSpike) es mucho mejor.


Me he enfrentado al problema mencionado anteriormente en JSF managed @ViewScoped bean. Después de referirme a algunos blogs, entendí que JSF guarda los estados de bean en una sesión http y se destruye solo cuando la sesión se invalida. Cada vez que hacemos clic en la página jsf cada vez que se crea un bean de ámbito de vista nuevo referido en la página. Hice un trabajo con Spring Custom View Scope. Funciona bien. A continuación está el código de detalle.

Para JSF 2.1:

Paso 1: cree un oyente de construcción de postes Bean de View View de la siguiente manera.

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

Paso 2: Registrar el detector de eventos en 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>

Paso 3: cree un bean Scope de vista personalizado de la siguiente manera.

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: Otros métodos anulados pueden estar vacíos.

Para JSF 2.2:

JSF 2.2 guarda los mapas de vista navegados en la sesión http en ''com.Sun.faces.application.view.activeViewMaps'' como clave. Por lo tanto, agregue el código a continuación en Spring Custom View Scope. No hay necesidad de oyentes como en 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; } }


Veo que muchos desarrolladores están satisfechos con @ViewAccessScoped en Myface CODI. ¿Podría intentarlo y contar los comentarios?