G1垃圾收集器的字符串去重功能默认开启吗?

16

JEP 192: G1中的字符串去重在Java 8 Update 20中实现,增加了新的字符串去重功能:

通过增强G1垃圾收集器,自动且持续地去重重复的字符串,从而减少Java堆的活动数据集。

JEP页面提到一个命令行选项UseStringDeduplication(bool),允许启用或禁用去重功能。但是JEP页面并没有指出默认值。

➠ G1垃圾收集器默认情况下是否启用了去重功能,包括Java 8和Java 9?

➠ 是否有“getter”方法可以在运行时验证当前设置?

我不知道除了JEP页面之外还可以在哪里查找文档。

至少在Java 9的HotSpot版本中,G1垃圾收集器默认是启用的。这一事实促使了这个问题的提出。有关字符串内部化和去重的更多信息,请参见Aleksey Shipilev在2014年10月的演示文稿,第29页。


它在Java 8中默认未启用。 你必须使用-XX:+UseStringDeduplication标志。请注意,如果您未使用G1 GC,则无法正常工作。 - Lakshmikant Deshpande
2个回答

22

默认关闭字符串去重

在以下Java 8和Java 9版本中,默认情况下,UseStringDeduplicationfalse(已禁用)。

验证此功能设置的一种方法是:列出JVM的所有最终标志,然后搜索它。

构建版本1.8.0_131-b11

    $ java -XX:+UseG1GC  -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version | grep -i 'duplicat'
     bool PrintStringDeduplicationStatistics        = false                               {product}
    uintx StringDeduplicationAgeThreshold           = 3                                   {product}
     bool StringDeduplicationRehashALot             = false                               {diagnostic}
     bool StringDeduplicationResizeALot             = false                               {diagnostic}
     bool UseStringDeduplication                    = false                               {product}
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

构建 9+18

    $ java -XX:+UseG1GC  -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version | grep -i 'duplicat'
    uintx StringDeduplicationAgeThreshold          = 3                                        {product} {default}
     bool StringDeduplicationRehashALot            = false                                 {diagnostic} {default}
     bool StringDeduplicationResizeALot            = false                                 {diagnostic} {default}
     bool UseStringDeduplication                   = false                                    {product} {default}
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)

另一种测试它的方法是通过

package jvm;

import java.util.ArrayList;
import java.util.List;

public class StringDeDuplicationTester {

    public static void main(String[] args) throws Exception {
        List<String> strings = new ArrayList<>();
        while (true) {
            for (int i = 0; i < 100_00; i++) {
                strings.add(new String("String " + i));
            }
            Thread.sleep(100);
        }
    }
}

在不明确指定的情况下运行。

$ java  -Xmx256m -XX:+UseG1GC -XX:+PrintStringDeduplicationStatistics jvm.StringDeDuplicationTester
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at jvm.StringDeDuplicationTester.main(StringDeDuplicationTester.java:12)

明确地打开并运行。

$ java  -Xmx256m -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics jvm.StringDeDuplicationTester
[GC concurrent-string-deduplication, 5116.7K->408.7K(4708.0K), avg 92.0%, 0.0246084 secs]
   [Last Exec: 0.0246084 secs, Idle: 1.7075173 secs, Blocked: 0/0.0000000 secs]
      [Inspected:          130568]
         [Skipped:              0(  0.0%)]
         [Hashed:          130450( 99.9%)]
         [Known:                0(  0.0%)]
         [New:             130568(100.0%)   5116.7K]
      [Deduplicated:       120388( 92.2%)   4708.0K( 92.0%)]
         [Young:                0(  0.0%)      0.0B(  0.0%)]
         [Old:             120388(100.0%)   4708.0K(100.0%)]
   [Total Exec: 1/0.0246084 secs, Idle: 1/1.7075173 secs, Blocked: 0/0.0000000 secs]
      [Inspected:          130568]
         [Skipped:              0(  0.0%)]
         [Hashed:          130450( 99.9%)]
         [Known:                0(  0.0%)]
         [New:             130568(100.0%)   5116.7K]
      [Deduplicated:       120388( 92.2%)   4708.0K( 92.0%)]
         [Young:                0(  0.0%)      0.0B(  0.0%)]
         [Old:             120388(100.0%)   4708.0K(100.0%)]
   [Table]
      [Memory Usage: 264.9K]
      [Size: 1024, Min: 1024, Max: 16777216]
      [Entries: 10962, Load: 1070.5%, Cached: 0, Added: 10962, Removed: 0]
      [Resize Count: 0, Shrink Threshold: 682(66.7%), Grow Threshold: 2048(200.0%)]
      [Rehash Count: 0, Rehash Threshold: 120, Hash Seed: 0x0]
      [Age Threshold: 3]
   [Queue]
      [Dropped: 0]
[GC concurrent-string-deduplication, deleted 0 entries, 0.0000008 secs]
...
output truncated

注意: 这个输出是从 build 1.8.0_131-b11生成的。看起来Java 9没有选项可以打印字符串去重统计信息。可能是个bug? 不是。统一日志记录杀死了这个特定的选项。

$ java  -Xmx256m -XX:+UseG1GC -XX:+PrintStringDeduplicationStatistics -version
Unrecognized VM option 'PrintStringDeduplicationStatistics'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

关于这个 bug 说明,可能该标志现在已经过时了,需要改为 Xlogged。我在我的答案中解决了这个问题。 - Naman
感谢您提醒我们,在Java9中,统计标志未能正常工作。 - fxrbfg
但是为什么默认情况下它是关闭的呢? - flow2k
在JDK 11中默认为OFF。 - vsingh

15

虽然Jigar已经提供了了解JVM标志和统计信息的方法,但为了链接到一些有用的文档来回答这个问题的部分:

我不知道在JEP页面之外查找文档的位置。

在JDK 9中,当未明确指定垃圾收集器时,默认垃圾收集器是G1。

  • java工具详细介绍了该标志的使用方法。

-XX:+UseStringDeduplication

启用字符串去重。默认情况下,此选项已禁用。要使用此选项,您必须启用垃圾优先(G1)垃圾回收器。

字符串去重通过利用许多相同的字符串对象来减少Java堆上String对象的内存占用。相同的字符串对象可以指向并共享相同的字符数组,而不是每个字符串对象都指向自己的字符数组。


另外,回答了开放性问题:

Java 9没有打印字符串去重统计信息的选项。

随着Java 9中 JEP 158: 统一JVM日志记录 的实现,垃圾收集器标志被标记为旧版,跟踪它们的替代方法是使用-Xlog功能。将GC Logging Flags转换为Xlog的替代清单详见此处,其中之一建议将

PrintStringDeduplicationStatistics  =>   -Xlog:stringdedup*=debug

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