如何确定我使用哪个GC? - java

我没有指定任何GC,并且我认为我的JVM默认没有启用任何GC。

当然,我知道OpenJDK8默认使用ParallelGC,但我认为它应该可以通过命令行打印,如下所示:
java -XX:+PrintFlagsFinal|grep Use|grep GC
我希望输出包含bool UseParallelOldGC = true {product},但不是:

     bool ParGCUseLocalOverflow                     = false                               {product}
     bool UseAdaptiveGCBoundary                     = false                               {product}
     bool UseAdaptiveSizeDecayMajorGCCost           = true                                {product}
     bool UseAdaptiveSizePolicyWithSystemGC         = false                               {product}
     bool UseAutoGCSelectPolicy                     = false                               {product}
     bool UseConcMarkSweepGC                        = false                               {product}
     bool UseDynamicNumberOfGCThreads               = false                               {product}
     bool UseG1GC                                   = false                               {product}
     bool UseGCLogFileRotation                      = false                               {product}
     bool UseGCOverheadLimit                        = true                                {product}
     bool UseGCTaskAffinity                         = false                               {product}
     bool UseMaximumCompactionOnSystemGC            = true                                {product}
     bool UseParNewGC                               = false                               {product}
     bool UseParallelGC                             = false                               {product}
     bool UseParallelOldGC                          = false                               {product}
     bool UseSerialGC                               = false                               {product}


java -XX:+PrintCommandLineFlags -version
我希望输出包含:XX:+UseParallelGC,但它也不是:

-XX:InitialHeapSize=460493056 -XX:MaxHeapSize=7367888896 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
.

我的JVM选项:

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -verbose:gc -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime
.

上面的输出显示没有启用任何GC(我认为是),这种情况令我非常困惑。

GC记录输出,例如:

