g1 初始化堆过程分析

145 阅读6分钟

本文是基于jdk11的hotspot源码进行分析的

堆是什么?

堆就是进程中的一段动态内存,在java中,一般会先申请一大段堆内存,然后mutatar在这一大段内存中进行分配。

活动对象: 能够通过gc root访问到的对象

g1创建堆入口

  Universe::initialize_heap()
CollectedHeap* GCArguments::create_heap_with_policy() {
  Policy* policy = new Policy();
  policy->initialize_all();
  return new Heap(policy);
}

创建G1CollectorPolicy对象

G1CollectorPolicy::G1CollectorPolicy() {
  HeapRegion::setup_heap_region_size(InitialHeapSize, MaxHeapSize);
  //Region初始化时,会初始化一个RSet,主要用来记录并跟踪其它Region指向该Region中对象的引用
  HeapRegionRemSet::setup_remset_size();
}
  initial_heap_size,初始化堆大小
  max_heap_size: 最大堆大小
void HeapRegion::setup_heap_region_size(size_t initial_heap_size, size_t max_heap_size) {
  size_t region_size = G1HeapRegionSize;
  if (FLAG_IS_DEFAULT(G1HeapRegionSize)) {
    size_t average_heap_size = (initial_heap_size + max_heap_size) / 2;
    region_size = MAX2(average_heap_size / HeapRegionBounds::target_number(),HeapRegionBounds::min_size());
  }
  
  // 计算region_size是2的幂次方,如果region_size=4*1024*1024,则region_size_log=22
  int region_size_log = log2_long((jlong) region_size);
  
  // Recalculate the region size to make sure it's a power of
  // 2. This means that region_size is the largest power of 2 that's
  // <= what we've calculated so far.
  
  region_size = ((size_t)1 << region_size_log);
    log_info(gc)("setup_heap region_size(M):" SIZE_FORMAT,region_size/1024/1024);
  
  // Now make sure that we don't go over or under our limits.
  if (region_size < HeapRegionBounds::min_size()) {
    region_size = HeapRegionBounds::min_size();
  } else if (region_size > HeapRegionBounds::max_size()) {
    region_size = HeapRegionBounds::max_size();
  }

  // And recalculate the log. 算出region_size的lg值即2的几次方
  region_size_log = log2_long((jlong) region_size);

  // Now, set up the globals.
  guarantee(LogOfHRGrainBytes == 0, "we should only set it once");
  LogOfHRGrainBytes = region_size_log;

  guarantee(LogOfHRGrainWords == 0, "we should only set it once"); 
  //LogOfHRGrainWords=一个region的2次方再以8个字节对齐后的大小数
  LogOfHRGrainWords = LogOfHRGrainBytes - LogHeapWordSize;  
  //算出一个region,占8个字节后,几次方

  guarantee(GrainBytes == 0, "we should only set it once");
  // The cast to int is safe, given that we've bounded region_size by
  // MIN_REGION_SIZE and MAX_REGION_SIZE.
  // 设置细粒度字节数为计算出来的分区大小(region_size)
  GrainBytes = region_size; 
  log_info(gc, heap)("Heap region size: " SIZE_FORMAT "M", GrainBytes / M);
  guarantee(GrainWords == 0, "we should only set it once"); 
  
  //GrainWords=一个region再以8个字节对齐后的大小数
  GrainWords = GrainBytes >> LogHeapWordSize; //一个region能容纳多少字节
  guarantee((size_t) 1 << LogOfHRGrainWords == GrainWords, "sanity");

  guarantee(CardsPerRegion == 0, "we should only set it once");       //G1CardTable::card_shift=2的9次方
  CardsPerRegion = GrainBytes >> G1CardTable::card_shift; 
  //一个region有多少个卡页,card_shift占2的9次方=512,算出一个region有多少个卡面
  log_info(gc, heap)("一个region 对应CardsPerRegion[卡页]: " SIZE_FORMAT , CardsPerRegion);
  
  if (G1HeapRegionSize != GrainBytes) {
    FLAG_SET_ERGO(size_t, G1HeapRegionSize, GrainBytes);
  }
}
   执行垃圾回收策略类对象的初始化:policy->initialize_all();
   _space_alignment: 为了提高内存访问效率和遵循特定的内存对齐规则,需要确保空间按照一定的字节数进行对齐
   卡表: JVM中用于记录内存引用关系的一种数据结构
void G1CollectorPolicy::initialize_alignments() {
   // 空间对齐是1m
  _space_alignment = HeapRegion::GrainBytes; 
  // 卡表对齐:卡表大小* os的page大小= 512 * 16 * 1024
  size_t card_table_alignment = CardTableRS::ct_max_alignment_constraint();
  size_t page_size = UseLargePages ? os::large_page_size() : os::vm_page_size();
  _heap_alignment = MAX3(card_table_alignment, _space_alignment, page_size);
}

进入到G1CollectedHeap的构造方法

