【问题标题】:Java Collections and Garbage CollectorJava 集合和垃圾收集器
【发布时间】:2011-01-06 09:41:19
【问题描述】:

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

假设我有一个 List<Rubrique> listRubriques 和十个 Rubrique 对象。

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

如果我这样做,内存中究竟会发生什么:

listRubriques.clear(); listRubriques = null;

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

编辑:现在假设我的 Client 对象包含 List<Client>。因此,我的对象之间有一种循环引用。如果我的listRubrique 设置为null 会发生什么?这一次,我的观点是我的 Client 对象将变得“无法访问”并且可能造成内存泄漏?

【问题讨论】:

    标签: java performance collections garbage-collection


    【解决方案1】:

    Java 的实际 Sun 实现会不时复制所有引用/活动对象。从中复制的空间可以再次用于内存分配。

    也就是说,您的示例实际上会损害性能。 listRubriques.clear() 是不需要的(除非您在其他地方持有对它的引用),因为 listRubrique 引用的所有内容在不再引用 listRubriques 时都是垃圾。 listRubriques = null 如果变量 listRubriques 之后超出范围(可能是因为它是一个局部变量并且方法在这里结束),也可能不需要。

    不仅不需要对 clear 的调用,因为 clear 会访问后来不再使用的对象的内存,对象被访问并且现代处理器会将其放入缓存中。因此,一个死对象会显式进入处理器缓存 - 一些可能更有用的数据将被该操作覆盖。

    This article 是获取有关 Java 垃圾收集器的更多信息的好参考。

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

    【讨论】:

    • 小心:您的第一句话描述了 Sun JVM 的当前实现。一般而言,这不是关于 Java 的陈述。不过,其余的答案似乎普遍适用。
    • 谢谢提示,第一句我改了。
    【解决方案2】:

    如果你有:

    listRubriques = null;
    

    并且没有其他对象持有对listRubriques 或其包含对象的引用,它有资格进行垃圾回收。但是不能保证 JVM 何时会在其上实际运行垃圾收集并释放内存。您可以致电:

    System.gc();
    

    向JVM推荐你认为此时运行垃圾收集是个好主意。但即便如此,也无法保证。

    还有

    listRubriques.clear();
    

    在将listRubriques 设置为null 之前是不必要的。

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

    【讨论】:

    • 关于 null 之前的清除,我这样做只是为了确保我的对象不再有任何引用。但我没关系,这是一个无用的双重检查。我读了那个 System.gc();在旧的 Java 版本中很有用,但现在完全被忽略了,不是吗?
    • @Anth0:我还没有听说过,Java 6 的 J2SE 文档也没有提到类似的东西 (java.sun.com/javase/6/docs/api/java/lang/System.html#gc())。为了获得最大的可移植性,最好不要对您的应用程序将在其上运行的 JVM 实现做出任何假设。查看 Sun 的这篇文章,了解有关垃圾收集的真相:java.sun.com/docs/books/performance/1st_edition/html/…
    • Invoking System.gc() 通常也由较新的 VM 授予,但不能保证。如果您请求 GC 运行,尽管这不是必需的,但您可能会产生性能问题而不是解决问题。
    • 非常感谢您的链接。我学到了很多东西!我要编辑我的问题:)
    • @Anth0:我已经更新了我的答案。不要担心循环引用。 JVM 一直以来都足够聪明,可以处理这种边缘情况。有关详细讨论,请阅读我在上面的评论中发布的关于垃圾收集的真相链接中的所有第 A.3.4 节。
    【解决方案3】:

    “很快”是一个相当模糊的规范,但垃圾收集器可能不像您预期​​的那样迅速。何时实际收集对象很大程度上取决于 GC 配置和您的服务器负载,但这可能需要一些时间,如果您的 VM 有足够的可用堆和其他事情要做,则不会“很快”收集对象.

    VM 规范保证 GC 将运行的唯一情况是,在某些时候会抛出 OutOfMemoryError。在抛出 OutOfMemoryError 之前,VM 有义务尝试至少收集这么多符合条件的对象实例,以便相关的内存分配请求能够成功(但您仍然不能保证 所有 符合条件的实例被收集)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-07
      • 1970-01-01
      • 1970-01-01
      • 2010-12-13
      • 1970-01-01
      • 2015-07-10
      • 2010-10-04
      • 1970-01-01
      相关资源
      最近更新 更多