Jakarta ee 在WildFly中取消部署时是否通常卸载静态字段?

Jakarta ee 在WildFly中取消部署时是否通常卸载静态字段?,jakarta-ee,ejb,wildfly,classloader,Jakarta Ee,Ejb,Wildfly,Classloader,我正在使用WildFly 11 Final,并创建了以下EJB: @Singleton @Startup public class MyDebug { private static final MyStaticSingleton myStaticSingleton = new MyStaticSingleton(); } 现在,如果我重新部署应用程序并通过JVisualVm查看堆,那么每次重新部署都会看到MyStaticSingleton的一个实例。 MyStaticSingleton

我正在使用WildFly 11 Final,并创建了以下EJB:

@Singleton
@Startup
public class MyDebug {
    private static final MyStaticSingleton myStaticSingleton = new MyStaticSingleton();
}
现在,如果我重新部署应用程序并通过JVisualVm查看堆,那么每次重新部署都会看到MyStaticSingleton的一个实例。 MyStaticSingleton的实例由不同的类装入器引用


取消部署应用程序后,JavaEE应用程序的类加载器不会被丢弃,这是正常行为吗?

很可能是由于应用程序的某些错误行为导致了泄漏。 事实上,这是一个相当普遍的问题

主要的问题不是单例实例数量的增加,而是类加载器数量的增加,因此加载的类的数量(最终可能是巨大的)。通过javacore转储,您可以很容易地证明这一点

要解决这个问题,您必须找到一个对象来防止这些类加载器中的每一个都被丢弃,因此所有加载的类都被丢弃

EE应用程序这种错误行为的一个众所周知的例子是使用log4j启用关闭挂钩(默认情况下启用)

更新

仅需确认,给定示例中的静态引用对象不会导致上述类加载器/本机内存泄漏。 此外,由于静态字段声明为final,因此它符合EJB规范

正如TS所提到的,主要的嫌疑犯是连接池,它通过运行自己的线程违反了EJB规范

在EE环境中使用线程,特别是与上下文类加载器一起使用,是此类泄漏的典型原因。

EJB3.2规范(16.2.2)明确指出:您不能这样做

企业bean不能使用读/写静态字段。允许使用只读静态字段。因此,建议将企业bean类中的所有静态字段声明为final

只需为您的单例使用一个常规字段,并将创建移动到
@PostConstruct

如果单例也是管理的,那么在这个字段上使用注入。

我很好奇为什么要在@singleton中添加一个静态属性。由于您的封闭实例是作为单例管理的,所以您“应该”能够将其标记为私有(如果确实需要,则标记为final)。您的静态引用可能会导致封闭的“previous”实例无法被垃圾收集(即使ejb容器不再管理它)。通过这种方式,我确保在部署时正确加载该类,以便在连续几次重新部署后立即检查堆。非常感谢。我对log4j的例子很好奇,你知道它是否有详细的解释吗?关于类加载器的根,是的,我做了一些核心转储并检查了它们。保存过时类加载器的一个根来自C3P0,它是hibernate中的连接池提供程序:ThreadPoolAsynchronousRunner$PoolThread,它扩展了线程。这是由Hibernate/C3P0完成的,我不知道如何停止这种行为。请记住,无状态EJB中的非托管实例可能是一个问题(singleton和stateful可以避免这种情况,因为它们具有不同的生命周期)。通常,除了在“业务方法”的上下文中,您不会在EJB中实例化任何内容,因此它的引用是短暂的。这也是因为您没有得到对ejb的具体引用,只有一个代理充当底层bean池的门面,容器认为合适时,任何bean池都可以被编组以响应业务方法执行。@Thomas,我说得对吗,您没有使用EE server提供的数据源,但是在您的应用程序中创建一个池吗?@user3714601是的,您是正确的,我没有使用EE服务器提供的数据源。我正在使用Hibernate/C3P0,但在我的应用程序中创建了一些应用程序管理的EntityRangerFactory。我已经在这里问过这个问题:没有比为每个用户创建EntityRangerFactory更好的主意了。我预计最多100个用户,所以我想这仍然是可行的。如果你有更好的办法,我很乐意听你说。