gc分为了多种回收器,在虚拟机启动时有默认参数或者输入参数来控制具体的gc回收器
thread#create_vm时进行参数加载
1、默认参数
具体的启动参数有哪些可以通过java命令java -XX:+PrintFlagsInitial查看
常用参数类型:
标准参数:-(如-version)
非标准:-X(如-Xmixed)
不稳定:-XX(如-XX:+UseG1GC)
这些参数在jvm内部处理一共有五种:
flags-file:文件配置参数,指定后-XX:参数前缀可以省略,多条换行即可
cmd_args:启动命令行传入的参数
vm_options_args:系统默认配置,通过解析jar或者jmod获取
java_options_args:jvm选项,可以覆盖java_tool_options_args
java_tool_options_args:jvm监控选项,如JPS,JMAP,JSTAT接收的参数,语法为-Djava.tool.option=value (如-Dcom.sun.management.jmxremote.port=8444)
在选择具体的gc之前,获取系统默认参数并且将vm option 解析出来进行替换
//arguments.cpp
jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
assert(verify_special_jvm_flags(false), "deprecated and obsolete flag table inconsistent");
JVMFlag::check_all_flag_declarations();
// If flag "-XX:Flags=flags-file" is used it will be the first option to be processed.
const char* hotspotrc = ".hotspotrc";
bool settings_file_specified = false;
bool needs_hotspotrc_warning = false;
ScopedVMInitArgs initial_vm_options_args("");
ScopedVMInitArgs initial_java_tool_options_args("env_var='JAVA_TOOL_OPTIONS'");
ScopedVMInitArgs initial_java_options_args("env_var='_JAVA_OPTIONS'");
// Pointers to current working set of containers
JavaVMInitArgs* cur_cmd_args;
JavaVMInitArgs* cur_vm_options_args;
JavaVMInitArgs* cur_java_options_args;
JavaVMInitArgs* cur_java_tool_options_args;
// Containers for modified/expanded options
ScopedVMInitArgs mod_cmd_args("cmd_line_args");
ScopedVMInitArgs mod_vm_options_args("vm_options_args");
ScopedVMInitArgs mod_java_tool_options_args("env_var='JAVA_TOOL_OPTIONS'");
ScopedVMInitArgs mod_java_options_args("env_var='_JAVA_OPTIONS'");
jint code =
parse_java_tool_options_environment_variable(&initial_java_tool_options_args);
if (code != JNI_OK) {
return code;
}
code = parse_java_options_environment_variable(&initial_java_options_args);
if (code != JNI_OK) {
return code;
}
// Parse the options in the /java.base/jdk/internal/vm/options resource, if present
//通过解析module文件中的配置获取vm options (jdk17源码中未找到这个文件,使用jmod查看java.base的包也没找到这个配置)
char *vmoptions = ClassLoader::lookup_vm_options();
if (vmoptions != NULL) {
code = parse_options_buffer("vm options resource", vmoptions, strlen(vmoptions), &initial_vm_options_args);
FREE_C_HEAP_ARRAY(char, vmoptions);
if (code != JNI_OK) {
return code;
}
}
code = expand_vm_options_as_needed(initial_java_tool_options_args.get(),
&mod_java_tool_options_args,
&cur_java_tool_options_args);
if (code != JNI_OK) {
return code;
}
code = expand_vm_options_as_needed(initial_cmd_args,
&mod_cmd_args,
&cur_cmd_args);
if (code != JNI_OK) {
return code;
}
code = expand_vm_options_as_needed(initial_java_options_args.get(),
&mod_java_options_args,
&cur_java_options_args);
if (code != JNI_OK) {
return code;
}
code = expand_vm_options_as_needed(initial_vm_options_args.get(),
&mod_vm_options_args,
&cur_vm_options_args);
if (code != JNI_OK) {
return code;
}
const char* flags_file = Arguments::get_jvm_flags_file();
settings_file_specified = (flags_file != NULL);
if (IgnoreUnrecognizedVMOptions) {
cur_cmd_args->ignoreUnrecognized = true;
cur_java_tool_options_args->ignoreUnrecognized = true;
cur_java_options_args->ignoreUnrecognized = true;
}
// Parse specified settings file
if (settings_file_specified) {
if (!process_settings_file(flags_file, true,
cur_cmd_args->ignoreUnrecognized)) {
return JNI_EINVAL;
}
} else {
#ifdef ASSERT
// Parse default .hotspotrc settings file
if (!process_settings_file(".hotspotrc", false,
cur_cmd_args->ignoreUnrecognized)) {
return JNI_EINVAL;
}
#else
struct stat buf;
if (os::stat(hotspotrc, &buf) == 0) {
needs_hotspotrc_warning = true;
}
#endif
}
if (PrintVMOptions) {
print_options(cur_java_tool_options_args);
print_options(cur_cmd_args);
print_options(cur_java_options_args);
}
// Parse JavaVMInitArgs structure passed in, as well as JAVA_TOOL_OPTIONS and _JAVA_OPTIONS
jint result = parse_vm_init_args(cur_vm_options_args,
cur_java_tool_options_args,
cur_java_options_args,
cur_cmd_args);
if (result != JNI_OK) {
return result;
}
//...
apply_debugger_ergo();
return JNI_OK;
}
2、选择gc
上一步初始化完成参数设置后在这里选择具体的gc
具体选择的地方在threads create_vm中
jint ergo_result = Arguments::apply_ergo();
jinit Arguments::apply_ergo(){
jint result = set_ergonomics_flags();
if (result != JNI_OK) return result;
// Set heap size based on available physical memory
set_heap_size();
//选择gc后进行初始参数设置
GCConfig::arguments()->initialize();
//...
}
//调用了选择器
void GCConfig::initialize() {
assert(_arguments == NULL, "Already initialized");
_arguments = select_gc();
}
//选择对应的gc
GCArguments* GCConfig::select_gc() {
//...
// Exactly one GC selected
FOR_EACH_INCLUDED_GC(gc) {
if (gc->_flag) {
return &gc->_arguments;
}
}
fatal("Should have found the selected GC");
return NULL;
}
//当前版本支持的gc以及参数,UseG1GC这第一个参数就是系统设置的gc
static const IncludedGC IncludedGCs[] = {
EPSILONGC_ONLY_ARG(IncludedGC(UseEpsilonGC, CollectedHeap::Epsilon, epsilonArguments, "epsilon gc"))
G1GC_ONLY_ARG(IncludedGC(UseG1GC, CollectedHeap::G1, g1Arguments, "g1 gc"))
PARALLELGC_ONLY_ARG(IncludedGC(UseParallelGC, CollectedHeap::Parallel, parallelArguments, "parallel gc"))
SERIALGC_ONLY_ARG(IncludedGC(UseSerialGC, CollectedHeap::Serial, serialArguments, "serial gc"))
SHENANDOAHGC_ONLY_ARG(IncludedGC(UseShenandoahGC, CollectedHeap::Shenandoah, shenandoahArguments, "shenandoah gc"))
ZGC_ONLY_ARG(IncludedGC(UseZGC, CollectedHeap::Z, zArguments, "z gc"))
};
#define FOR_EACH_INCLUDED_GC(var) \
for (const IncludedGC* var = &IncludedGCs[0]; var < &IncludedGCs[ARRAY_SIZE(IncludedGCs)]; var++)
初始化选择了gc类型后调用到了同样在create_vm中的init_globals
在universe_init()中进行了gc初始化,完成后创建了SymbolTable,StringTable,ResolvedMethodTable
jint universe_init() {
assert(!Universe::_fully_initialized, "called after initialize_vtables");
guarantee(1 << LogHeapWordSize == sizeof(HeapWord),
"LogHeapWordSize is incorrect.");
guarantee(sizeof(oop) >= sizeof(HeapWord), "HeapWord larger than oop?");
guarantee(sizeof(oop) % sizeof(HeapWord) == 0,
"oop size is not not a multiple of HeapWord size");
TraceTime timer("Genesis", TRACETIME_LOG(Info, startuptime));
initialize_global_behaviours();
GCLogPrecious::initialize();
GCConfig::arguments()->initialize_heap_sizes();
//看这里
jint status = Universe::initialize_heap();
if (status != JNI_OK) {
return status;
}
Universe::initialize_tlab();
Metaspace::global_initialize();
// Initialize performance counters for metaspaces
MetaspaceCounters::initialize_performance_counters();
// Checks 'AfterMemoryInit' constraints.
if (!JVMFlagLimit::check_all_constraints(JVMFlagConstraintPhase::AfterMemoryInit)) {
return JNI_EINVAL;
}
// Create memory for metadata. Must be after initializing heap for
// DumpSharedSpaces.
ClassLoaderData::init_null_class_loader_data();
// We have a heap so create the Method* caches before
// Metaspace::initialize_shared_spaces() tries to populate them.
Universe::_finalizer_register_cache = new LatestMethodCache();
Universe::_loader_addClass_cache = new LatestMethodCache();
Universe::_throw_illegal_access_error_cache = new LatestMethodCache();
Universe::_throw_no_such_method_error_cache = new LatestMethodCache();
Universe::_do_stack_walk_cache = new LatestMethodCache();
#if INCLUDE_CDS
if (UseSharedSpaces) {
// Read the data structures supporting the shared spaces (shared
// system dictionary, symbol table, etc.). After that, access to
// the file (other than the mapped regions) is no longer needed, and
// the file is closed. Closing the file does not affect the
// currently mapped regions.
MetaspaceShared::initialize_shared_spaces();
StringTable::create_table();
} else
#endif
{
SymbolTable::create_table();
StringTable::create_table();
}
#if INCLUDE_CDS
if (Arguments::is_dumping_archive()) {
MetaspaceShared::prepare_for_dumping();
}
#endif
if (strlen(VerifySubSet) > 0) {
Universe::initialize_verify_flags();
}
ResolvedMethodTable::create_table();
return JNI_OK;
}
initialize_heap根据参数找到对应的gc参数进行初始化
jint Universe::initialize_heap() {
assert(_collectedHeap == NULL, "Heap already created");
_collectedHeap = GCConfig::arguments()->create_heap();
log_info(gc)("Using %s", _collectedHeap->name());
return _collectedHeap->initialize();
}
_collectedHeap在g1时为G1CollectedHeap,在其方法initialize中,开启了gc线程
如果是serial gc则不会异步开启线程
jint G1CollectedHeap::initialize() {
// Necessary to satisfy locking discipline assertions.
MutexLocker x(Heap_lock);
// While there are no constraints in the GC code that HeapWordSize
// be any particular value, there are multiple other areas in the
// system which believe this to be true (e.g. oop->object_size in some
// cases incorrectly returns the size in wordSize units rather than
// HeapWordSize).
guarantee(HeapWordSize == wordSize, "HeapWordSize must equal wordSize");
size_t init_byte_size = InitialHeapSize;
size_t reserved_byte_size = G1Arguments::heap_reserved_size_bytes();
// Ensure that the sizes are properly aligned.
Universe::check_alignment(init_byte_size, HeapRegion::GrainBytes, "g1 heap");
Universe::check_alignment(reserved_byte_size, HeapRegion::GrainBytes, "g1 heap");
Universe::check_alignment(reserved_byte_size, HeapAlignment, "g1 heap");
// Reserve the maximum.
// When compressed oops are enabled, the preferred heap base
// is calculated by subtracting the requested size from the
// 32Gb boundary and using the result as the base address for
// heap reservation. If the requested size is not aligned to
// HeapRegion::GrainBytes (i.e. the alignment that is passed
// into the ReservedHeapSpace constructor) then the actual
// base of the reserved heap may end up differing from the
// address that was requested (i.e. the preferred heap base).
// If this happens then we could end up using a non-optimal
// compressed oops mode.
ReservedHeapSpace heap_rs = Universe::reserve_heap(reserved_byte_size,
HeapAlignment);
initialize_reserved_region(heap_rs);
// Create the barrier set for the entire reserved region.
G1CardTable* ct = new G1CardTable(heap_rs.region());
ct->initialize();
G1BarrierSet* bs = new G1BarrierSet(ct);
bs->initialize();
assert(bs->is_a(BarrierSet::G1BarrierSet), "sanity");
BarrierSet::set_barrier_set(bs);
_card_table = ct;
{
G1SATBMarkQueueSet& satbqs = bs->satb_mark_queue_set();
satbqs.set_process_completed_buffers_threshold(G1SATBProcessCompletedThreshold);
satbqs.set_buffer_enqueue_threshold_percentage(G1SATBBufferEnqueueingThresholdPercent);
}
// Create the hot card cache.
_hot_card_cache = new G1HotCardCache(this);
// Create space mappers.
size_t page_size = heap_rs.page_size();
G1RegionToSpaceMapper* heap_storage =
G1RegionToSpaceMapper::create_mapper(heap_rs,
heap_rs.size(),
page_size,
HeapRegion::GrainBytes,
1,
mtJavaHeap);
if(heap_storage == NULL) {
vm_shutdown_during_initialization("Could not initialize G1 heap");
return JNI_ERR;
}
os::trace_page_sizes("Heap",
MinHeapSize,
reserved_byte_size,
page_size,
heap_rs.base(),
heap_rs.size());
heap_storage->set_mapping_changed_listener(&_listener);
// Create storage for the BOT, card table, card counts table (hot card cache) and the bitmaps.
G1RegionToSpaceMapper* bot_storage =
create_aux_memory_mapper("Block Offset Table",
G1BlockOffsetTable::compute_size(heap_rs.size() / HeapWordSize),
G1BlockOffsetTable::heap_map_factor());
G1RegionToSpaceMapper* cardtable_storage =
create_aux_memory_mapper("Card Table",
G1CardTable::compute_size(heap_rs.size() / HeapWordSize),
G1CardTable::heap_map_factor());
G1RegionToSpaceMapper* card_counts_storage =
create_aux_memory_mapper("Card Counts Table",
G1CardCounts::compute_size(heap_rs.size() / HeapWordSize),
G1CardCounts::heap_map_factor());
size_t bitmap_size = G1CMBitMap::compute_size(heap_rs.size());
G1RegionToSpaceMapper* prev_bitmap_storage =
create_aux_memory_mapper("Prev Bitmap", bitmap_size, G1CMBitMap::heap_map_factor());
G1RegionToSpaceMapper* next_bitmap_storage =
create_aux_memory_mapper("Next Bitmap", bitmap_size, G1CMBitMap::heap_map_factor());
_hrm.initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage);
_card_table->initialize(cardtable_storage);
// Do later initialization work for concurrent refinement.
_hot_card_cache->initialize(card_counts_storage);
// 6843694 - ensure that the maximum region index can fit
// in the remembered set structures.
const uint max_region_idx = (1U << (sizeof(RegionIdx_t)*BitsPerByte-1)) - 1;
guarantee((max_reserved_regions() - 1) <= max_region_idx, "too many regions");
// The G1FromCardCache reserves card with value 0 as "invalid", so the heap must not
// start within the first card.
guarantee(heap_rs.base() >= (char*)G1CardTable::card_size, "Java heap must not start within the first card.");
G1FromCardCache::initialize(max_reserved_regions());
// Also create a G1 rem set.
_rem_set = new G1RemSet(this, _card_table, _hot_card_cache);
_rem_set->initialize(max_reserved_regions());
size_t max_cards_per_region = ((size_t)1 << (sizeof(CardIdx_t)*BitsPerByte-1)) - 1;
guarantee(HeapRegion::CardsPerRegion > 0, "make sure it's initialized");
guarantee(HeapRegion::CardsPerRegion < max_cards_per_region,
"too many cards per region");
FreeRegionList::set_unrealistically_long_length(max_regions() + 1);
_bot = new G1BlockOffsetTable(reserved(), bot_storage);
{
size_t granularity = HeapRegion::GrainBytes;
_region_attr.initialize(reserved(), granularity);
_humongous_reclaim_candidates.initialize(reserved(), granularity);
}
//gc线程
_workers = new WorkGang("GC Thread", ParallelGCThreads,
true /* are_GC_task_threads */,
false /* are_ConcurrentGC_threads */);
if (_workers == NULL) {
return JNI_ENOMEM;
}
_workers->initialize_workers();
_numa->set_region_info(HeapRegion::GrainBytes, page_size);
// Create the G1ConcurrentMark data structure and thread.
// (Must do this late, so that "max_[reserved_]regions" is defined.)
//gc标记线程
_cm = new G1ConcurrentMark(this, prev_bitmap_storage, next_bitmap_storage);
_cm_thread = _cm->cm_thread();
// Now expand into the initial heap size.
if (!expand(init_byte_size, _workers)) {
vm_shutdown_during_initialization("Failed to allocate initial heap.");
return JNI_ENOMEM;
}
// Perform any initialization actions delegated to the policy.
policy()->init(this, &_collection_set);
jint ecode = initialize_concurrent_refinement();
if (ecode != JNI_OK) {
return ecode;
}
ecode = initialize_service_thread();
if (ecode != JNI_OK) {
return ecode;
}
// Initialize and schedule sampling task on service thread.
_rem_set->initialize_sampling_task(service_thread());
// Create and schedule the periodic gc task on the service thread.
_periodic_gc_task = new G1PeriodicGCTask("Periodic GC Task");
_service_thread->register_task(_periodic_gc_task);
{
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
dcqs.set_process_cards_threshold(concurrent_refine()->yellow_zone());
dcqs.set_max_cards(concurrent_refine()->red_zone());
}
// Here we allocate the dummy HeapRegion that is required by the
// G1AllocRegion class.
HeapRegion* dummy_region = _hrm.get_dummy_region();
// We'll re-use the same region whether the alloc region will
// require BOT updates or not and, if it doesn't, then a non-young
// region will complain that it cannot support allocations without
// BOT updates. So we'll tag the dummy region as eden to avoid that.
dummy_region->set_eden();
// Make sure it's full.
dummy_region->set_top(dummy_region->end());
G1AllocRegion::setup(this, dummy_region);
_allocator->init_mutator_alloc_regions();
// Do create of the monitoring and management support so that
// values in the heap have been properly initialized.
_g1mm = new G1MonitoringSupport(this);
_preserved_marks_set.init(ParallelGCThreads);
_collection_set.initialize(max_reserved_regions());
_regions_failed_evacuation = NEW_C_HEAP_ARRAY(volatile bool, max_regions(), mtGC);
G1InitLogger::print();
return JNI_OK;
}