Java集合和垃圾回收器

13

关于Java Web应用程序中的性能问题,有一个小问题。

假设我有一个包含十个Rubrique对象的List<Rubrique> listRubriques

Rubrique包含一个产品列表(List<product> listProducts)和一个客户列表(List<Client> listClients)。

如果我这样做会发生什么:

listRubriques.clear(); listRubriques = null;

我的观点是,由于listRubriques为空,我之前引用的所有对象(包括listProductslistClients)很快就会被垃圾回收。但由于Java中的Collection有些棘手,并且我的应用程序存在性能问题,所以我提出了这个问题 :)

编辑:现在假设我的Client对象包含一个List<Client>,因此我的对象之间存在一种循环引用。那么,如果将我的listRubrique设置为null,这时我的观点是我的Client对象将变得“不可访问”,可能会创建内存泄漏?

3个回答

15

Java的实际Sun实现会不时地复制所有引用/活动对象。然后可以重新使用已复制的空间进行内存分配。

也就是说,你的示例实际上会损害性能。 listRubriques.clear() 是不必要的(除非您在其他地方保留了对它的引用),因为 listRubrique 引用的所有内容在 listRubriques 不再被引用的那一刻就成为了垃圾。如果变量 listRubriques 后面不再使用(可能是因为它是局部变量并且方法在此处结束),listRubriques = null 也可能是不必要的。

不仅调用 clear 是不必要的,而且由于 clear 访问了之后不再使用的对象的内存,该对象会被访问并被现代处理器放入其缓存中。因此,一个无用的对象明确地进入了处理器缓存 - 一些可能更有用的数据将被覆盖以进行该操作。

这篇文章 是获取有关 Java 垃圾收集器更多信息的好参考资料。

编辑: 针对问题中的编辑做出回应:垃圾收集器(至少是 Sun 使用的实现)从一些根引用开始,并复制所有可以从这些引用到达且被复制对象引用的对象。因此,您的循环引用对象是垃圾,因为没有“外部”引用指向它们,内存将在垃圾回收中被回收。


请注意:你的第一个句子描述了Sun JVM的当前实现情况。这并不是一个普遍适用于Java的陈述。不过,答案的其余部分似乎是普遍适用的。 - Joachim Sauer
谢谢你的提示,我已经修改了第一句话。 - Mnementh

5

如果您拥有以下条件:

listRubriques = null;

如果没有其他对象引用listRubriques或其包含的对象,它就可以被垃圾回收。但是不能保证JVM何时实际运行垃圾回收并释放内存。您可以调用:

System.gc();

建议JVM在此时运行垃圾回收,但即使如此,也不能保证一定会运行。

同时还有

listRubriques.clear();

在设置listRubriquesnull之前并不是必要的。

编辑:回答你关于循环引用的问题,JVM足够聪明,可以确定整个对象图形与任何正在运行的代码都没有连接,并且JVM将正确地确定它们都符合垃圾回收的条件。即使在参考计数的旧时代,这也一直是正确的。现代JVM只是更快,更高效。但它们并没有比旧的JVM多回收任何对象。


关于在 null 之前进行清除,我这样做只是为了确保我的对象不再有任何引用。但我认为这是一个无用的双重检查,没必要。我读到 System.gc(); 在旧版 Java 中很有用,但现在完全被忽略了,是吗? - Anth0
@Anth0:我没有听说过这个,并且Java 6的J2SE文档中也没有提到类似的内容(http://java.sun.com/javase/6/docs/api/java/lang/System.html#gc())。为了最大的可移植性,最好不要对你的应用程序将在其上运行的JVM实现做任何假设。查看Sun的这篇文章以了解垃圾回收的真相:http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html - Asaph
调用 System.gc() 通常也会被较新的虚拟机所支持,但并不保证。如果你请求运行 GC,尽管这并非必要,你可能会创建一个性能问题而不是解决它。 - jarnbjo
非常感谢您提供的链接。我学到了很多!我将编辑我的问题 :) - Anth0
@Anth0:我已经更新了我的答案。不用担心循环引用的问题。JVM一直都足够聪明,能够处理这种边缘情况。如果想要详细讨论,请阅读我在上面评论中发布的垃圾回收真相链接中的A.3.4章节。 - Asaph

1
“很快”是一个相当模糊的规范,但垃圾收集器可能不像你期望的那样迅速。对象实际上何时被收集取决于GC配置和服务器负载,但这可能需要一些时间,如果您的VM有足够的堆可用和其他事情要做,对象将不会很快被收集。
唯一保证GC运行的VM规范的情况是,在某些情况下,否则将抛出OutOfMemoryError。在抛出OutOfMemoryError之前,VM有义务尝试至少收集足够数量的合格对象实例,以便相关的内存分配请求可以成功(但仍无法保证收集所有合格实例)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接