GC悲观策略之Serial GC篇

5 篇文章 0 订阅
3 篇文章 0 订阅

仍然是http://blog.bluedavy.com/?p=166这篇blog中的代码,换成-Xms30m -Xmx30m -Xmn10m -XX:+UseSerialGC后执行的结果为YGC、YGC、YGC、YGC、FGC。
原因就在于Serial GC的悲观策略是不同的,Serial GC在执行YGC时,首先进入如下代码片段进行检查:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
void DefNewGeneration::collect( bool   full,
                                bool   clear_all_soft_refs,
                                size_t size,
                                bool   is_tlab) {
      ...
      if (!collection_attempt_is_safe()) {
          gch->set_incremental_collection_will_fail();
          return ;
      }
      ...
}
bool DefNewGeneration::collection_attempt_is_safe() {
   if (!to()->is_empty()) {
     return false ;
   }
   if (_next_gen == NULL) {
     GenCollectedHeap* gch = GenCollectedHeap::heap();
     _next_gen = gch->next_gen( this );
     assert (_next_gen != NULL,
            "This must be the youngest gen, and not the only gen" );
   }
 
   const double evacuation_ratio = MaxLiveObjectEvacuationRatio / 100.0;
 
   size_t worst_case_evacuation = ( size_t )(used() * evacuation_ratio);
   // 这里的_next_gen也就是旧生代了,下面贴出旧生代对应的代码
   return _next_gen->promotion_attempt_is_safe(worst_case_evacuation,
                                               HandlePromotionFailure);
}
bool TenuredGeneration::promotion_attempt_is_safe(
     size_t max_promotion_in_bytes,
     bool younger_handles_promotion_failure) const {
   bool result = max_contiguous_available() >= max_promotion_in_bytes;
   if (younger_handles_promotion_failure && !result) {
     result = max_contiguous_available() >=
       ( size_t ) gc_stats()->avg_promoted()->padded_average();
     if (PrintGC && Verbose && result) {
       gclog_or_tty->print_cr( "TenuredGeneration::promotion_attempt_is_safe"
                   " contiguous_available: " SIZE_FORMAT
                   " avg_promoted: " SIZE_FORMAT,
                   max_contiguous_available(),
                   gc_stats()->avg_promoted()->padded_average());
     }
   } else {
     if (PrintGC && Verbose) {
       gclog_or_tty->print_cr( "TenuredGeneration::promotion_attempt_is_safe"
                   " contiguous_available: " SIZE_FORMAT
                   " promotion_in_bytes: " SIZE_FORMAT,
                   max_contiguous_available(), max_promotion_in_bytes);
     }
   }
   return result;
}

这个检查首先是检查目前新生代中使用的空间是否大于了旧生代剩余的空间,如大于且HandlePromotionFailure为true(默认值),那么再检查旧生代剩余的空间是否大于之前平均晋升的old的大小,如大于则返回true,小于则返回false,在返回false的情况下,就不进行YGC的剩下的操作了。
按照这样的规则,在第九次循环的时候,应该执行的是FGC,而不是YGC,这里的原因是在Serial GC时,是先进行计数和时间的统计等,再调用DefNewGeneration的collect的,因此尽管这次没有真正的执行YGC的动作,但还是被计数和计入时间了,但这次为什么GC log中输出的不是Full GC呢,请看下面的代码片段:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void GenCollectedHeap::do_collection( bool  full,
                                      bool   clear_all_soft_refs,
                                      size_t size,
                                      bool   is_tlab,
                                      int    max_level) {
      ...
      // 在当前场景下,传入的full为false,因此complete为false
      bool complete = full && (max_level == (n_gens()-1));
      const char * gc_cause_str = "GC " ;
      if (complete) {
          GCCause::Cause cause = gc_cause();
          if (cause == GCCause::_java_lang_system_gc) {
             gc_cause_str = "Full GC (System) " ;
          } else {
            gc_cause_str = "Full GC " ;
          }
     }
     ...
     for ( int i = starting_level; i <= max_level; i++) {
       if (_gens[i]->should_collect(full, size, is_tlab)) {
           ...
           // Determine if allocation request was met.
           if (size > 0) {
             if (!is_tlab || _gens[i]->supports_tlab_allocation()) {
               if (size*HeapWordSize <= _gens[i]->unsafe_max_alloc_nogc()) {
                 size = 0;
              }
            }
          }
          ...
       }
}

从上可看出,当YGC结束后,eden的空间可以满足分配的需求的话,需要分配的对象的大小size就被置为零了,而在第九次循环中,由于YGC提前结束,因此eden的空间是仍然不足的,此时需要分配的size大小会不变,上面的GC动作还将进入TenuredGeneration的should_allocate来进行检查了,该方法的代码片段如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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 ;
   // 因为full是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;
}
virtual bool should_allocate( size_t word_size, bool is_tlab) {
     bool result = false ;
     // 32 bit上BitsPerSize_t为32,64 bit上为64, LogHeapWordSize在32 bit为2,64 bit为3
     size_t overflow_limit = ( size_t )1 << (BitsPerSize_t - LogHeapWordSize);
     if (!is_tlab || supports_tlab_allocation()) {
       result = (word_size > 0) && (word_size < overflow_limit);
     }
     return result;
   }

由此可看出,should_allocate为true,因此触发了FGC。

这样就可以理解为什么在第九次循环的时候打印出来的日志是没有Full GC字样的,但又计算为执行了一次YGC和一次FGC的。

由于Concurrent GC是基于Serial GC实现的,因此悲观策略是相同的。

ps: 如大家想研究这些东西,一方面是下载源码,另一方面也可以下载一个debug版本的jdk,这样就可以通过打开一些日志,看到更多的hotspot运行的细节,另外,也可以看出,Parallel GC的实现在代码上就清晰多了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值