好吧,经过大量的搜索和研究,我了解到如下,
-XX:+UseParallelGC - 这使得GC能够在年轻一代中使用多个线程,但对于旧代/终身制代,仍然使用串行标记和紧凑算法。
-XX:+UseParallelOldGC - 这使GC能够在旧代/终身制一代中使用并行标记和压缩算法。
让我们来了解一下——
在年轻一代中起作用的算法和内存安排,例如标记和复制,交换空间,由于许多原因,不适用于老一代
低死亡率 -在老一代,“死亡率”明显低于年轻一代。在典型的Java应用程序中,大多数对象死得很快,很少有对象存活得更长。作为在年轻一代中生存并晋升为老一代的物体,观察到这些物体往往寿命更长。与年轻一代相比,这导致老一辈人的死亡率非常低。
显著尺寸 -老一代明显大于年轻一代。由于年轻一代很快清理干净,因此可用于许多短命物体(小型年轻一代)的空间相对较小。在旧一代中,对象会随着时间的推移而积累。因此,老一辈人的空间肯定比年轻一代(老一辈人)大得多。
分配很少 -在老一代,分配比年轻一代少。这是因为在旧一代中,只有当垃圾回收器将幸存的对象从年轻一代提升到老一代时,才会出现对象。另一方面,在年轻一代中,应用程序用新生成的所有对象(即大部分分配)都发生在年轻一代中。
考虑到这些差异,已经为年轻一代选择了一种算法,该算法将尽快完成垃圾收集,因为死亡率高[点(1)],因此必须经常调用它。此外,该算法必须确保尽可能有效地分配内存[点(3)],因为在年轻一代中分配了很多东西。年轻一代的标记和复制算法具有这些属性。
另一方面,这种算法在老一代没有意义。情况有所不同:垃圾收集器必须照顾旧一代中的许多对象[第(2)点],其中大多数还活着;只有一小部分变得无法访问并且可以释放[点(1)]。如果垃圾回收器要复制每个垃圾回收上所有幸存的对象,就像它使用标记和复制一样,那么它将花费大量时间来复制它而不会获得太多收益。
因此,标记和扫描算法是在旧一代上制作的,其中不复制任何内容,而只是释放无法访问的对象。由于此算法会导致堆的碎片化,因此还考虑了标记和扫描算法的变体,其中在扫描阶段之后,进行压缩,从而减少碎片。此算法称为标记和紧凑算法。
标记和紧凑算法可能非常耗时,因为它需要在以下阶段遍历对象图。
- 标记。
- 计算新位置。
- 参考调整。
- 移动
在新位置的计算阶段,当它获得可用空间时,尝试找到一个可以移动到此空间的对象(碎片整理)。存储该对以备后续阶段使用。这会导致算法消耗更多时间。
虽然标记和比较解决了一些特定于终身制生成的问题,但它遇到了一些严重的问题,因为这是一个STW(停止世界)事件,并且消耗了大量时间,可能会严重影响应用程序。
老一代的替代算法
为了减少中断时间,已经考虑了串行标记和紧凑算法的替代方案:
一种并行标记和紧凑算法,它仍锁存所有应用程序线程,但随后使用多个垃圾回收器线程处理标记和后续压缩。虽然这仍然是一种停止世界的方法,但在多核或多处理器机器上产生的暂停时间比串行标记和紧凑算法短。旧一代的并行算法(称为“ParallelOld”)自Java 5 Update 6以来一直可用,并且使用选项-XX:+UseParallelOldGC进行选择。
一种相互竞争的标记和扫描算法,至少在不停止其线程的情况下与应用程序竞争,并且偶尔需要短暂的停止世界阶段。这种并发标记和扫描算法(称为“CMS”)自Java 1.4.1以来一直存在。它使用选项-XX:+UseConcMarkSweepGC打开。重要的是,这只是一个标记和扫描算法;没有发生压实,导致已经讨论过的不成体系问题。
因此,简而言之-XX:+UseParallelOldGC被用作使用多个线程的指示,同时使用Mark和Compact算法进行主要收集。如果改用此选项,则次要或年轻集合是并行的,但主要集合仍然是单线程的。
我希望这个答案。