Jsf 2 在Glassfish中创建多余的SFSB,从而泄漏内存

Jsf 2 在Glassfish中创建多余的SFSB,从而泄漏内存,jsf-2,java-ee-6,glassfish-3,stateful-session-bean,Jsf 2,Java Ee 6,Glassfish 3,Stateful Session Bean,在Glassfish 3.1.1中,我试图将单个SFSB注入到一个可视范围的JSF支持bean中 我的日志显示了这一点,即创建了两个SFSB,但只注入了第二个: INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@f48cde0 INFO: constructed a new sfsb: com.exmaple.test.service.impl._TestSFSBImpl_

在Glassfish 3.1.1中,我试图将单个SFSB注入到一个可视范围的JSF支持bean中

我的日志显示了这一点,即创建了两个SFSB,但只注入了第二个:

INFO: constructed a new sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@f48cde0
INFO: constructed a new sfsb: com.exmaple.test.service.impl._TestSFSBImpl_Serializable@13dbf5ce
INFO: constructed a new view scoped bean: com.example.test.ui.TestViewScopedSFSB@4de6b191
稍后,当我离开并且ViewScope bean超出范围时,我在SFSB上调用remove(),因此我在日志中看到:

INFO: destroying view scoped bean: com.example.test.ui.TestViewScopedSFSB@4de6b191
INFO: removing sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@13dbf5ce
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@13dbf5ce
但是多余的第一个以@f48cde0结尾的,从来没有被注射过,所以我没有处理它的方法,它也从来没有被移除过。直到后来,当我关闭服务器时,我看到它被删除了

这是我的密码:

支持bean:

package com.example.test.ui;

import java.io.Serializable;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.AjaxBehaviorEvent;

import com.example.test.service.api.TestSFSB;

@ManagedBean(name = "testViewScopedSFSB")
@ViewScoped
public class TestViewScopedSFSB implements Serializable {

  private static final long serialVersionUID = 1L;
  private static final Logger LOGGER = Logger.getLogger(TestViewScopedSFSB.class.getCanonicalName());

  @EJB
  private TestSFSB testSFSB;

  @PostConstruct
  public void postConstruct() {
    LOGGER.info("constructed a new view scoped bean: " + this);
  }

  public int getNumClicks() {
    return testSFSB.getNumClicks();
  }

  public void clicked(AjaxBehaviorEvent event) {
    testSFSB.clicked();
  }

  @PreDestroy
  public void preDestroy() {
    LOGGER.info("destroying view scoped bean: " + this);
    testSFSB.remove();
  }

}
SFSB接口:

package com.example.test.service.api;

public interface TestSFSB {

  void clicked();

  int getNumClicks();

  void remove();

}
SFSB的实施:

package com.example.test.service.impl;

import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Local;
import javax.ejb.Remove;
import javax.ejb.Stateful;

import com.example.test.service.api.TestSFSB;

@Stateful
@Local(TestSFSB.class)
public class TestSFSBImpl implements TestSFSB {

  private static final long serialVersionUID = 1L;
  private static final Logger LOGGER = Logger.getLogger(TestSFSBImpl.class.getCanonicalName());

  int numClicks = 0;

  @PostConstruct
  public void postConstruct() {
    LOGGER.info("constructed a new sfsb: " + this);
  }

  @Override
  public void clicked() {
    numClicks++;
  }

  @Override
  public int getNumClicks() {
    return numClicks;
  }

  @Override
  @Remove
  public void remove() {
    LOGGER.info("removing sfsb: " + this);
  }

  @PreDestroy
  public void preDestroy() {
    LOGGER.info("destroying sfsb: " + this);
  }

}
最后是jsf页面:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<h:html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:c="http://java.sun.com/jsp/jstl/core"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">

<h:head>

</h:head>

<h:body>
    <h:form>
        <h:commandLink action="/public/publicResource.jsf" value="Home" />
        <h:panelGrid id="panel1">
            <h:commandButton value="Click me">
                <f:ajax event="click" listener="#{testViewScopedSFSB.clicked}"
                    render="panel1" />
            </h:commandButton>
            <h:outputText value="#{testViewScopedSFSB.numClicks}" />
        </h:panelGrid>
    </h:form>

</h:body>
</h:html>
最后,当我卸载应用程序时,请注意10个多余的SFSB最终被销毁:

INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@561b0019
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@5c8608b9
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@31a4ef37
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@69e3f60e
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@266c4c10
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@42650c3b
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@70879d38
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@49649260
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@1a722605
INFO: destroying sfsb: com.example.test.service.impl._TestSFSBImpl_Serializable@21da38c0

基于我的声誉,我相信这一行为可以保持100次点击,导致200个相同模式的bean。

这不是一个bug,而是一个特性。这是一个重要的问题。在JavaEE中,容器创建了一些不同事物的实例(如无状态bean、消息驱动bean、实体和SFSB),并对它们进行池和池处理,等等。让容器来做和处理这是使用JavaEE的原因之一。您可以在大多数容器中配置此行为

从Glassfish文档中:

package com.example.test.service.impl;

