Jsf 2 在Glassfish中创建多余的SFSB,从而泄漏内存
在Glassfish 3.1.1中,我试图将单个SFSB注入到一个可视范围的JSF支持bean中 我的日志显示了这一点,即创建了两个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_
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)的新手。