jvm(8) g1 gc线程和tlab初始化源码

以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();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容