【问题标题】:Java GC: why two survivor regions?Java GC:为什么有两个幸存者区域?
【发布时间】:2012-05-28 13:07:08
【问题描述】:

对于 Sun/Oracle 的 JVM,我读到 GC 算法将新一代划分为一个 Eden 区域和两个幸存者区域。我想知道的是,为什么有两个幸存者区域而不是一个?该算法可以在伊甸园和一个幸存者区域之间保持乒乓球(就像它目前在两个幸存者区域之间所做的那样);或者这种方法有什么缺点吗?

【问题讨论】:

  • 有趣 - 您介意将链接发布到您读到此内容的地方吗?我认为这将有助于这个问题。 编辑: this article 似乎在描述它,不确定它是否与您阅读的相同。
  • Paul Bellora:不是说这个链接,但我在多本书和文章中读过它。很快就会发布这些链接。
  • @Paul Bellora 这是针对 Java 5 的,但它确实提到了“to”和“from”幸存空间:oracle.com/technetwork/java/javase/…
  • 哦,太棒了,感谢您的链接 - 很好的问题!

标签: java garbage-collection jvm


【解决方案1】:

我相信 JRockit 的 GC 实现更像你建议的那样工作,只有一个伊甸园和一个幸存者空间,但不要引用我的话。

HotSpot JVM 有两个幸存者空间的原因是为了减少处理碎片的需要。新对象分配在伊甸园空间中。一切都很好。当它满了时,你需要一个 GC,所以杀死陈旧的对象并将活的对象移动到幸存者空间,在那里它们可以成熟一段时间,然后再被提升到老年代。到目前为止还是不错的。但是,下次我们用完伊甸园空间时,我们遇到了一个难题。下一次 GC 出现并清除了 eden 和我们的幸存者空间中的一些空间,但这些空间并不连续。那么是不是更好

  1. 尝试将来自伊甸园的幸存者放入 GC 清除的幸存者空间中的孔中?
  2. 将幸存者空间中的所有对象向下移动以消除碎片,然后然后将幸存者移入其中?
  3. 只需说“去死吧,无论如何我们都在移动所有东西”,然后将所有幸存者从两个空间复制到一个完全独立的空间——第二个幸存者空间——从而为您留下一个干净的伊甸园和幸存者空间在哪里可以在下一次 GC 上重复该序列?

孙对这个问题的回答是显而易见的。

【讨论】:

  • 这对我来说并不明显。 HotSpot JVM 遵循什么替代方案?
  • 感谢您提供非常清晰准确的答案。如果可能的话,你也可以吗?评论 JRockit 使用哪个选项?
  • 我发现这个答案不正确 - 您描述的复制算法不正确。 Copying GC 将所有对象从“from”空间复制到“to”空间。之后,两个空间切换角色。也就是说,新对象的分配将始终发生在“from”空间(而“to”空间始终为空),因此不会出现碎片问题。更好的解释是使用 3 个空格以更有效的方式利用内存,如下所述:stackoverflow.com/questions/21476348/…
  • 有人可以帮忙解决选项二 (2) 吗? 这里的“向下”和整行是什么意思。 "将幸存者空间中的所有对象向下移动以消除碎片"
  • @Vikash。这基本上意味着 GC 将遵循 Mark、Sweep 和 Compact 规则来克服 Fragmentation。在紧凑阶段,所有活动对象都移向幸存者内存的开头,删除幸存者内存之间的非活动对象。
【解决方案2】:

在一次次要垃圾回收操作后,两个幸存者空间的角色被颠倒了

两个幸存者空间。这些保存的对象至少在一次次要垃圾回收中幸存下来,但在被提升到老年代之前又被赋予了另一个机会变得不可访问。其中只有一个持有对象,而另一个大部分时间 未使用。

在一次次要垃圾回收操作期间,已发现为垃圾的对象将被标记。伊甸园中在集合中幸存下来的活动对象被复制到未使用的幸存者空间。幸存者空间中正在使用的活动对象,将在年轻代中再次有机会被回收,也被复制到未使用的幸存者空间。最后,幸存者空间中正在使用的活动对象被视为 “足够老”,被提升为老一代。

