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