当我在servlet中使用单例时,单例类创建一个ArrayList,如:
private static ArrayList<SessionData> mSessionData = new ArrayList<>();
当servlet最终被终止并且在没有收到更多请求的一段时间之后收集垃圾时,单例会通过保留对上面显示的ArrayList的引用来导致内存泄漏吗?通常情况下,当没有根引用时,会收集内容。但是单例没有根引用,因为它们可以在整个servlet中的任何地方引用,并且只在首次被调用时才被实例化。
答案 0 :(得分:2)
单例将保留在内存中,因此不会被垃圾回收。这是设计的。当应用程序作为单例的一部分运行时,您的列表也将保留在内存中。
虽然这不是内存泄漏!
计算机程序管理不正确时发生内存泄漏 内存分配
内存泄漏是指有多个对象累积时没有收集垃圾并填满内存。在你的情况下,你只有一个设计对象(单例),它不会占用比它实际使用的更多的内存 - 所以它不是泄漏,它正确分配内存。
如何使用数组列表管理内存取决于您。如果用对象填充它并且从不清除它们可能会导致内存泄漏,但这与单例无关。因此,如果您将SessionData对象保留在数组列表中,哪些引用已经关闭/非活动的会话,您应该从列表中清除它们(并让java最终GC它们)。通常在Web编程中,可以通过您使用的框架为您处理会话管理。
在servlet容器中,每个servlet都在不同的上下文中。这对于Web应用程序的安全性来说是主要的。因此,如果您在不同的应用程序/ servlet中使用该单例,它们将各自获得自己的实例。由于几个原因,封装非常重要。想象一下,您创建了一个单例类,并且恶意软件安装在同一个servlet容器中。如果他们实例化同一个类并且他们获得了您的实例,他们就能够读取您的数据。这不应该发生。因此,每个servlet实例都将拥有它自己的单例实例,当servlet消失时,单例实例将随之消失。
有关示例,您可以在此处查看https://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html
tomcat类加载的工作原理。我相信大多数servlet容器都是这样工作的。如果不同的类加载器加载它,Singleton可以有两个实例。所以在tomcat的情况下:
Bootstrap
|
System
|
Common
/ \
Webapp1 Webapp2 ...
将为每个webapp加载您的Singleton。实际上所有的网络应用都有自己的类加载器。如果你使用bootstrap类加载器中的单例类(例如java.lang.runtime或类似的东西)那么它将被共享。但是servlet容器可能会限制你使用它们。
通过类加载器的分离解决的另一个类似问题是静态值。如果在servlet中有一个带有公共静态值的类,则不同的servlet不能修改静态值,因为它们在分离的类加载器中工作。