为什么/当你不想在JVM中启用Java 8 UseStringDuplication?

2022-09-01 05:52:58

Java 8 引入了字符串重复数据删除,可以通过启动 JVM 来启用,该选项允许通过引用相似的对象而不是保留重复对象来节省一些内存。当然,它的有效性因程序而异,具体取决于利用率,但我认为可以肯定地说,一般来说,它可以被认为是对大多数应用程序(如果不是全部)有益的,这让我对一些事情感到疑惑:-XX:+UseStringDeduplicationStringStrings

为什么默认情况下不启用它?是因为与去紧急重复相关的成本,还是仅仅因为G1GC仍然被认为是新的?

是否有任何(或可能存在)您不想使用重复数据删除的边缘情况?


答案 1

字符串重复数据消除可能有害的情况包括:

  • 字符串很多,但重复的可能性非常低:查找重复项的时间开销和重复数据删除数据结构的空间开销将不会得到补偿。
  • 重复的概率是合理的,但大多数字符串无论如何都会在几个GC周期1内死亡。如果去重复的字符串很快就要进行 GC,则重复数据消除的益处较小。

    (这不是关于在第一个GC周期中无法生存的字符串。对于 GC 来说,甚至尝试对它知道是垃圾的字符串进行去抖动是没有意义的。

我们只能推测为什么Java团队没有默认启用去重复数据消除,但他们处于一个更好的位置,可以做出理性的(即基于证据的)决策, 你和我。我的理解是,他们可以访问许多大型现实世界的应用程序,以进行基准测试/尝试优化的效果。他们也可能在合作伙伴或客户组织中具有类似大型代码库和效率问题的联系......他们可以要求他们提供反馈,了解抢先体验版本中的优化是否按预期工作。

1 - 这取决于 StringDeduplicationAgeThreshold JVM 设置的值。这默认为 3,这意味着(粗略地)一个字符串必须在 3 个次要集合或一个主要集合中存活下来才能考虑进行重复数据消除。但无论如何,如果一个字符串被去重复,然后在不久之后被发现无法访问,那么去重复的开销将不会为该字符串偿还。


如果您询问何时应考虑启用重复数据消除,我的建议是尝试一下,看看它是否对每个应用程序有帮助。但是,您需要进行一些应用程序级基准测试(这需要付出努力!),以确保去重复是有益的......

仔细阅读 JEP 192 还可以帮助您了解这些问题,并判断它们如何适用于您的 Java 应用程序。


答案 2

我绝对理解这并没有回答这个问题,只是想提一下jdk-9引入了另一个优化,默认情况下称为:

-XX:+紧凑型字符串

其中 Latin1 字符占用单个字节而不是两个字节(通过字符)。由于这种变化,String的许多内部方法都发生了变化 - 它们对用户的作用相同,但在很多情况下,它们在内部速度更快。

此外,如果字符串通过加号将两个字符串连接在一起,javac将生成不同的字节码。

没有字节码指令将两个字符串连接在一起,因此javac将生成一个

StringBuilder#append

在后端。直到jdk-9。

现在字节码委托给

StringConcatFactory#makeConcatWithConstants

StringConcatFactory#makeConcat

通过调用动态字节码指令:

   aload_0
   1: aload_2
   2: aload_1
   3: invokedynamic #8,  0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
   8: areturn 

现在,这两个字符串的串联方式是运行时的决定。它可能仍然是一个StringBuilder,或者它可能是字节数组的串联,等等。您所知道的是,这种情况可能会改变,您将获得最快的解决方案。

编辑

我刚刚调试过,发现有很多关于如何附加这些字符串的策略:

    private enum Strategy {
    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder}.
     */
    BC_SB,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
    BC_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also tries to estimate the required storage.
     */
    MH_SB_SIZED,

    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
    MH_INLINE_SIZED_EXACT
}

默认值为:

MH_INLINE_SIZED_EXACT


推荐