内存代管理器TenuredGeneration作为基于内存分代管理的内存堆管理器GenCollectedHeap默认的旧生代管理器,它对垃圾对象的回收算法要比年青代Gc要复杂的多,但其主体思路就是:标记-清除-压缩.本文主要围绕内存代Gc时的三个过程来详细讲解TenuredGeneration是如何进行垃圾回收的.
一.Gc条件
可对 TenuredGeneration管理的旧生代进行Gc的条件主要有4个,满足其中的任何一个即可对旧生代进行垃圾回收:
- 1.当前是Full Gc
- 2.可在当前内存代分配请求的空间
- 3.当前内存代空闲空间<10000
- 4.当前内存代容量>上一次Gc之前的容量
- bool TenuredGeneration::should_collect(bool full, size_t size, bool is_tlab) {
- // This should be one big conditional or (||), but I want to be able to tell
- // why it returns what it returns (without re-evaluating the conditionals
- // in case they aren't idempotent), so I'm doing it this way.
- // DeMorgan says it's okay.
- bool result = false;
- if (!result && full) {
- result = true;
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr("TenuredGeneration::should_collect: because full");
- }
- }
- if (!result && should_allocate(size, is_tlab)) {
- result = true;
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr("TenuredGeneration::should_collect: because"
- " should_allocate(" SIZE_FORMAT ")", size);
- }
- }
- // If we don't have very much free space.
- // XXX: 10000 should be a percentage of the capacity!!!
- if (!result && free() < 10000) {
- result = true;
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr("TenuredGeneration::should_collect: because"
- " free(): " SIZE_FORMAT,
- free());
- }
- }
- // If we had to expand to accomodate promotions from younger generations
- if (!result && _capacity_at_prologue < capacity()) {
- result = true;
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr("TenuredGeneration::should_collect: because"
- "_capacity_at_prologue: " SIZE_FORMAT " < capacity(): " SIZE_FORMAT,
- _capacity_at_prologue, capacity());
- }
- }
- return result;
- }
二.Gc基本流程
默认的旧生代管理器TenuredGeneration回收垃圾对象的基本思路就是:
- 第一步: 标记所有的active对象
- 第二步: 计算所有active对象在其内存代压缩后的偏移位置
- 第三步: 更新所有active对象的地址映射表
- 第四步: 移动复制所有的active对象到新的存储位置
1.标记所有的active对象
TenuredGeneration标记所有active对象的过程跟年青代Gc相似,都是从根对象开始以深度优先的方式搜索标记所有的active对象,具体过程如下:
- /**
- * 从根对象开始递归迭代标记所有活动的对象
- */
- void GenMarkSweep::mark_sweep_phase1(int level,
- bool clear_all_softrefs) {
- // Recursively traverse all live objects and mark them
- TraceTime tm("phase 1", PrintGC && Verbose, true, gclog_or_tty);
- trace(" 1");
- VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false));
- GenCollectedHeap* gch = GenCollectedHeap::heap();
- // Because follow_root_closure is created statically, cannot
- // use OopsInGenClosure constructor which takes a generation,
- // as the Universe has not been created when the static constructors
- // are run.
- follow_root_closure.set_orig_generation(gch->get_gen(level));
- /**
- * 遍历当前所有的根对象并标记,并递归遍历标记它们的引用对象
- */
- gch->gen_process_strong_roots(level,
- false, // Younger gens are not roots.
- true, // activate StrongRootsScope
- true, // Collecting permanent generation.
- SharedHeap::SO_SystemClasses,
- &follow_root_closure,
- true, // walk code active on stacks
- &follow_root_closure);
- //标记所有的软引用对象
- {
- ref_processor()->setup_policy(clear_all_softrefs);
- ref_processor()->process_discovered_references(&is_alive, &keep_alive, &follow_stack_closure, NULL);
- }
- // Follow system dictionary roots and unload classes
- bool purged_class = SystemDictionary::do_unloading(&is_alive);
- // 清理代码高速缓冲区
- CodeCache::do_unloading(&is_alive, &keep_alive, purged_class);
- follow_stack(); // Flush marking stack
- // Update subklass/sibling/implementor links of live klasses
- follow_weak_klass_links();
- assert(_marking_stack.is_empty(), "just drained");
- //清理未被标记的软/弱引用对象
- follow_mdo_weak_refs();
- assert(_marking_stack.is_empty(), "just drained");
- //清除没有被引用的常量字符串
- StringTable::unlink(&is_alive);
- //清除符号表中没有被引用的符号
- SymbolTable::unlink();
- assert(_marking_stack.is_empty(), "stack should be empty by now");
- }
2.计算所有active对象在其内存代压缩后的偏移位置
以内存代为单位计算各内存代中的active对象压缩后的新存储位置:
- /**
- * 通过对象的标记,计算存活的对象在其内存区压缩后的偏移位置
- */
- void GenMarkSweep::mark_sweep_phase2() {
- // Now all live objects are marked, compute the new object addresses.
- // It is imperative that we traverse perm_gen LAST. If dead space is
- // allowed a range of dead object may get overwritten by a dead int
- // array. If perm_gen is not traversed last a klassOop may get
- // overwritten. This is fine since it is dead, but if the class has dead
- // instances we have to skip them, and in order to find their size we
- // need the klassOop!
- //
- // It is not required that we traverse spaces in the same order in
- // phase2, phase3 and phase4, but the ValidateMarkSweep live oops
- // tracking expects us to do so. See comment under phase4.
- GenCollectedHeap* gch = GenCollectedHeap::heap();
- Generation* pg = gch->perm_gen();
- TraceTime tm("phase 2", PrintGC && Verbose, true, gclog_or_tty);
- trace("2");
- VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false));
- //计算年青代+老生代中active对象在其内存代压缩后的偏移位置
- gch->prepare_for_compaction();
- VALIDATE_MARK_SWEEP_ONLY(_live_oops_index_at_perm = _live_oops_index);
- //计算永久代中active对象在其内存代压缩后的偏移位置
- CompactPoint perm_cp(pg, NULL, NULL);
- pg->prepare_for_compaction(&perm_cp);
- }
- /**
- * 计算内存代放入各个内存区中活着的对象在其压缩后的偏移位置
- */
- void Generation::prepare_for_compaction(CompactPoint* cp) {
- // Generic implementation, can be specialized
- CompactibleSpace* space = first_compaction_space();
- while (space != NULL) {
- space->prepare_for_compaction(cp);
- space = space->next_compaction_space();
- }
- }
- /**
- * 计算内存区中所有活着(被标记)的对象在其被压缩后的偏移位置
- */
- void CompactibleSpace::prepare_for_compaction(CompactPoint* cp) {
- SCAN_AND_FORWARD(cp, end, block_is_obj, block_size);
- }
- #define SCAN_AND_FORWARD(cp,scan_limit,block_is_obj,block_size) { \
- /* Compute the new addresses for the live objects and store it in the mark \
- * Used by universe::mark_sweep_phase2() \
- */ \
- HeapWord* compact_top; /* This is where we are currently compacting to. */ \
- \
- /* We're sure to be here before any objects are compacted into this \
- * space, so this is a good time to initialize this: \
- */ \
- set_compaction_top(bottom()); \
- \
- if (cp->space == NULL) { \
- assert(cp->gen != NULL, "need a generation"); \
- assert(cp->threshold == NULL, "just checking"); \
- assert(cp->gen->first_compaction_space() == this, "just checking"); \
- \
- cp->space = cp->gen->first_compaction_space(); \
- compact_top = cp->space->bottom(); \
- cp->space->set_compaction_top(compact_top); \
- cp->threshold = cp->space->initialize_threshold(); \
- } else { \
- compact_top = cp->space->compaction_top(); \
- } \
- \
- /* We allow some amount of garbage towards the bottom of the space, so \
- * we don't start compacting before there is a significant gain to be made.\
- * Occasionally, we want to ensure a full compaction, which is determined \
- * by the MarkSweepAlwaysCompactCount parameter. \
- */ \
- int invocations = SharedHeap::heap()->perm_gen()->stat_record()->invocations;\
- bool skip_dead = (MarkSweepAlwaysCompactCount < 1) \
- ||((invocations % MarkSweepAlwaysCompactCount) != 0); \
- \
- size_t allowed_deadspace = 0; \
- if (skip_dead) { \
- const size_t ratio = allowed_dead_ratio(); \
- allowed_deadspace = (capacity() * ratio / 100) / HeapWordSize; \
- } \
- \
- /** \
- * 当前内存区压缩的开始位置和结束位置 \
- */ \
- HeapWord* q = bottom(); \
- HeapWord* t = scan_limit(); \
- \
- HeapWord* end_of_live= q; /* One byte beyond the last byte of the last \
- live object. */ \
- HeapWord* first_dead = end();/* The first dead object. */ \
- LiveRange* liveRange = NULL; /* The current live range, recorded in the \
- first header of preceding free area. */ \
- _first_dead = first_dead; /*当前内存区中最后一个active对象*/ \
- \
- const intx interval = PrefetchScanIntervalInBytes; \
- \
- /** \
- * 开始遍历该内存区分配的所有对象 \
- */ \
- while (q < t) { \
- assert(!block_is_obj(q) || \
- oop(q)->mark()->is_marked() || oop(q)->mark()->is_unlocked() || \
- oop(q)->mark()->has_bias_pattern(), \
- "these are the only valid states during a mark sweep"); \
- /** \
- * active对象,则确定其新的存储位置 \
- */ \
- if (block_is_obj(q) && oop(q)->is_gc_marked()) { \
- /* prefetch beyond q */ \
- Prefetch::write(q, interval); \
- /* size_t size = oop(q)->size(); changing this for cms for perm gen */\
- /*对象大小*/ \
- size_t size = block_size(q); \
- compact_top = cp->space->forward(oop(q), size, cp, compact_top); \
- q += size; \
- end_of_live = q; \
- } else { \
- /* 垃圾对象,则一直遍历到其下一个active对象 */ \
- HeapWord* end = q; \
- do { \
- /* prefetch beyond end */ \
- Prefetch::write(end, interval); \
- end += block_size(end); \
- } while (end < t && (!block_is_obj(end) || !oop(end)->is_gc_marked()));\
- \
- /* see if we might want to pretend this object is alive so that \
- * we don't have to compact quite as often. \
- */ \
- if (allowed_deadspace > 0 && q == compact_top) { \
- size_t sz = pointer_delta(end, q); \
- if (insert_deadspace(allowed_deadspace, q, sz)) { \
- compact_top = cp->space->forward(oop(q), sz, cp, compact_top); \
- q = end; \
- end_of_live = end; \
- continue; \
- } \
- } \
- \
- /* otherwise, it really is a free region. */ \
- \
- /* for the previous LiveRange, record the end of the live objects. */ \
- if (liveRange) { \
- liveRange->set_end(q); \
- } \
- \
- /* record the current LiveRange object. \
- * liveRange->start() is overlaid on the mark word. \
- */ \
- liveRange = (LiveRange*)q; \
- liveRange->set_start(end); \
- liveRange->set_end(end); \
- \
- /* see if this is the first dead region. */ \
- if (q < first_dead) { \
- first_dead = q; \
- } \
- \
- /* move on to the next object */ \
- q = end; \
- } \
- } \
- \
- assert(q == t, "just checking"); \
- if (liveRange != NULL) { \
- liveRange->set_end(q); \
- } \
- _end_of_live = end_of_live; \
- if (end_of_live < first_dead) { \
- first_dead = end_of_live; \
- } \
- _first_dead = first_dead; \
- \
- /* save the compaction_top of the compaction space. */ \
- cp->space->set_compaction_top(compact_top); \
- }
3.更新所有active对象的地址映射表
该过程的实现和标记所有的active对象很相似,就是一个是标记,一个是更新地址指针:
- /**
- * 从根对象开始递归迭代更新所有活动对象的地址映射表(调整所有的对象指针)
- */
- void GenMarkSweep::mark_sweep_phase3(int level) {
- GenCollectedHeap* gch = GenCollectedHeap::heap();
- Generation* pg = gch->perm_gen();
- // Adjust the pointers to reflect the new locations
- TraceTime tm("phase 3", PrintGC && Verbose, true, gclog_or_tty);
- trace("3");
- VALIDATE_MARK_SWEEP_ONLY(reset_live_oop_tracking(false));
- // Needs to be done before the system dictionary is adjusted.
- pg->pre_adjust_pointers();
- // Because the two closures below are created statically, cannot
- // use OopsInGenClosure constructor which takes a generation,
- // as the Universe has not been created when the static constructors
- // are run.
- adjust_root_pointer_closure.set_orig_generation(gch->get_gen(level));
- adjust_pointer_closure.set_orig_generation(gch->get_gen(level));
- gch->gen_process_strong_roots(level,
- false, // Younger gens are not roots.
- true, // activate StrongRootsScope
- true, // Collecting permanent generation.
- SharedHeap::SO_AllClasses,
- &adjust_root_pointer_closure,
- false, // do not walk code
- &adjust_root_pointer_closure);
- // Now adjust pointers in remaining weak roots. (All of which should
- // have been cleared if they pointed to non-surviving objects.)
- CodeBlobToOopClosure adjust_code_pointer_closure(&adjust_pointer_closure,
- /*do_marking=*/ false);
- gch->gen_process_weak_roots(&adjust_root_pointer_closure,
- &adjust_code_pointer_closure,
- &adjust_pointer_closure);
- adjust_marks();
- GenAdjustPointersClosure blk;
- gch->generation_iterate(&blk, true);
- pg->adjust_pointers();
- }
- /**
- * 调整对象的物理地址指针
- */
- template <class T> inline void MarkSweep::adjust_pointer(T* p, bool isroot) {
- T heap_oop = oopDesc::load_heap_oop(p);
- if (!oopDesc::is_null(heap_oop)) {
- oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
- oop new_obj = oop(obj->mark()->decode_pointer()); //对象新的地址指针
- assert(new_obj != NULL || // is forwarding ptr?
- obj->mark() == markOopDesc::prototype() || // not gc marked?
- (UseBiasedLocking && obj->mark()->has_bias_pattern()) ||
- // not gc marked?
- obj->is_shared(), // never forwarded?
- "should be forwarded");
- if (new_obj != NULL) {
- assert(Universe::heap()->is_in_reserved(new_obj),
- "should be in object space");
- //更新对象的物理地址指针
- oopDesc::encode_store_heap_oop_not_null(p, new_obj);
- }
- }
- VALIDATE_MARK_SWEEP_ONLY(track_adjusted_pointer(p, isroot));
- }
4.移动复制所有的active对象到新的存储位置
该过程跟计算所有active对象在其内存代压缩后的偏移位置的操作流程很相似,这里不在详细赘述.
三.Gc后的内存代大小调整
内存堆管理器在对某些内存代进行Gc之后,基本都会调整他们的内存容量,即对内存代进行扩容还是缩容.这个操作主要受控于两个参数:最小空闲率(MinHeapFreeRatio)和最大空闲率MaxHeapFreeRatio.TenuredGeneration根据这两个参数来调整旧生代的容量过程如下:
- /**
- * 调整当前内存代的容量(一般发生在一次Gc之后)
- */
- void TenuredGeneration::compute_new_size() {
- assert(_shrink_factor <= 100, "invalid shrink factor");
- size_t current_shrink_factor = _shrink_factor;
- _shrink_factor = 0;
- printf("%s[%d] [tid: %lu]: 当前内存代[%s]开始调整其容量大小.\n", __FILE__, __LINE__, pthread_self(), this->name());
- // We don't have floating point command-line arguments
- // Note: argument processing ensures that MinHeapFreeRatio < 100.
- const double minimum_free_percentage = MinHeapFreeRatio / 100.0; //内存代的最小空闲率
- const double maximum_used_percentage = 1.0 - minimum_free_percentage;
- //一次Gc之后,当前内存代的使用量和容量
- const size_t used_after_gc = used();
- const size_t capacity_after_gc = capacity();
- //计算当前内存代期望的最小容量
- const double min_tmp = used_after_gc / maximum_used_percentage;
- size_t minimum_desired_capacity = (size_t)MIN2(min_tmp, double(max_uintx));
- // Don't shrink less than the initial generation size
- minimum_desired_capacity = MAX2(minimum_desired_capacity, spec()->init_size());
- assert(used_after_gc <= minimum_desired_capacity, "sanity check");
- if (PrintGC && Verbose) {
- const size_t free_after_gc = free();
- const double free_percentage = ((double)free_after_gc) / capacity_after_gc;
- gclog_or_tty->print_cr("TenuredGeneration::compute_new_size: ");
- gclog_or_tty->print_cr(" "
- " minimum_free_percentage: %6.2f"
- " maximum_used_percentage: %6.2f",
- minimum_free_percentage,
- maximum_used_percentage);
- gclog_or_tty->print_cr(" "
- " free_after_gc : %6.1fK"
- " used_after_gc : %6.1fK"
- " capacity_after_gc : %6.1fK",
- free_after_gc / (double) K,
- used_after_gc / (double) K,
- capacity_after_gc / (double) K);
- gclog_or_tty->print_cr(" "
- " free_percentage: %6.2f",
- free_percentage);
- }
- //当前内存代的实际容量小于期望的容量,则扩展当前内存代的容量
- if (capacity_after_gc < minimum_desired_capacity) {
- // If we have less free space than we want then expand
- size_t expand_bytes = minimum_desired_capacity - capacity_after_gc;
- // Don't expand unless it's significant
- if (expand_bytes >= _min_heap_delta_bytes) {
- printf("%s[%d] [tid: %lu]: Gc之后,试图为内存代[%s]的容量扩大 %lu bytes.\n", __FILE__, __LINE__, pthread_self(), this->name(), expand_bytes);
- expand(expand_bytes, 0); // safe if expansion fails
- }
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr(" expanding:"
- " minimum_desired_capacity: %6.1fK"
- " expand_bytes: %6.1fK"
- " _min_heap_delta_bytes: %6.1fK",
- minimum_desired_capacity / (double) K,
- expand_bytes / (double) K,
- _min_heap_delta_bytes / (double) K);
- }
- return;
- }
- // No expansion, now see if we want to shrink
- size_t shrink_bytes = 0;
- // We would never want to shrink more than this
- size_t max_shrink_bytes = capacity_after_gc - minimum_desired_capacity;
- if (MaxHeapFreeRatio < 100) {
- //计算当前内存代期望的大容量
- const double maximum_free_percentage = MaxHeapFreeRatio / 100.0;
- const double minimum_used_percentage = 1.0 - maximum_free_percentage;
- const double max_tmp = used_after_gc / minimum_used_percentage;
- size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx));
- maximum_desired_capacity = MAX2(maximum_desired_capacity, spec()->init_size());
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr(" "
- " maximum_free_percentage: %6.2f"
- " minimum_used_percentage: %6.2f",
- maximum_free_percentage,
- minimum_used_percentage);
- gclog_or_tty->print_cr(" "
- " _capacity_at_prologue: %6.1fK"
- " minimum_desired_capacity: %6.1fK"
- " maximum_desired_capacity: %6.1fK",
- _capacity_at_prologue / (double) K,
- minimum_desired_capacity / (double) K,
- maximum_desired_capacity / (double) K);
- }
- assert(minimum_desired_capacity <= maximum_desired_capacity,
- "sanity check");
- //当前内存代Gc之后的容量大于期望的最大容量
- if (capacity_after_gc > maximum_desired_capacity) {
- // Capacity too large, compute shrinking size
- shrink_bytes = capacity_after_gc - maximum_desired_capacity;
- // We don't want shrink all the way back to initSize if people call
- // System.gc(), because some programs do that between "phases" and then
- // we'd just have to grow the heap up again for the next phase. So we
- // damp the shrinking: 0% on the first call, 10% on the second call, 40%
- // on the third call, and 100% by the fourth call. But if we recompute
- // size without shrinking, it goes back to 0%.
- shrink_bytes = shrink_bytes / 100 * current_shrink_factor;
- assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size");
- if (current_shrink_factor == 0) {
- _shrink_factor = 10;
- } else {
- _shrink_factor = MIN2(current_shrink_factor * 4, (size_t) 100);
- }
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr(" "
- " shrinking:"
- " initSize: %.1fK"
- " maximum_desired_capacity: %.1fK",
- spec()->init_size() / (double) K,
- maximum_desired_capacity / (double) K);
- gclog_or_tty->print_cr(" "
- " shrink_bytes: %.1fK"
- " current_shrink_factor: %d"
- " new shrink factor: %d"
- " _min_heap_delta_bytes: %.1fK",
- shrink_bytes / (double) K,
- current_shrink_factor,
- _shrink_factor,
- _min_heap_delta_bytes / (double) K);
- }
- }
- }
- if (capacity_after_gc > _capacity_at_prologue) {
- //当前内存代Gc之后的容量大于Gc之前的容量,那么就是在内存代Gc时为了存储升级来的active对象而扩展了内存容量,
- //现在至少应该缩小到Gc之前的容量大小
- size_t expansion_for_promotion = capacity_after_gc - _capacity_at_prologue;
- expansion_for_promotion = MIN2(expansion_for_promotion, max_shrink_bytes);
- // We have two shrinking computations, take the largest
- shrink_bytes = MAX2(shrink_bytes, expansion_for_promotion);
- assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size");
- if (PrintGC && Verbose) {
- gclog_or_tty->print_cr(" "
- " aggressive shrinking:"
- " _capacity_at_prologue: %.1fK"
- " capacity_after_gc: %.1fK"
- " expansion_for_promotion: %.1fK"
- " shrink_bytes: %.1fK",
- capacity_after_gc / (double) K,
- _capacity_at_prologue / (double) K,
- expansion_for_promotion / (double) K,
- shrink_bytes / (double) K);
- }
- }
- // Don't shrink unless it's significant
- if (shrink_bytes >= _min_heap_delta_bytes) {
- printf("%s[%d] [tid: %lu]: Gc之后,试图为内存代[%s]的容量缩小 %lu bytes.\n", __FILE__, __LINE__, pthread_self(), this->name(), shrink_bytes);
- shrink(shrink_bytes);
- }
- assert(used() == used_after_gc && used_after_gc <= capacity(),
- "sanity check");
- }