import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Local;
import javax.ejb.Remove;
import javax.ejb.Stateful;

import com.example.test.service.api.TestSFSB;

@Stateful
@Local(TestSFSB.class)
public class TestSFSBImpl implements TestSFSB {

  private static final long serialVersionUID = 1L;
  private static final Logger LOGGER = Logger.getLogger(TestSFSBImpl.class.getCanonicalName());

  int numClicks = 0;

  @PostConstruct
  public void postConstruct() {
    LOGGER.info("constructed a new sfsb: " + this);
  }

  @Override
  public void clicked() {
    numClicks++;
  }

  @Override
  public int getNumClicks() {
    return numClicks;
  }

  @Override
  @Remove
  public void remove() {
    LOGGER.info("removing sfsb: " + this);
  }

  @PreDestroy
  public void preDestroy() {
    LOGGER.info("destroying sfsb: " + this);
  }

}
此外,GlassFish服务器还支持许多可调参数 可以控制“有状态”实例数的参数 (有状态会话bean和实体bean)以及 它们被缓存的持续时间

[……]

GlassFish服务器池最重要的参数之一是 稳定的池大小。当稳定池大小设置为大于 0时,容器不仅使用 指定数量的bean,但也尝试确保 空闲池中始终有可用的bean数。这确保了 有足够的bean处于就绪服务状态以进行处理 用户请求

()


如果您不希望出现这种行为,可以对其进行配置:阅读并遵循上面链接中的相应链接。

这不是错误,而是功能。这是一个重要的问题。在JavaEE中,容器创建了一些不同事物的实例(如无状态bean、消息驱动bean、实体和SFSB),并对它们进行池和池处理,等等。让容器来做和处理这是使用JavaEE的原因之一。您可以在大多数容器中配置此行为

从Glassfish文档中:

package com.example.test.service.impl;

import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Local;
import javax.ejb.Remove;
import javax.ejb.Stateful;

import com.example.test.service.api.TestSFSB;

@Stateful
@Local(TestSFSB.class)
public class TestSFSBImpl implements TestSFSB {

  private static final long serialVersionUID = 1L;
  private static final Logger LOGGER = Logger.getLogger(TestSFSBImpl.class.getCanonicalName());

  int numClicks = 0;

  @PostConstruct
  public void postConstruct() {
    LOGGER.info("constructed a new sfsb: " + this);
  }

  @Override
  public void clicked() {
    numClicks++;
  }

  @Override
  public int getNumClicks() {
    return numClicks;
  }

  @Override
  @Remove
  public void remove() {
    LOGGER.info("removing sfsb: " + this);
  }

  @PreDestroy
  public void preDestroy() {
    LOGGER.info("destroying sfsb: " + this);
  }

}
此外,GlassFish服务器还支持许多可调参数 可以控制“有状态”实例数的参数 (有状态会话bean和实体bean)以及 它们被缓存的持续时间

[……]

GlassFish服务器池最重要的参数之一是 稳定的池大小。当稳定池大小设置为大于 0时,容器不仅使用 指定数量的bean,但也尝试确保 空闲池中始终有可用的bean数。这确保了 有足够的bean处于就绪服务状态以进行处理 用户请求

()


如果您不希望出现这种行为,可以对其进行配置:阅读并遵循上面链接中的相应链接。

我认为这是不对的。有界缓存(对于有状态bean)不同于池(对于无状态bean)。有状态bean不能被重用。我看到的行为是每次加载页面时创建2个SFSB。如果我加载页面100次,就会看到创建了200个bean。这100颗“多余”的豆子再也不用了;他们又不是被添加到池中的。这显然是不可取的行为。确实,它们不是以与无状态相同的方式或出于相同的原因合并在一起的。然而,在这种情况下,它们被构造(对象初始化在java中是“昂贵的”)以满足第二次+并发的需要。如果200确实是在100个请求之后创建的,那么我们确实有一个问题(glassfish的默认配置)。我很抱歉,但我确实怀疑这是事实。我会更仔细地研究它-希望今晚(CET),如果我听起来很严厉,对不起。接受道歉!谢谢你抽出时间来看我。很可能是我的配置有问题;我以前和SLSB合作过,但我是SFSB(和JEE6)的新手。我认为这不对。有界缓存(对于有状态bean)不同于池(对于无状态bean)。有状态bean不能被重用。我看到的行为是每次加载页面时创建2个SFSB。如果我加载页面100次,就会看到创建了200个bean。这100颗“多余”的豆子再也不用了;他们又不是被添加到池中的。这显然是不可取的行为。确实,它们不是以与无状态相同的方式或出于相同的原因合并在一起的。然而,在这种情况下,它们被构造(对象初始化在java中是“昂贵的”)以满足第二次+并发的需要。如果200确实是在100个请求之后创建的,那么我们确实有一个问题(glassfish的默认配置)。我很抱歉,但我确实怀疑这是事实。我会更仔细地研究它-希望今晚(CET),如果我听起来很严厉,对不起。接受道歉!谢谢你抽出时间来看我。很可能是我的配置有问题;我以前曾与SLSB合作过,但我是SFSB(和JEE6)的新手。