1 /*
  2  * Copyright (c) 2013, 2019, Red Hat, Inc. All rights reserved.
  3  * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
  4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  5  *
  6  * This code is free software; you can redistribute it and/or modify it
  7  * under the terms of the GNU General Public License version 2 only, as
  8  * published by the Free Software Foundation.
  9  *
 10  * This code is distributed in the hope that it will be useful, but WITHOUT
 11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 13  * version 2 for more details (a copy is included in the LICENSE file that
 14  * accompanied this code).
 15  *
 16  * You should have received a copy of the GNU General Public License version
 17  * 2 along with this work; if not, write to the Free Software Foundation,
 18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 19  *
 20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 21  * or visit www.oracle.com if you need additional information or have any
 22  * questions.
 23  *
 24  */
 25 
 26 #include "gc/shenandoah/shenandoahFreeSet.hpp"
 27 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
 28 #include "gc/shenandoah/shenandoahHeapRegion.hpp"
 29 #include "gc/shenandoah/shenandoahMetrics.hpp"
 30 
 31 ShenandoahMetricsSnapshot::ShenandoahMetricsSnapshot() {
 32   _heap = ShenandoahHeap::heap();
 33 }
 34 
 35 void ShenandoahMetricsSnapshot::snap_before() {
 36   _used_before = _heap->used();
 37   _if_before = _heap->free_set()->internal_fragmentation();
 38   _ef_before = _heap->free_set()->external_fragmentation();
 39 }
 40 void ShenandoahMetricsSnapshot::snap_after() {
 41   _used_after = _heap->used();
 42   _if_after = _heap->free_set()->internal_fragmentation();
 43   _ef_after = _heap->free_set()->external_fragmentation();
 44 }
 45 
 46 // For degenerated GC, generation is Young in generational mode, Global in non-generational mode.
 47 // For full GC, generation is always Global.
 48 //
 49 // Note that the size of the chosen collection set is proportional to the relevant generation's collection set.
 50 // Note also that the generation size may change following selection of the collection set, as a side effect
 51 // of evacuation.  Evacuation may promote objects, causing old to grow and young to shrink.  Or this may be a
 52 // mixed evacuation.  When old regions are evacuated, this typically allows young to expand.  In all of these
 53 // various scenarios, the purpose of asking is_good_progress() is to determine if there is enough memory available
 54 // within young generation to justify making an attempt to perform a concurrent collection.  For this reason, we'll
 55 // use the current size of the generation (which may not be different than when the collection set was chosen) to
 56 // assess how much free memory we require in order to consider the most recent GC to have had good progress.
 57 
 58 bool ShenandoahMetricsSnapshot::is_good_progress(ShenandoahGeneration* generation) {
 59   // Under the critical threshold?
 60   ShenandoahFreeSet* free_set = _heap->free_set();
 61   size_t free_actual   = free_set->available();
 62   assert(free_actual != ShenandoahFreeSet::FreeSetUnderConstruction, "Avoid this race");
 63 
 64   // ShenandoahCriticalFreeThreshold is expressed as a percentage.  We multiple this percentage by 1/100th
 65   // of the generation capacity to determine whether the available memory within the generation exceeds the
 66   // critical threshold.
 67   size_t free_expected = (ShenandoahHeap::heap()->soft_max_capacity() / 100) * ShenandoahCriticalFreeThreshold;
 68 
 69   bool prog_free = free_actual >= free_expected;
 70   log_info(gc, ergo)("%s progress for free space: %zu%s, need %zu%s",
 71                      prog_free ? "Good" : "Bad",
 72                      byte_size_in_proper_unit(free_actual),   proper_unit_for_byte_size(free_actual),
 73                      byte_size_in_proper_unit(free_expected), proper_unit_for_byte_size(free_expected));
 74   if (!prog_free) {
 75     return false;
 76   }
 77 
 78   // Freed up enough?
 79   size_t progress_actual   = (_used_before > _used_after) ? _used_before - _used_after : 0;
 80   size_t progress_expected = ShenandoahHeapRegion::region_size_bytes();
 81   bool prog_used = progress_actual >= progress_expected;
 82   log_info(gc, ergo)("%s progress for used space: %zu%s, need %zu%s",
 83                      prog_used ? "Good" : "Bad",
 84                      byte_size_in_proper_unit(progress_actual),   proper_unit_for_byte_size(progress_actual),
 85                      byte_size_in_proper_unit(progress_expected), proper_unit_for_byte_size(progress_expected));
 86   if (prog_used) {
 87     return true;
 88   }
 89 
 90   // Internal fragmentation is down?
 91   double if_actual = _if_before - _if_after;
 92   double if_expected = 0.01; // 1% should be enough
 93   bool prog_if = if_actual >= if_expected;
 94   log_info(gc, ergo)("%s progress for internal fragmentation: %.1f%%, need %.1f%%",
 95                      prog_if ? "Good" : "Bad",
 96                      if_actual * 100, if_expected * 100);
 97   if (prog_if) {
 98     return true;
 99   }
100 
101   // External fragmentation is down?
102   double ef_actual = _ef_before - _ef_after;
103   double ef_expected = 0.01; // 1% should be enough
104   bool prog_ef = ef_actual >= ef_expected;
105   log_info(gc, ergo)("%s progress for external fragmentation: %.1f%%, need %.1f%%",
106                      prog_ef ? "Good" : "Bad",
107                      ef_actual * 100, ef_expected * 100);
108   if (prog_ef) {
109     return true;
110   }
111 
112   // Nothing good had happened.
113   return false;
114 }