以jdk17中的g1为例
从universe_init()逐个查看
GCConfig::arguments()->initialize_heap_sizes();
//universe.cpp
GCConfig::arguments()->initialize_heap_sizes();
//调用到gcArguments.cpp
void GCArguments::initialize_heap_sizes() {
initialize_alignments();
//判断启动时传入的系统参数是否超过最大值
initialize_heap_flags_and_sizes();
//记录heap参数
initialize_size_info();
}
//主要看这个方法,实现在g1Arguments.cpp
void G1Arguments::initialize_alignments() {
//默认系统大小130862280,可以通过-Xmx修改
//方法中通过xmx计算出每个region的大小G1HeapRegionSize,必须要2048个region每个region要在1-32m之间
//通过计算完成后将G1HeapRegionSize写回到参数中
//CardsPerRegion = GrainBytes >> G1CardTable::card_shift 计算card数量,每个card右移九位=512byte
HeapRegion::setup_heap_region_size(MaxHeapSize);
// The remembered set needs the heap regions set up.
//记忆集,通过region size计算设置rset的相关参数(计算size二进制的的0的位数)得到G1RSetSparseRegionEntries和G1RSetRegionEntries
HeapRegionRemSet::setup_remset_size();
// Needs remembered set initialization as the ergonomics are based
// on it.
if (FLAG_IS_DEFAULT(G1EagerReclaimRemSetThreshold)) {
FLAG_SET_ERGO(G1EagerReclaimRemSetThreshold, G1RSetSparseRegionEntries);
}
//空间大小对其,等于region大小
SpaceAlignment = HeapRegion::GrainBytes;
//堆大小对齐,取值为card table,region,系统page三者中最大的值
HeapAlignment = calculate_heap_alignment(SpaceAlignment);
}
代码中出现的两个与g1相关的参数:
HeapRegion就是堆内存最小管理单位,一共2048个,大小通过最大内存进行计算,每个region记录了一段连续的内存地址,使用时分别对应top和end,如果是大对象超过了一个region,第一个是作为StartsHumongous region,除了最后一个都是ContinuesHumongous region,这些的top会指向end
remember set
与cardTable合作解决跨代(或跨region)回收,每个region对应一个rset
jint status = Universe::initialize_heap();
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();
}
//堆内部进行初始化
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);
//将上一步的堆设置到gc堆中
initialize_reserved_region(heap_rs);
// Create the barrier set for the entire reserved region.
G1CardTable* ct = new G1CardTable(heap_rs.region());
// cardtable初始化,创建了bitmap,其大小为heap中的heapword数量向上取64倍数
ct->initialize();
G1BarrierSet* bs = new G1BarrierSet(ct);
bs->initialize();
assert(bs->is_a(BarrierSet::G1BarrierSet), "sanity");
//这里面创建了主线程的dirty_card_queue和satb_mark_queue
//这俩都是跟并发gc相关的
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.
//热点card,写屏障在标记cards时延迟处理,提高吞吐量
_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());
//将内存放到HeapRegionManager方便管理
_hrm.initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage);
//初始化_byte_map
_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.");
//card缓存,AlwaysPreTouch这个参数生效位置,默认关闭开启时在初始化就实际分配heap内存
//cardcache初始化了一个数组,记录堆(rows)和线程(columns)的情况,形成了二维图
G1FromCardCache::initialize(max_reserved_regions());
// Also create a G1 rem set.
//g1记录卡表和heap信息的rset
_rem_set = new G1RemSet(this, _card_table, _hot_card_cache);
_rem_set->initialize(max_reserved_regions());
//一个region最大card,2^31 - 1,对应起来1T的内存,这个数字也就是Integer.MAX_VALUE,就是说card是用int做的数组存的
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表
_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;
}
//启动线程,可以在jstat看到
_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并发标记线程,开了一个主标记和多个并发标记,与ConcGCThreads参数有关
_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);
//refine线程
jint ecode = initialize_concurrent_refinement();
if (ecode != JNI_OK) {
return ecode;
}
//G1 Service线程
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.
// g1周期性的触发任务
_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.
// 复制的一个空region,头指向尾表明已满,region用来判空的
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负责Region内存的分配
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());
//初始化所有region回收标志布尔值的空间
_regions_failed_evacuation = NEW_C_HEAP_ARRAY(volatile bool, max_regions(), mtGC);
G1InitLogger::print();
return JNI_OK;
}
gc过程主要就是标记和清除的线程的工作过程
通过jstack <pid>的方式可以看到这些工作线程
GC Thread gc线程
G1 Main Marker 主要标记
G1 Conc 并发标记G1ConcurrentMark(与ConcGCThreads参数有关)
G1 Refine dirtycard队列更新到rset
G1 Service G1垃圾收集器的服务线程
VM Periodic Task Thread 定时触发gc
这一段太多后续单独梳理
Universe::initialize_tlab();
tlab是一个线程的固定空间,做为快速分配策略,在并发量上来后,创建小对象可以直接分配,不参与heap的锁竞争,提升性能
默认是开启的
void Universe::initialize_tlab() {
//最大空间不能超过int[Integer.MAX_VALUE],用于后续tlab真实处理时
ThreadLocalAllocBuffer::set_max_size(Universe::heap()->max_tlab_size());
if (UseTLAB) {
ThreadLocalAllocBuffer::startup_initialization();
}
}
//初始化当前线程
void ThreadLocalAllocBuffer::startup_initialization() {
ThreadLocalAllocStats::initialize();
// Assuming each thread's active tlab is, on average,
// 1/2 full at a GC
//默认的TLABWasteTargetPercent为1
_target_refills = 100 / (2 * TLABWasteTargetPercent);
// We need to set initial target refills to 2 to avoid a GC which causes VM
// abort during VM initialization.
_target_refills = MAX2(_target_refills, 2U);
//...
// During jvm startup, the main thread is initialized
// before the heap is initialized. So reinitialize it now.
guarantee(Thread::current()->is_Java_thread(), "tlab initialization thread not Java thread");
Thread::current()->tlab().initialize();
log_develop_trace(gc, tlab)("TLAB min: " SIZE_FORMAT " initial: " SIZE_FORMAT " max: " SIZE_FORMAT,
min_size(), Thread::current()->tlab().initial_desired_size(), max_size());
}
Thread::current()->tlab().initialize()
这一段是线程的初始化,java的thread调用的就是操作系统的线程方法
在thread.cpp中的方法JavaThread::run()可以同样看到initialize_tlab();
//初始化tlab关于分配对象的参数
void ThreadLocalAllocBuffer::initialize() {
initialize(NULL, // start
NULL, // top
NULL); // end
set_desired_size(initial_desired_size());
size_t capacity = Universe::heap()->tlab_capacity(thread()) / HeapWordSize;
// Keep alloc_frac as float and not double to avoid the double to float conversion
float alloc_frac = desired_size() * target_refills() / (float) capacity;
_allocation_fraction.sample(alloc_frac);
set_refill_waste_limit(initial_refill_waste_limit());
reset_statistics();
}