Утечка памяти с компонентом ViewScoped?
В нашем проекте JavaEE6 (EJB3, JSF2) на JBoss 7.1.1 кажется, что у нас есть утечка памяти с SeamFaces @ViewScoped
,
Мы сделали небольшой прототип, чтобы проверить факт:
- мы используем JMeter для вызова страницы 200 раз;
- страница содержит и вызывает bean-объект видимости, который внедряет EJB с состоянием;
- мы фиксируем время ожидания сеанса на 1 минуту.
В конце теста мы проверяем содержимое памяти с помощью VisualVM, и вот что мы получили:
- с
@ViewScoped
боб, мы все еще получаем 200 экземпляровMyController
- и@PreDestroy
метод никогда не вызывается; - с
@ConversationScoped
фасоль,@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>
Теперь включенный боб 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;
}
}
А затем вводится боб с состоянием MyController
:
@Stateful
@LocalBean
public class MyController
{
public void call()
{
System.out.println("call ");
}
@PreDestroy
public void destroy()
{
System.out.println("Destroy");
}
}
3 ответа
Я вижу, что многие разработчики довольны @ViewAccessScoped в Myface CODI. Не могли бы вы попробовать и рассказать об этом.
Я столкнулся с вышеупомянутой проблемой в управляемом JSF bean-компоненте @ViewScoped. Обратившись к нескольким блогам, я понял, что JSF сохраняет состояния bean-компонентов вида в сеансе http и уничтожается только при недействительности сеанса. Всякий раз, когда мы нажимаем на страницу jsf каждый раз, когда создается новый компонент области видимости, указанный на странице. Я работал вокруг с использованием Spring Custom View Scope. Работает нормально. Ниже приведен подробный код.
Для JSF 2.1:
Шаг 1. Создайте прослушиватель постконструктивного объекта View Scope Bean следующим образом.
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());
}
}
}
Шаг 2: Зарегистрируйте прослушиватель событий в 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>
Шаг 3. Создайте bean-компонент Custom View Scope следующим образом.
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 сохраняет в качестве ключа карты навигации с навигацией в сеансе http в файле com.Sun.faces.application.view.activeViewMaps. Поэтому добавьте приведенный ниже код в 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;
}
}
Скорее всего, это ошибка. Честно говоря, реализация Seam 3 была не так уж хороша, а реализация CODI (а также то, что будет в DeltaSpike) намного лучше.