G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* collector_policy) :
  CollectedHeap(),
  _young_gen_sampling_thread(NULL),
  _collector_policy(collector_policy),
  _soft_ref_policy(),
  _card_table(NULL),
  _memory_manager("G1 Young Generation", "end of minor GC"),
  _full_gc_memory_manager("G1 Old Generation", "end of major GC"),
  _eden_pool(NULL),
  _survivor_pool(NULL),
  _old_pool(NULL),
  _gc_timer_stw(new (ResourceObj::C_HEAP, mtGC) STWGCTimer()),
  _gc_tracer_stw(new (ResourceObj::C_HEAP, mtGC) G1NewTracer()),
  _g1_policy(new G1Policy(_gc_timer_stw)),
  _collection_set(this, _g1_policy),
  _dirty_card_queue_set(false),
  _ref_processor_stw(NULL),
  _is_alive_closure_stw(this),
  _is_subject_to_discovery_stw(this),
  _ref_processor_cm(NULL),
  _is_alive_closure_cm(this),
  _is_subject_to_discovery_cm(this),
  _bot(NULL),
  _hot_card_cache(NULL),
  _g1_rem_set(NULL),
  _cr(NULL),
  _g1mm(NULL),
  _preserved_marks_set(true /* in_c_heap */),
  _old_set("Old Set", false /* humongous */, new OldRegionSetMtSafeChecker()),
  _humongous_set("Master Humongous Set", true /* humongous */, new HumongousRegionSetMtSafeChecker()),
  _humongous_reclaim_candidates(),
  _has_humongous_reclaim_candidates(false),
  _archive_allocator(NULL),
  _summary_bytes_used(0),
  _survivor_evac_stats("Young", YoungPLABSize, PLABWeight),
  _old_evac_stats("Old", OldPLABSize, PLABWeight),
  _expand_heap_after_alloc_failure(true),
  _old_marking_cycles_started(0),
  _old_marking_cycles_completed(0),
  _in_cset_fast_test() {
  //初始化Gang工作线程  
  _workers = new WorkGang("GC Thread", ParallelGCThreads,
                          /* are_GC_task_threads */true,
                          /* are_ConcurrentGC_threads */false);
                          
  _workers->initialize_workers();
  _verifier = new G1HeapVerifier(this);
   //分配器
  _allocator = new G1Allocator(this);

  _heap_sizing_policy = G1HeapSizingPolicy::create(this, _g1_policy->analytics());
  
   //HeapRegion::GrainWords=128,在G1垃圾收集器中,内存管理是以Region为单位的,假如region=1m,以8位对齐,那只有131072个words,大对象阈值就是占用一半
  
  _humongous_object_threshold_in_words = humongous_threshold_for(HeapRegion::GrainWords);
   //在G1中,“humongous”对象是指那些大小超过一个或多个Heap Region容量一半的对象。这类大型对象会被特殊处理,因为它们太大而无法放入普通的Heap Region中。G1垃圾收集器会为这些大型对象分配专门的Humongous Region
  // Override the default _filler_array_max_size so that no humongous filler
  // objects are created. 覆盖默认的_filler_array_max_size,这样就不会创建庞大的填充对象
  _filler_array_max_size = _humongous_object_threshold_in_words;
  //并行gc数 RefToScanQueueSet扫描队列是管理需要被扫描和回收的对象引用
  uint n_queues = ParallelGCThreads;
  _task_queues = new RefToScanQueueSet(n_queues);

  _evacuation_failed_info_array = NEW_C_HEAP_ARRAY(EvacuationFailedInfo, n_queues, mtGC);
   //为每个线程创建一个扫描队列
  for (uint i = 0; i < n_queues; i++) {
    RefToScanQueue* q = new RefToScanQueue();
    q->initialize();
    _task_queues->register_queue(i, q);
    ::new (&_evacuation_failed_info_array[i]) EvacuationFailedInfo();
  }

  // Initialize the G1EvacuationFailureALot counters and flags.初始化g1evacationfailurealot计数器和标志
  NOT_PRODUCT(reset_evacuation_should_fail();)
  _gc_tracer_stw->initialize();

  guarantee(_task_queues != NULL, "task_queues allocation failure.");
}
jint status = _collectedHeap->initialize(); //执行初始化
jint G1CollectedHeap::initialize() {
  os::enable_vtime();

size_t init_byte_size = collector_policy()->initial_heap_byte_size();
//G1GC 堆的最大大小
size_t max_byte_size = collector_policy()->max_heap_byte_size();
//g1gc 堆对齐为8192k
size_t heap_alignment = collector_policy()->heap_alignment();

// 返回ReservedHeapSpace对象,实际地申请VM堆,采用mmap方式,直接从操作系统中分配
ReservedSpace heap_rs = Universe::reserve_heap(max_byte_size,heap_alignment);
//初始化,从基地址开始:0x7e8800000,这里申请256m,尾地址:0x7f8800000
initialize_reserved_region((HeapWord*)heap_rs.base(), (HeapWord*)(heap_rs.base() + heap_rs.size()));

// CardTable会映射到整个堆的空间,每个卡片会对应堆中的512B空间,在一个大小为1 GB的堆下,那么CardTable的长度为2097151 (1GB / 512B);每个Region 大小为1 MB,每个Region都会对应2048个Card Page
// Create the barrier set for the entire reserved region.G1CardTable 是实现并发标记(Concurrent Marking)和混合收集(Mixed Collections)等垃圾收集操作,创建全局卡表
// G1CardTable用于跟踪和记录哪些内存区域(或称为“卡片”,Cards)包含对其他区域有引用的对象,它通过维护一个卡片表来高效地管理这些卡片,使得垃圾收集器能够快速定位到哪些卡片可能包含跨区域引用
G1CardTable* ct = new G1CardTable(reserved_region());
ct->initialize(); //它将堆空间划分为一系列2次幂大小的卡页,记录堆内存中哪些“卡片”(堆内存的小块区域)包含跨代引用(年轻代对象引用老年代对象