在次要垃圾回收结束时,两个幸存者空间交换角色。伊甸园完全是空的;只有一个幸存者空间正在使用中;并且老一代的入住率略有增长。由于活动对象在其运行过程中被复制,这种类型的垃圾收集器称为复制垃圾收集器。

来源:以上是 Java Performance 第 83 页的摘录,作者为 Charlie Hunt 和 Binu John。

【讨论】:

    【解决方案3】:

    年轻一代:是短期居住的地方,分为两个空间:

    伊甸园空间: 新对象将被分配到内存池中。假设大多数对象在创建后不久就会被取消引用并变得无法访问。未被取消引用的对象将被新一代垃圾收集器复制到幸存者空间中。在某些特殊情况下,它们可能会被直接复制到老年代池中。

    幸存者空间:这两个小空间保存年轻代垃圾收集的幸存对象。幸存的对象将从一个幸存者复制到另一个幸存者(少量)次。这允许收获我们更多取消引用的对象。

    老年代:最大的内存池,应该保存长寿命的对象。一旦离开幸存者空间,对象就会被复制到这个池中。

    永久生成:这个相当未知的池保存所有类的信息。对于大多数应用程序来说,它不需要任何关注。它可能需要适应一些具有许多类的应用程序。如果应用程序永久加载和卸载类,也可能需要注意。

    其他优点:

    • 内存碎片
    • 它提高了 GC 性能

    请点击以下链接了解更多详情,帮助您了解更多

    http://www.scalingbits.com/javaprimer

    http://java.sys-con.com/node/84695

    【讨论】:

      【解决方案4】:

      目前所有的答案都在谈论内存碎片,这也是在 GC 中产生世代的另一个原因。

      运行时记录所有指向“新对象”的“旧对象”,每次更新“指针”字段时都会这样做。然后当“次要”GC 完成时,只需要扫描“新”对象。

      多年来发现只有“新”和“旧”是不够的,有一个“中年”的第三代就好了。

      【讨论】:

        【解决方案5】:

        将一代的所有实例从一个空间复制到另一个空间,而不是按内存地址顺序将它们复制到一代空间的开头,有什么优点和缺点?按顺序处理项目可能需要为每个项目添加一个额外的指针,但会消除对“幸存者”空间之一的需要。

        【讨论】:

          【解决方案6】:

          两个幸存者是标记和复制算法的实现。这些在GC中用于年轻一代。正如 Ryan 在选项 3 中提到的那样 here

          【讨论】:

          【解决方案7】:

          Java 中的堆内存 Java对象创建在一个称为堆内存的区域。堆内存是在JVM启动时创建的,当Java应用程序运行时会增加或减少堆内存。当堆内存满时,垃圾收集器会移除未使用的对象,从而为新对象腾出空间。

          堆内存分为两个区域(或代)称为

          1.年轻的空间。 2.旧空间。

          1.在young space中,有新Object的Eden空间和两个Survivor Spaces(from和to),这两个Survivor Spaces的大小总是一样的。

          2.Survivor Spaces 用于存储存活对象。当年轻空间满时,垃圾收集器通过运行一个特殊的年轻收集来移除未使用的对象,其中所有在年轻空间中存活足够长的对象都会被提升(移动) 到旧空间,从而释放年轻空间以分配更多对象。

          3.如果 Eden 空间已满,GC 将运行,如果该 Eden 空间中存在任何对象,则将其移动到 Survivor Space。

          4.在young space中,GC一般使用Copying Algorithm,速度快,每次将生存对象复制到Survivor Space之一。

          5.如果Survivor Space满了,剩下的live Objects会直接复制到Old space。

          6.在Old space,GC一般使用Mark-Compact算法,速度慢但占用内存少。

          7.当old space变满时,垃圾会在那里收集,一个称为old collection的过程。在Old space,长寿命的对象会留在那儿。

          8.Out of Memory 会发生新对象没有空间,即使 GC 为 OLD 或 Perm 部分完成。

          9.垃圾收集期间对象被移动:eden ->survivor ->tenured(old space)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-10-06
            • 2013-08-01
            • 2011-04-06
            • 2021-02-12
            • 2019-01-12
            • 1970-01-01
            相关资源
            最近更新 更多