OpenJDK 64-Bit Server VM (25.181-b13) for linux-amd64 JRE (1.8.0_181-b13), built on Oct 23 2018 11:39:12 by "buildozer" with gcc 6.4.0
Memory: 4k page, physical 28780816k(6283132k free), swap 0k(0k free)
CommandLine flags: -XX:InitialHeapSize=460493056 -XX:MaxHeapSize=7367888896 -XX:+PrintGC -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps -XX:+PrintGCDeta
ils -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
{Heap before GC invocations=0 (full 0):
 def new generation   total 135168K, used 120192K [0x0000000608c00000, 0x0000000611ea0000, 0x000000069b2a0000)
  eden space 120192K, 100% used [0x0000000608c00000, 0x0000000610160000, 0x0000000610160000)
  from space 14976K,   0% used [0x0000000610160000, 0x0000000610160000, 0x0000000611000000)
  to   space 14976K,   0% used [0x0000000611000000, 0x0000000611000000, 0x0000000611ea0000)
 tenured generation   total 300416K, used 0K [0x000000069b2a0000, 0x00000006ad800000, 0x00000007c0000000)
   the space 300416K,   0% used [0x000000069b2a0000, 0x000000069b2a0000, 0x000000069b2a0200, 0x00000006ad800000)
 Metaspace       used 20532K, capacity 20780K, committed 20992K, reserved 1067008K
  class space    used 2628K, capacity 2726K, committed 2816K, reserved 1048576K
2019-02-25T06:29:46.105+0000: 2.890: [GC (Allocation Failure) 2019-02-25T06:29:46.105+0000: 2.890: [DefNew
Desired survivor size 7667712 bytes, new threshold 1 (max 15)
- age   1:   15335424 bytes,   15335424 total
: 120192K->14976K(135168K), 0.0238110 secs] 120192K->18041K(435584K), 0.0238765 secs] [Times: user=0.01 sys=0.01, real=0.02 secs] 
Heap after GC invocations=1 (full 0):
 def new generation   total 135168K, used 14976K [0x0000000608c00000, 0x0000000611ea0000, 0x000000069b2a0000)
  eden space 120192K,   0% used [0x0000000608c00000, 0x0000000608c00000, 0x0000000610160000)
  from space 14976K, 100% used [0x0000000611000000, 0x0000000611ea0000, 0x0000000611ea0000)
  to   space 14976K,   0% used [0x0000000610160000, 0x0000000610160000, 0x0000000611000000)
 tenured generation   total 300416K, used 3065K [0x000000069b2a0000, 0x00000006ad800000, 0x00000007c0000000)
   the space 300416K,   1% used [0x000000069b2a0000, 0x000000069b59e660, 0x000000069b59e800, 0x00000006ad800000)
 Metaspace       used 20532K, capacity 20780K, committed 20992K, reserved 1067008K
  class space    used 2628K, capacity 2726K, committed 2816K, reserved 1048576K
}
2019-02-25T06:29:46.129+0000: 2.914: Total time for which application threads were stopped: 0.0241189 seconds, Stopping threads took: 0.0000181 seconds
{Heap before GC invocations=1 (full 0):
 def new generation   total 135168K, used 21860K [0x0000000608c00000, 0x0000000611ea0000, 0x000000069b2a0000)
  eden space 120192K,   5% used [0x0000000608c00000, 0x00000006092b93f8, 0x0000000610160000)
  from space 14976K, 100% used [0x0000000611000000, 0x0000000611ea0000, 0x0000000611ea0000)
  to   space 14976K,   0% used [0x0000000610160000, 0x0000000610160000, 0x0000000611000000)
 tenured generation   total 300416K, used 3065K [0x000000069b2a0000, 0x00000006ad800000, 0x00000007c0000000)
   the space 300416K,   1% used [0x000000069b2a0000, 0x000000069b59e660, 0x000000069b59e800, 0x00000006ad800000)
 Metaspace       used 20982K, capacity 21132K, committed 21248K, reserved 1067008K
  class space    used 2667K, capacity 2758K, committed 2816K, reserved 1048576K
2019-02-25T06:29:46.187+0000: 2.972: [Full GC (Metadata GC Threshold) 2019-02-25T06:29:46.187+0000: 2.972: [Tenured: 3065K->9617K(300416K), 0.0270556 secs] 24926K-
>9617K(435584K), [Metaspace: 20982K->20982K(1067008K)], 0.0271334 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 
Heap after GC invocations=2 (full 1):
 def new generation   total 135296K, used 0K [0x0000000608c00000, 0x0000000611ec0000, 0x000000069b2a0000)
  eden space 120320K,   0% used [0x0000000608c00000, 0x0000000608c00000, 0x0000000610180000)
  from space 14976K,   0% used [0x0000000610180000, 0x0000000610180000, 0x0000000611020000)
  to   space 14976K,   0% used [0x0000000611020000, 0x0000000611020000, 0x0000000611ec0000)
 tenured generation   total 300416K, used 9617K [0x000000069b2a0000, 0x00000006ad800000, 0x00000007c0000000)
   the space 300416K,   3% used [0x000000069b2a0000, 0x000000069bc04698, 0x000000069bc04800, 0x00000006ad800000)
 Metaspace       used 20982K, capacity 21132K, committed 21248K, reserved 1067008K
  class space    used 2667K, capacity 2758K, committed 2816K, reserved 1048576K
}

(从应用开始到第一个主要GC)

日志显示JVM堆分为newtenured,但没有打印GC类型。

可悲的是,我也不能使用jmap -heap {pid}来获取GC类型,因为在我的环境中没有此选项的jmap(-heap)。

所以我想知道:

  • 我真正使用的是哪个GC?
  • 命令行选项(-XX:+PrintCommandLineFlags-XX:+PrintFlagsFinal)输出信息正确吗?
  • 我的环境:
    k8s + docker,Alpine OpenJKD8

    参考方案

    问题是您实际上需要名称时正在查看bool类型的JVM选项。这不是您的错,JVM的设计者决定给不同的垃圾收集器命名,但是提供类似于布尔选项的控件。

    因此,即使所有这些选项都是false,也存在垃圾回收器,但是这些选项不足以获取其名称。但是,另一方面,大多数名称不足以描述这些垃圾收集器的作用,或者它们与其他算法的区别。

    JDK8默认使用ParallelGC并不完全正确;正如this answer所描述的,该算法是通过某种启发式方法选择的,不过,在大多数情况下,您最终还是会使用ParallelGC。

    使用以下代码

    Object flags = ManagementFactory.getPlatformMBeanServer().invoke(
        ObjectName.getInstance("com.sun.management:type=DiagnosticCommand"),
        "vmFlags", new Object[] { null }, new String[] { "[Ljava.lang.String;" });
    for(String f: ((String)flags).split("\\s+"))
        if(f.contains("GC")) System.out.println(f);
    for(GarbageCollectorMXBean gc: ManagementFactory.getGarbageCollectorMXBeans())
        System.out.printf("%-20s%s%n", gc.getName(), Arrays.toString(gc.getMemoryPoolNames()));
    

    我懂了

    > jdk1.8.0_162\bin\java ...
    -XX:+UseParallelGC
    PS Scavenge         [PS Eden Space, PS Survivor Space]
    PS MarkSweep        [PS Eden Space, PS Survivor Space, PS Old Gen]
    

    在我的机器上,因此在没有选项的情况下运行确实在此环境中选择了ParallelGC。但是请注意,报告的名称“PS Scavenge”和“PS MarkSweep”突出显示了选项和名称的另一个问题:典型配置考虑了两种垃圾回收算法,一种用于次要gc,一种用于主要gc。

    当我尝试-XX:-UseParallelGC时,我得到

    > jdk1.8.0_162\bin\java -XX:-UseParallelGC ...
    -XX:+UseParallelGC
    PS Scavenge         [PS Eden Space, PS Survivor Space]
    PS MarkSweep        [PS Eden Space, PS Survivor Space, PS Old Gen]
    

    这展示了JVM选项看起来像boolean的问题:我无法关闭它们,因为JVM需要一个实际的其他垃圾收集器来选择。
    因此,要关闭并行,可以使用-XX:+UseSerialGC:

    > jdk1.8.0_162\bin\java -XX:+UseSerialGC ...
    -XX:+UseSerialGC
    Copy                [Eden Space, Survivor Space]
    MarkSweepCompact    [Eden Space, Survivor Space, Tenured Gen]
    

    为了比较

    > jdk1.8.0_162\bin\java -XX:+UseConcMarkSweepGC ...
    -XX:+UseConcMarkSweepGC
    -XX:+UseParNewGC
    ParNew              [Par Eden Space, Par Survivor Space]
    ConcurrentMarkSweep [Par Eden Space, Par Survivor Space, CMS Old Gen]
    

    请注意,两种算法中的每一种如何与一个选项相关联,但是指定一个选项可以选择两种垃圾回收算法。

    > jdk-9.0.4\bin\java ...
    -XX:ConcGCThreads=2
    -XX:+UseG1GC
    G1 Young Generation [G1 Eden Space, G1 Survivor Space]
    G1 Old Generation   [G1 Eden Space, G1 Survivor Space, G1 Old Gen]
    
    > jdk-11.0.1\bin\java ...
    -XX:ConcGCThreads=2
    -XX:GCDrainStackTargetSize=64
    -XX:+UseG1GC
    G1 Young Generation [G1 Eden Space, G1 Survivor Space, G1 Old Gen]
    G1 Old Generation   [G1 Eden Space, G1 Survivor Space, G1 Old Gen]
    
    > jdk-11.0.1\bin\java -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC ...
    -XX:+UseEpsilonGC
    Epsilon Heap        [Epsilon Heap]
    

    因此,如果尝试获取关联的Use…GC boolean选项的代码(即,使用非标准com.sun.management:type=DiagnosticCommand MBean的上述代码的第一部分)找不到任何内容,则可以尝试使用getGarbageCollectorMXBeans()报告的垃圾收集器名称,但是如您所见,这些名称与JVM选项的名称不匹配,因此您必须知道这些名称是如何关联的。

    但是最后,这些名称都没有真正的描述性,因此,只有当您已经知道这些名称背后的含义时,它们才有帮助……

    Java:正则表达式模式匹配器是否有大小限制? - java

    我的模式类似于OR:“word1 | word2 | word3”我大约有800个字。可能有问题吗? 参考方案 您仅受记忆和理智的限制。 :)

    Java:线程池如何将线程映射到可运行对象 - java

    试图绕过Java并发问题,并且很难理解线程池,线程以及它们正在执行的可运行“任务”之间的关系。如果我创建一个有10个线程的线程池,那么我是否必须将相同的任务传递给池中的每个线程,或者池化的线程实际上只是与任务无关的“工人无人机”可用于执行任何任务?无论哪种方式,Executor / ExecutorService如何将正确的任务分配给正确的线程? 参考方案 …

    JAVA:字节码和二进制有什么区别? - java

    java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

    java:继承 - java

    有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

    Java:BigInteger,如何通过OutputStream编写它 - java

    我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…