G1垃圾回收器的String Deduplication功能是不是默认开启?

Posted

技术标签:

【中文标题】G1垃圾回收器的String Deduplication功能是不是默认开启?【英文标题】:Is String Deduplication feature of the G1 garbage collector enabled by default?G1垃圾回收器的String Deduplication功能是否默认开启? 【发布时间】:2018-03-14 05:27:04 【问题描述】:

在 Java 8 Update 20 中实现的JEP 192: String Deduplication in G1 添加了新的字符串重复数据删除功能:

通过增强 G1 垃圾收集器减少 Java 堆实时数据集,以便自动连续删除重复的 String 实例。

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

➠ 在与 Java 8 和 Java 9 捆绑在一起的 G1 垃圾收集器中,重复数据删除功能是默认开启还是关闭?

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

我不知道在哪里可以找到 JEP 页面之外的文档。

至少在配备HotSpot 的Java 9 实现中,G1 garbage collector 是enabled by default。这个事实现在提示了这个问题。有关字符串实习和重复数据删除的更多信息,请参阅 29:00 的 this 2014-10 presentation by Aleksey Shipilev。

【问题讨论】:

在 Java 8 中默认没有启用它。你必须使用 -XX:+UseStringDeduplication 标志。请注意,如果您不使用 G1 GC,这将不起作用。 【参考方案1】:

字符串去重关闭默认

对于下面看到的 Java 8 和 Java 9 版本,UseStringDeduplication 默认为 false(禁用)。

验证功能设置的一种方法:list out all the final flags for 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 没有打印字符串重复数据删除统计信息的选项。潜在的错误? 。统一日志记录杀死了这个特定的选项。

$ 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.

【讨论】:

对于错误声明,可能该标志现在是遗留的,需要改为 Xlogged。在我的回答中已经解决了这个问题。 感谢统计标志在 java9 中不起作用的通知 但是为什么默认是关闭的呢? 在 jdk 11 中也默认关闭。【参考方案2】:

虽然Jigar 已经精确地提供了了解 JVM 标志和统计信息的方法,但还没有链接到一些有用的文档来解决这部分问题:

我不知道在哪里可以找到 JEP 页面之外的文档。

Java9 Release Note 描述了 JEP 248:Make G1 the Default Garbage Collector 的实现,其中包含 -

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

java tool 详细说明了标志的用法

-XX:+UseStringDeduplication

启用字符串重复数据删除。 默认情况下,此选项处于禁用状态。到 使用此选项,您必须启用垃圾优先 (G1) 垃圾 收集器。

字符串重复数据删除减少了字符串对象的内存占用 Java 堆利用了许多 String 对象这一事实 是相同的。而不是每个 String 对象都指向自己的 字符数组,相同的 String 对象可以指向并共享 相同的字符数组。


如果

Java 9 没有打印字符串重复数据删除统计信息的选项。

使用 Java9 中的 JEP 158:Unified JVM Logging 实现,垃圾收集器标志被标记为旧式,跟踪它们的替代方法是使用 -Xlog 功能。 here 列出了将 GC Logging Flags 转换为 Xlog 的替换的详细列表。其中之一建议更换

PrintStringDeduplicationStatistics  =>   -Xlog:stringdedup*=debug

【讨论】:

以上是关于G1垃圾回收器的String Deduplication功能是不是默认开启?的主要内容,如果未能解决你的问题,请参考以下文章

JVM之G1垃圾回收器

G1垃圾回收器在并发场景调优

JVM面试必问:G1垃圾回收器

JVM面试高频考点:由浅入深带你了解G1垃圾回收器!!!

G1垃圾回收器

#yyds干货盘点# Java 垃圾回收器之G1详解