问题描述
我没有指定任何 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 heap分为new
和tenured
,但没有print GC类型。
可悲的是,我也不能使用jmap -heap {pid}
来获取 GC 类型,因为 jmap 在我的环境中没有这个选项(-heap)。
所以我想知道:
- 我真正使用的是哪个 GC?
-
命令行选项(
-XX:+PrintCommandLineFlags
和-XX:+PrintFlagsFinal
)输出信息是否正确?
我的环境:k8s+docker,Alpine OpenJKD8
1楼
jcmd <pid> VM.flags
应该有帮助。
例如
$ /usr/java/jdk1.8.0_202/bin/java Test
...
$ jcmd 28815 VM.flags
28815:
-XX:CICompilerCount=3 -XX:InitialHeapSize=266338304 -XX:MaxHeapSize=4257218560 -XX:MaxNewSize=1418723328 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=88604672 -XX:OldSize=177733632 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
最后打印的标志是-XX:+UseParallelGC
即使没有明确指定。
然后,如果我运行以 G1 作为默认收集器的 JDK 11,我将得到以下输出:
$ /usr/java/jdk11.0.2/bin/java Test
...
$ jcmd 28862 VM.flags
28862:
-XX:CICompilerCount=3 -XX:ConcGCThreads=1 -XX:G1ConcRefinementThreads=4 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=266338304 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=4257218560 -XX:MaxNewSize=2554331136 -XX:MinHeapDeltaBytes=1048576 -XX:NonNMethodCodeHeapSize=5830092 -XX:NonProfiledCodeHeapSize=122914074 -XX:ProfiledCodeHeapSize=122914074 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC
现在最后打印的选项是-XX:+UseG1GC
。
如果由于某种原因您没有jcmd
可用,您可以尝试 ,它是的轻量级独立替代jcmd
:
$ jattach 28862 jcmd VM.flags
更新
如果没有打印-XX:Use*GC
标志,则表示使用了串行 GC。
这仅发生在 JDK 8 及更早版本上。
从 JDK 9 开始,JVM 会自动设置Use*GC
标志之一。
有关详细信息,请参阅 。
2楼
问题是当您真正需要name时,您正在查看bool
类型的 JVM 选项。
这不是你的错,JVM 的设计者决定给不同的垃圾收集器命名,但提供看起来像布尔选项的控件。
因此,即使所有这些选项都是false
,也有垃圾收集器,但这些选项不足以获得它的名字。
但另一方面,无论如何,大多数名称不足以描述这些垃圾收集器的作用,或者它们与其他算法的区别。
JDK8 默认使用 ParallelGC 并不完全正确; 正如所描述的那样,算法是通过一些启发式选择的,但是,在大多数情况下,您最终会使用 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 选项的名称不匹配,因此您必须知道这些名称是如何关联的。
但最后,这些名字都不是真正的描述性的,所以只有当你已经知道这些名字背后的含义时,它们才有用……