自动将对象提升至老年代

5
当对象达到Tenuring阈值或在转移时“TO”Survival空间已满时,可以将对象从年轻代提升到老年代。
因此,我的问题是:为了提高性能,如果我知道我的对象将经常使用(引用),是否可能自动/手动声明一个老/永久代中的对象,以便不在Eden中声明它可以延迟对小垃圾回收的需要,从而延迟“停止世界”事件并提高应用程序的性能?

当然不能使用 MaxTenuringThreshold=0 - Samuel Hošovský
1
年轻代暂停应该相当便宜,不应对应用程序性能产生重大影响。如果您遇到实际的性能问题而不仅仅是尝试一些过早的优化,那么也许您应该明确说明。 - the8472
1
你的使用场景是什么?通常在 GC 调优方面,我发现人们通常试图解决错误的问题。 - Surbhi Sharma
2个回答

4

通常情况下:

不,不能只为一个特定的对象进行分配。

更详细地说:

分配大致如下:

  1. 如果 tlab_top + size <= tlab_end,使用线程本地分配缓冲区 (TLAB)。这是最快速的路径。分配只需将 tlab_top 指针增加即可。
  2. 如果 TLAB 几乎已满,在伊甸园空间中创建新的 TLAB,并在一个新的 TLAB 中重试。
  3. 如果 TLAB 剩余空间不足,但仍太大而无法丢弃,则尝试直接在伊甸园空间中分配对象。由于伊甸园是所有线程共享的,因此需要使用原子操作进行伊甸园空间中的分配。
  4. 如果在伊甸园空间中分配失败 (eden_top + size > eden_end),通常会发生小型垃圾回收。
  5. 即使在 Young GC 后伊甸园空间仍有不足够的空间,也会尝试直接在老年代中分配。

“Hack”:

以下参数:

XX:PretenureSizeThreshold=size

这个参数默认设置为0,因此被禁用。如果设置了该参数,它将定义对象自动分配到旧一代的大小阈值。

但是:

使用时要小心:设置该参数错误可能会极大地改变GC的行为。而且,只有少数百分比的对象在第一次GC后存活下来,所以大多数对象不必在年轻代GC期间进行复制。

因此,年轻代GC非常快速,您应该不需要通过强制对象分配到旧一代来“优化”它。

Java参数:

如果您想获得可能的Java参数概述,请运行以下命令:

java -XX:+PrintVMOptions -XX:+AggressiveOpts -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal  -version

这将打印出您可以设置的所有标志。
不同的垃圾收集器:
还要记住,有不同的垃圾收集器存在,并且计划使用“Garbage First(G1)GC”作为默认垃圾收集器的是Java 9,这可能会以不同的方式处理大对象(通过将它们分配到“ humongous regions ”中)。
附加来源:
Stack overflow问题:Size of Huge Objects directly allocated to Old Generation

这确实是可能的,但需要注明一点。http://www.oracle.com/technetwork/systems/index-156457.html - Surbhi Sharma
@SurbhiSharma: 在并发收集器中,可以启用选项-XX:PretenureSizeThreshold = <字节大小>来指示直接在旧一代中创建的阈值。这可有效用于创建长期存在且不必经历从年轻一代创建然后复制到旧一代的推广周期的缓存、查找表等。请谨慎使用此选项,因为它可能会降低性能而不是提高性能。 所有在该帖子中关于PretenureSizeThreshold的陈述也是我的答案的一部分吗? - Markus Weninger

0

在老生代中无法直接创建对象,必须经过 伊甸园空间幸存者空间(年轻代)才能到达老生代。但是,如果您知道您的对象的寿命很长(例如,如果您实现了类似缓存的东西),您可以设置以下JVM参数:

-XX:InitialTenuringThreshold=7:设置用于并行年轻收集器自适应GC大小的初始tenuring阈值。tenuring阈值是对象在年轻收集之前存活的次数,然后被提升到老年代或tenured代。

-XX:MaxTenuringThreshold=n:设置用于自适应GC大小的最大tenuring阈值。当前最大值为15。并行收集器的默认值为15,CMS的默认值为4。

来源:http://www.oracle.com/technetwork/articles/java/vmoptions-jsp-140102.html

所以,您可以降低应用程序的终身阈值。 我实际上已经这样做了,对于小型GC,停止世界GC时间减少了(我有一个巨大的250GB JVM,因此效果非常显著)。


这不正确,巨大的对象可以直接在旧代中创建。 - the8472
同意,我从问题中得出的是当前对象被分配到年轻代,如何将这些对象安全地放入老年代?“安全”是关键词。我在这里做出的另一个假设是,他想避免停止所有时间,无论在哪一代GC中。您可以始终使用PretenureSizeThreshold直接在Old gen中创建对象,但这真的会增加性能吗?不。根据我的实验,它会降低性能。将垃圾放入老年代,最终触发老年代GC。 - Surbhi Sharma

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