elasticHeap.hpp 18.2 KB
Newer Older
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
/*
 * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation. Alibaba designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_ELASTICHEAP_HPP
#define SHARE_VM_GC_IMPLEMENTATION_G1_ELASTICHEAP_HPP

#include "gc_implementation/g1/heapRegionSet.hpp"
#include "gc_implementation/g1/heapRegion.hpp"
#include "runtime/mutex.hpp"
#include "runtime/thread.inline.hpp"
#include "utilities/exceptions.hpp"

#define GC_INTERVAL_SEQ_LENGTH 10

class ElasticHeap;

// ElasticHeapConcThread:
// Commit/uncommit memory (several GB) may cost significant time so we won't do it in STW.
// We use a concurrent thread for doing commit/uncommit/pretouch the memory of regions
// Heap regions will be removed from free list of _hrm and put into commit/uncommit list
// for doing the commit/uncommit job and moved back into _hrm in next GC stw
// after commit/uncommit finishes.

class ElasticHeapConcThread : public NamedThread {
friend class ElasticHeap;
private:
  ElasticHeap*        _elastic_heap;
  // List of regions to uncommit
  FreeRegionList      _uncommit_list;
  // List of regions to commit
  FreeRegionList      _commit_list;
  // List of old regions to free
  FreeRegionList      _to_free_list;

  // Indicate the commit/uncommit is working
  // Will be set when VM thread(GC) triggers ElasticHeapThread to do memory job
  // and be cleared when the memory job finishes
  //
  // Java threads(Jcmd listener or MXBean) will check this flag if the memory work is done
  // ElasticHeap::recover_for_full_gc will also check this flag
  volatile bool       _working;

  // ElasticHeapThread will wait on it for doing memory job
  // And VM trhead(GC) will notify ElasticHeapThread to do memory job via _conc_lock
  Monitor*            _conc_lock;
  // Java threads(Jcmd listener or MXBean) will wait on _working_lock for the finish of memory work
  // ElasticHeap::recover_for_full_gc will also wait on _working_lock for the finish of memory work
  Monitor*            _working_lock;

  // Number of parallel worker threads
  uint                _parallel_worker_threads;
  // Parallel workers
  FlexibleWorkGang*   _parallel_workers;
  // For terminating conc thread
  bool                _should_terminate;
  bool                _has_terminated;

  void                sanity_check();
  void                print_work_summary(uint uncommit_length, uint commit_length, double start);
  // Commit/Uncommit phyical pages of regions
  void                do_memory_job();
  // Commit/Uncommit phyical pages of regions
  uint                do_memory(FreeRegionList* list, HeapRegionClosure* cl);
  // Parallel Commit/Uncommit phyical pages
  void                par_work_on_regions(FreeRegionList* list, HeapRegionClosure* cl);
  void                wait_for_universe_init();

public:
  ElasticHeapConcThread(ElasticHeap* elastic_heap);

  // Region list for commit/uncommit
  FreeRegionList*     uncommit_list()           { return &_uncommit_list; }
  FreeRegionList*     commit_list()             { return &_commit_list; }
  FreeRegionList*     to_free_list()            { return &_to_free_list; }

  void                start();
  bool                working() const           { return _working; }
  void                set_working();
  void                clear_working()           {
    assert(_working, "Sanity");
    _working = false;
  }

  // Lock for manipulating ElasticHeapThread data structures
  Monitor*            conc_lock()               { return _conc_lock; }
  Monitor*            working_lock()            { return _working_lock; }
  void                sleep_before_next_cycle();
  virtual void        run();
  void                stop();

  // Count of regions in FreeRegionList
  uint                total_unavaiable_regions();
};

// Data structure for elastic heap timer thread
// We will trigger young gc in this thread
// but we cannot do it in a regular native thread(a limitation in JDK1.8)
// so we have to make it a Java thread
class ElasticHeapTimer : AllStatic {
  friend class ElasticHeap;
private:
  volatile static bool _should_terminate;
  static JavaThread*  _thread;
  static Monitor*     _monitor;

public:
  // Timer thread entry
  static void         timer_thread_entry(JavaThread* thread, TRAPS);
  static void         start();
  static void         stop();
  static bool         has_error(TRAPS, const char* error);
};

class ElasticHeapEvaluator : public CHeapObj<mtGC> {
protected:
  ElasticHeap*        _elas;
  G1CollectedHeap*    _g1h;
  G1CollectorPolicy*  _g1_policy;
  HeapRegionManager*  _hrm;
public:
  ElasticHeapEvaluator(ElasticHeap* eh);

  void                sanity_check();
  virtual void        evaluate() = 0;
  virtual void        evaluate_young()                  { ShouldNotReachHere(); }
  virtual void        evaluate_old()                    { ShouldNotReachHere(); }
  virtual void        evaluate_old_common();

  virtual bool        ready_to_initial_mark()       { ShouldNotReachHere(); }
};

class RecoverEvaluator : public ElasticHeapEvaluator {
public:
  RecoverEvaluator(ElasticHeap* eh)
    : ElasticHeapEvaluator(eh) {}
  virtual void        evaluate();
};

class PeriodicEvaluator : public ElasticHeapEvaluator {
public:
  PeriodicEvaluator(ElasticHeap* eh)
    : ElasticHeapEvaluator(eh) {}
  virtual void        evaluate();
  virtual void        evaluate_young();
  virtual void        evaluate_old()          { evaluate_old_common(); }
  virtual bool        ready_to_initial_mark();
};

class GenerationLimitEvaluator : public ElasticHeapEvaluator {
public:
  GenerationLimitEvaluator(ElasticHeap* eh)
    : ElasticHeapEvaluator(eh) {}
  virtual void        evaluate();
  virtual void        evaluate_young();
  virtual void        evaluate_old()          { evaluate_old_common(); }
  virtual bool        ready_to_initial_mark();
};

class SoftmxEvaluator : public ElasticHeapEvaluator {
public:
  SoftmxEvaluator(ElasticHeap* eh)
    : ElasticHeapEvaluator(eh) {}
  virtual void        evaluate();
};

class ElasticHeapGCStats : public CHeapObj<mtGC> {
friend class ElasticHeap;
public:
  ElasticHeapGCStats(ElasticHeap*, G1CollectedHeap*);

  double              avg_normalized_interval() const {
    return _recent_ygc_normalized_interval_ms->avg();
  }
  double              last_normalized_interval() const {
    return _recent_ygc_normalized_interval_ms->last();
  }
  int                 num_normalized_interval() const {
    return _recent_ygc_normalized_interval_ms->num();
  }
  uint                last_normalized_eden_consumed_length() const {
    return _last_normalized_eden_consumed_length;
  }
  double              last_initial_mark_timestamp_s() const {
    return _last_initial_mark_timestamp_s;
  }
  double              last_initial_mark_interval_s() const {
    return _last_initial_mark_interval_s;
  }
  size_t              last_initial_mark_non_young_bytes() const {
    return _last_initial_mark_non_young_bytes;
  }
  // Reset the tracking info of mixed gc
  void                reset_mixed_gc_info() {
    _last_gc_mixed = false;
    _mixed_gc_finished = false;;
    _need_mixed_gc = false;
  }

  bool                last_gc_mixed() const          { return _last_gc_mixed; }

  bool                need_mixed_gc() const          { return _need_mixed_gc; }
  void                set_need_mixed_gc(bool f)      { _need_mixed_gc = f; }

  double              last_gc_end_timestamp_s() const  { return _last_gc_end_timestamp_s; }
  void                set_last_gc_end_timestamp_s(double s) { _last_gc_end_timestamp_s = s; }

  void                track_gc_start(bool full_gc);

  bool                check_mixed_gc_finished();
private:
  ElasticHeap*        _elas;
  G1CollectedHeap*    _g1h;
  // G1 will use non-fixed size of young generation
  // We scale the gc interval to a normalized interval as max size of young generation
  // e.g. we make interval X 2 if last GC only used 1/2 of max size of young gen
  TruncatedSeq*       _recent_ygc_normalized_interval_ms;
  // Time of last GC in seccond
  volatile double     _last_gc_end_timestamp_s;
  // Time of initial mark in seccond
  volatile double     _last_initial_mark_timestamp_s;
  // If last gc is mixed gc
  volatile bool       _last_gc_mixed;
  // If mixed gc has finished
  volatile bool       _mixed_gc_finished;
  // If mixed gc needs to happen
  volatile bool       _need_mixed_gc;
  // The interval in second between last 2 intial-marks
  volatile double     _last_initial_mark_interval_s;
  // Bytes of non-young part in last initial mark
  size_t              _last_initial_mark_non_young_bytes;
  // Number of eden regions consumed in last gc interval(normalized)
  uint                _last_normalized_eden_consumed_length;
};

class ElasticHeapSetting;

// ElasticHeap:
// Main class of elastic heap feature
class ElasticHeap : public CHeapObj<mtGC> {
friend class ElasticHeapEvaluator;
friend class RecoverEvaluator;
friend class PeriodicEvaluator;
friend class GenerationLimitEvaluator;
friend class SoftmxEvaluator;
public:
  ElasticHeap(G1CollectedHeap* g1h);
  ~ElasticHeap();

  enum ErrorType {
    NoError = 0,
    HeapAdjustInProgress,
    GCTooFrequent,
    IllegalYoungPercent,
    IllegalMode
  };

  // Modes when elastic heap is activated
  enum EvaluationMode {
    InactiveMode = 0,
    PeriodicUncommitMode,
    GenerationLimitMode,
    SoftmxMode,
    EvaluationModeNum
  };

  static uint         ignore_arg();

  ElasticHeapGCStats* stats()         { return _stats; }
  ElasticHeapSetting* setting()       { return _setting; }
private:
  G1CollectedHeap*    _g1h;
  ElasticHeapGCStats* _stats;
  ElasticHeapSetting* _setting;
  // Initial value of InitiatingHeapOccupancyPercent
  uint                _orig_ihop;
  // Initial value of desired young length
  uint                _orig_min_desired_young_length;
  uint                _orig_max_desired_young_length;

  // Indicates in the concurrent working cycle of memory job
  // 1. A concurrent cycle starts (start_conc_cycle) in VM Thread(GC stw) when triggering
  // ElasticHeapConcThread to do memory job
  // 2. A concurrent cycle ends (end_conc_cycle) in the next gc pause after region
  // commit/uncommit finishes and move regions in ElasticHeapConcThread back into _hrm
  volatile bool       _in_conc_cycle;

  // Concurrent thread for doing memory job
  ElasticHeapConcThread*  _conc_thread;

  volatile int        _configure_setting_lock;

  ElasticHeapEvaluator* _evaluators[EvaluationModeNum];

  bool _heap_capacity_changed;

  void                start_conc_cycle();
  void                end_conc_cycle();

  void                move_regions_back_to_hrm();
  // Move the regions in _thread back to _hrm and clear in_cycle
  void                finish_conc_cycle();
  // Check completion of previous ElasticHeapConcThread work
  void                check_end_of_previous_conc_cycle();
  // Update heap size information after expanding completes
  void                update_expanded_heap_size();
  // Do the elastic heap evaluation and operation
  void                evaluate_elastic_work();

  void                try_starting_conc_cycle();

  void                wait_for_conc_thread_working_done(bool safepointCheck);
  bool                enough_free_regions_to_uncommit(uint num_regions_to_uncommit);

  // Update min/max desired young length after change young length
  void                update_desired_young_length(uint unavailable_young_length);

public:
  void                uncommit_region_memory(HeapRegion* hr);
  void                commit_region_memory(HeapRegion* hr, bool pretouch);
  void                free_region_memory(HeapRegion* hr);

  bool                in_conc_cycle()              { return _in_conc_cycle; }

  // Record gc information
  void                record_gc_start(bool full_gc = false);
  void                record_gc_end();

  void                prepare_in_gc_start();

  // Main entry in GC pause for elastic heap
  void                perform();
  // Commit/uncommit regions
  void                uncommit_regions(uint num);
  void                commit_regions(uint num);

  void                resize_young_length(uint target_length);
  void                change_heap_capacity(uint target_heap_regions);
  void                change_young_size_for_softmx();

  uint                max_young_length() const      { return _orig_max_desired_young_length; }

  // Number of regions that old gen overlap with young gen
  // So young gen cannot use as many as MaxNewSize
  uint                overlapped_young_regions_with_old_gen();

  uint                num_unavailable_regions();

  // Main entry for processing elastic heap via JCMD/MXBean
  ErrorType           configure_setting(uint young_percent, uint uncommit_ihop, uint softmx_percent);

  // Get the commit percent and freed bytes
  int                 young_commit_percent() const;
  jlong               young_uncommitted_bytes() const;
  int                 softmx_percent() const;
  jlong               uncommitted_bytes() const;
  int                 uncommit_ihop() const;

  // Wait for the conc thread working done and finish the current cycle
  void                wait_for_conc_cycle_end();
  // If Full GC happens in auto mode, elastic heap will wait to recover the uncommitted regions
  void                wait_to_recover();

  // If the gc is triggered by elastic heap to resize young gen
  bool                is_gc_to_resize_young_gen();
  // Max survivor regions in this gc.
  // We probably do a young gen resize in the end of gc
  // so we cannot leave a too large survior size if we will make young gen smaller
  uint                max_available_survivor_regions();

  // Check to initiate initial-mark
  void                check_to_initate_conc_mark();
  // Check to invoke a GC
  void                check_to_trigger_ygc();

  // With softmx mode, we will change the capacity of heap
  bool                heap_capacity_changed()     { return _heap_capacity_changed; }
  void                set_heap_capacity_changed(uint num);

  EvaluationMode      evaluation_mode() const;
  bool                conflict_mode(EvaluationMode target_mode);

  // Get string from error type
  static const char*  to_string(ErrorType type) {
    switch(type) {
      case HeapAdjustInProgress: return "last elastic-heap resize is still in progress";
      case GCTooFrequent: return "gc is too frequent";
      case IllegalYoungPercent: return "illegal percent";
      case IllegalMode: return "not in correct mode";
      default: ShouldNotReachHere(); return NULL;
    }
  }

  // Get string from EvaluationMode
  static const char*  to_string(EvaluationMode mode) {
    switch (mode) {
      case InactiveMode: return "inactive";
      case PeriodicUncommitMode: return "periodic uncommit";
      case GenerationLimitMode: return "generation limit";
      case SoftmxMode: return "softmx";
      default: ShouldNotReachHere(); return NULL;
    }
  }

  void                destroy();

  bool                can_turn_on_periodic_uncommit();

  bool                ready_to_uncommit_after_mixed_gc();
  void                uncommit_old_gen();


  uint                calculate_young_list_desired_max_length();
  double              g1_reserve_factor();
  uint                g1_reserve_regions();
};

class ElasticHeapSetting : public CHeapObj<mtGC> {
friend class ElasticHeap;
public:
  ElasticHeapSetting(ElasticHeap*, G1CollectedHeap*);
  // Whether do elastic heap resize by explicit command
  bool                generation_limit_set() const {
      return young_percent_set() || uncommit_ihop_set();
  }
  // Whether a jcmd/mxbean command set the young commit percent
  bool                young_percent_set() const {
    return _young_percent != 0;
  }
  uint                young_percent() const {
    return _young_percent;
  }
  uint                set_young_percent(uint p) {
    _young_percent = p;
  }
  // Whether a jcmd/mxbean command set the uncommit ihop
  bool                uncommit_ihop_set() const {
    return _uncommit_ihop != 0;
  }
  uint                uncommit_ihop() const {
    return _uncommit_ihop;
  }
  bool                softmx_percent_set() const {
    return _softmx_percent != 0;
  }
  uint                softmx_percent() const {
    return _softmx_percent;
  }
  void                set_softmx_percent(uint p) {
    _softmx_percent = p;
  }

  ElasticHeap::ErrorType change_young_percent(uint young_percent, bool& trigger_gc);
  ElasticHeap::ErrorType change_uncommit_ihop(uint uncommit_ihop, bool& trigger_gc);
  ElasticHeap::ErrorType change_softmx_percent(uint softmx_percent, bool& trigger_gc);
  ElasticHeap::ErrorType process_arg(uint young_percent, uint uncommit_ihop, uint softmx_percent,
                                     bool& trigger_gc);
  ElasticHeap::EvaluationMode target_evaluation_mode(uint young_percent,
                                             uint uncommit_ihop,
                                             uint softmx_percent);
private:
  ElasticHeap*        _elas;
  G1CollectedHeap*    _g1h;
  // Young list percentage set by command
  volatile uint       _young_percent;
  // Set the initiating heap occupancy for elastic heap and do uncommit after concurrent gc
  volatile uint       _uncommit_ihop;
  // Set the softmx percent
  volatile uint       _softmx_percent;

  // Check if we can change the young percent by jcmd/MXBean
  bool                can_change_young_percent(uint percent);

  static uint         ignore_arg()  { return (uint)-1; }
  static bool         ignore_arg(uint arg, uint cur_value)  {
    return arg == cur_value || arg == ignore_arg();
  }
};

#endif // SHARE_VM_GC_IMPLEMENTATION_G1_ELASTICHEAP_HPP