はじめに
JavaのGC方式(ParallelGC、G1GCなど)がオプションにより指定されていない場合、どのように決定されているかJavaVMソースコードで処理を追ってみた。
ソースは OpenJDK Java11 のもの。以下のソースパスで src/hotspot は省略している。
結論
先に結論を書くと、GC方式がオプションにより指定されていない場合、以下のようになる。
条件 | GC方式 |
---|---|
サーバクラスマシン | UseG1GC |
クライアントクラスマシン | UseSerialGC |
サーバクラスマシンは基本的にCPU2個以上かつメモリ約2G以上のマシン。
ソース詳細
JavaVM起動時に以下のような順で呼び出される。
Threads::create_vm (share/runtime/thread.cpp)
Arguments::apply_ergo (share/runtime/arguments.cpp)
Arguments::set_ergonomics_flags
GCConfig::initialize (share/gc/shared/gcConfig.cpp)
select_gc
select_gc_ergonomically
各ソースを順に追っていく。
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
....
// Parse arguments
// Note: this internally calls os::init_container_support()
jint parse_result = Arguments::parse(args);
if (parse_result != JNI_OK) return parse_result;
os::init_before_ergo();
jint ergo_result = Arguments::apply_ergo(); // ★
if (ergo_result != JNI_OK) return ergo_result;
...
Arguments::parse でjavaの引数がパースされ、GC方式が指定されていると、そのオプションがtrueになる。
例えば -XX:+UseParallelGC なら、UseParallelGC がtrueになる。
基本的に、JVMの各オプションの値は、ソース上ではオプションの名前の変数が使われている。
jint Arguments::apply_ergo() {
// Set flags based on ergonomics.
jint result = set_ergonomics_flags(); // ★
if (result != JNI_OK) return result;
.....
jint Arguments::set_ergonomics_flags() {
GCConfig::initialize(); // ★
set_conservative_max_heap_alignment();
.....
void GCConfig::initialize() {
assert(_arguments == NULL, "Already initialized");
_arguments = select_gc(); // ★
}
....
GCArguments* GCConfig::select_gc() {
// Fail immediately if an unsupported GC is selected
fail_if_unsupported_gc_is_selected();
if (is_no_gc_selected()) { // ★ GC方式が指定されていなかったら
// Try select GC ergonomically
select_gc_ergonomically(); // ★ ここでGC方式を決定
.....
is_no_gc_selected
is_no_gc_selected はどの方式も指定されてないとtrue、GC方式がすでに指定されていると、falseを返す。
bool GCConfig::is_no_gc_selected() {
FOR_EACH_SUPPORTED_GC(gc) { // ★
if (gc->_flag) {
return false;
}
}
return true;
}
FOR_EACH_SUPPORTED_GC は以下で定義。
# define FOR_EACH_SUPPORTED_GC(var) \
for (const SupportedGC* var = &SupportedGCs[0]; var < &SupportedGCs[ARRAY_SIZE(SupportedGCs)]; var++)
SupportedGCs にはサポートされているGC方式が羅列されている。
static const SupportedGC SupportedGCs[] = {
CMSGC_ONLY_ARG(SupportedGC(UseConcMarkSweepGC, CollectedHeap::CMS, cmsArguments, "concurrent mark sweep gc"))
EPSILONGC_ONLY_ARG(SupportedGC(UseEpsilonGC, CollectedHeap::Epsilon, epsilonArguments, "epsilon gc"))
G1GC_ONLY_ARG(SupportedGC(UseG1GC, CollectedHeap::G1, g1Arguments, "g1 gc"))
PARALLELGC_ONLY_ARG(SupportedGC(UseParallelGC, CollectedHeap::Parallel, parallelArguments, "parallel gc"))
PARALLELGC_ONLY_ARG(SupportedGC(UseParallelOldGC, CollectedHeap::Parallel, parallelArguments, "parallel gc"))
SERIALGC_ONLY_ARG(SupportedGC(UseSerialGC, CollectedHeap::Serial, serialArguments, "serial gc"))
ZGC_ONLY_ARG(SupportedGC(UseZGC, CollectedHeap::Z, zArguments, "z gc"))
};
select_gc_ergonomically
ここでサーバクラスマシンか否かによってGC方式を決定する。
void GCConfig::select_gc_ergonomically() {
if (os::is_server_class_machine()) {
# if INCLUDE_G1GC
FLAG_SET_ERGO_IF_DEFAULT(bool, UseG1GC, true); // ★ サーバクラスマシンならG1GC
# elif INCLUDE_PARALLELGC
FLAG_SET_ERGO_IF_DEFAULT(bool, UseParallelGC, true);
# elif INCLUDE_SERIALGC
FLAG_SET_ERGO_IF_DEFAULT(bool, UseSerialGC, true);
# endif
} else {
# if INCLUDE_SERIALGC
FLAG_SET_ERGO_IF_DEFAULT(bool, UseSerialGC, true); // ★ クライアントクラスマシンならSerialGC
# endif
}
}
is_server_class_machine
is_server_class_machine では、以下の条件でtrue(つまりサーバクラスマシン) または、false を返す。
条件 | 返却値 |
---|---|
オプション NeverActAsServerClassMachine 指定時 | false |
オプション AlwaysActAsServerClassMachine 指定時 | true |
CPU2個以上かつメモリ約2G以上 | true |
それ以外 | false |
bool os::is_server_class_machine() {
// First check for the early returns
if (NeverActAsServerClassMachine) { //★ NeverActAsServerClassMachine 指定の場合、サーバクラスマシンでないとみなす
return false;
}
if (AlwaysActAsServerClassMachine) { //★ AlwaysActAsServerClassMachine 指定の場合、サーバクラスマシンとみなす。
return true;
}
// Then actually look at the machine
bool result = false;
const unsigned int server_processors = 2;
const julong server_memory = 2UL * G;
// We seem not to get our full complement of memory.
// We allow some part (1/8?) of the memory to be "missing",
// based on the sizes of DIMMs, and maybe graphics cards.
const julong missing_memory = 256UL * M;
/* Is this a server class machine? */ //★CPU2個以上かつメモリ約2G以上ならtrue
if ((os::active_processor_count() >= (int)server_processors) &&
(os::physical_memory() >= (server_memory - missing_memory))) {
const unsigned int logical_processors =
VM_Version::logical_processors_per_package();
if (logical_processors > 1) {
const unsigned int physical_packages =
os::active_processor_count() / logical_processors;
if (physical_packages >= server_processors) {
result = true;
}
} else {
result = true;
}
}
return result;
}