From 47fdd055dee9dc20508598e35490d899112673db Mon Sep 17 00:00:00 2001
Date: Thu, 20 Nov 2025 16:00:17 +0800
Subject: [PATCH] Add dynamic max heap size

---
 src/hotspot/cpu/aarch64/globals_aarch64.hpp   |  19 ++
 .../cpu/aarch64/vm_version_aarch64.cpp        |  25 ++
 .../cpu/aarch64/vm_version_aarch64.hpp        |  10 +-
 src/hotspot/os/linux/os_linux.cpp             |   8 +-
 src/hotspot/os/linux/os_linux.hpp             |   4 +-
 src/hotspot/share/cds/filemap.cpp             |   7 +-
 src/hotspot/share/classfile/vmSymbols.hpp     |   5 +
 src/hotspot/share/gc/g1/g1Arguments.cpp       |  19 +-
 src/hotspot/share/gc/g1/g1CollectedHeap.cpp   |  20 +-
 src/hotspot/share/gc/g1/g1CollectedHeap.hpp   |  26 +-
 .../share/gc/g1/g1HeapSizingPolicy.cpp        |  35 ++-
 src/hotspot/share/gc/g1/g1MemoryPool.cpp      |   1 +
 src/hotspot/share/gc/g1/g1MemoryPool.hpp      |   7 +
 .../share/gc/g1/g1MonitoringSupport.cpp       |   8 +
 .../share/gc/g1/g1MonitoringSupport.hpp       |   3 +
 src/hotspot/share/gc/g1/g1Policy.cpp          |   7 +-
 src/hotspot/share/gc/g1/g1VMOperations.cpp    | 129 +++++++++
 src/hotspot/share/gc/g1/g1VMOperations.hpp    |  12 +
 src/hotspot/share/gc/g1/heapRegionManager.cpp |  32 ++-
 src/hotspot/share/gc/g1/heapRegionManager.hpp |  18 +-
 .../share/gc/parallel/parallelArguments.cpp   |   6 +
 .../gc/parallel/parallelScavengeHeap.cpp      |  34 ++-
 .../gc/parallel/parallelScavengeHeap.hpp      |   4 +
 .../gc/parallel/psGenerationCounters.cpp      |  10 +-
 .../share/gc/parallel/psMemoryPool.hpp        |   2 +-
 src/hotspot/share/gc/parallel/psOldGen.cpp    |   9 +-
 src/hotspot/share/gc/parallel/psOldGen.hpp    |  24 +-
 .../share/gc/parallel/psVMOperations.cpp      | 252 ++++++++++++++++++
 .../share/gc/parallel/psVMOperations.hpp      |   8 +
 .../share/gc/parallel/psVirtualspace.cpp      |   7 +-
 .../share/gc/parallel/psVirtualspace.hpp      |  22 ++
 src/hotspot/share/gc/parallel/psYoungGen.cpp  |   9 +-
 src/hotspot/share/gc/parallel/psYoungGen.hpp  |  26 +-
 src/hotspot/share/gc/shared/collectedHeap.cpp |   1 +
 src/hotspot/share/gc/shared/collectedHeap.hpp |  11 +
 .../share/gc/shared/dynamicMaxHeap.cpp        | 150 +++++++++++
 .../share/gc/shared/dynamicMaxHeap.hpp        |  61 +++++
 src/hotspot/share/gc/shared/gcArguments.cpp   |   9 +
 src/hotspot/share/gc/shared/gcCause.cpp       |   3 +
 src/hotspot/share/gc/shared/gcCause.hpp       |   1 +
 src/hotspot/share/gc/shared/genArguments.cpp  |   9 +-
 src/hotspot/share/gc/shared/genArguments.hpp  |  15 ++
 .../share/gc/shared/generationCounters.cpp    |  17 +-
 .../share/gc/shared/generationCounters.hpp    |   5 +
 .../share/gc/shared/referencePolicy.cpp       |   3 +
 src/hotspot/share/memory/universe.cpp         |   3 +
 src/hotspot/share/memory/universe.hpp         |  45 ++++
 src/hotspot/share/runtime/arguments.cpp       |  15 ++
 src/hotspot/share/runtime/globals.hpp         |   3 +
 src/hotspot/share/runtime/os.cpp              |   4 +
 src/hotspot/share/runtime/os.hpp              |   2 +
 src/hotspot/share/runtime/threads.cpp         |   9 +
 src/hotspot/share/runtime/vmOperation.hpp     |   3 +-
 .../share/services/diagnosticCommand.cpp      | 117 ++++++++
 .../share/services/diagnosticCommand.hpp      |  53 ++++
 .../share/classes/java/nio/Bits.java          |  26 ++
 .../share/classes/jdk/internal/misc/VM.java   |   6 +
 test/hotspot/jtreg/gc/TestSmallHeap.java      |   2 +-
 .../jtreg/gc/arguments/TestMaxRAMFlags.java   |   2 +-
 .../jtreg/gc/dynamicmaxheap/BasicTest.java    |  94 +++++++
 .../dynamicmaxheap/DirectMemoryBasicTest.java |  76 ++++++
 .../dynamicmaxheap/LimitDirectMemoryTest.java |  92 +++++++
 .../gc/dynamicmaxheap/MemoryPoolTest.java     | 139 ++++++++++
 .../jtreg/gc/dynamicmaxheap/OptionsCheck.java |  73 +++++
 .../gc/dynamicmaxheap/RuntimeMemoryTest.java  |  96 +++++++
 .../jtreg/gc/dynamicmaxheap/TestBase.java     |  50 ++++
 .../LimitDirectMemoryTestBasic.java           |  63 +++++
 .../test_classes/NotActiveDirectMemory.java   |  41 +++
 .../test_classes/NotActiveHeap.java           |  27 ++
 69 files changed, 2091 insertions(+), 43 deletions(-)
 create mode 100644 src/hotspot/share/gc/shared/dynamicMaxHeap.cpp
 create mode 100644 src/hotspot/share/gc/shared/dynamicMaxHeap.hpp
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/BasicTest.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/DirectMemoryBasicTest.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/LimitDirectMemoryTest.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/MemoryPoolTest.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/OptionsCheck.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/RuntimeMemoryTest.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/TestBase.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/LimitDirectMemoryTestBasic.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/NotActiveDirectMemory.java
 create mode 100644 test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/NotActiveHeap.java

diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
index b26eaa4bf..d3d1c01bd 100644
--- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
@@ -127,6 +127,25 @@ define_pd_global(intx, InlineSmallCode,          1000);
           range(1, 99)                                                  \
   product(ccstr, UseBranchProtection, "none",                           \
           "Branch Protection to use: none, standard, pac-ret")          \
+                                                                        \
+  product(size_t, DynamicMaxHeapSizeLimit, ScaleForWordSize(96*M),      \
+          "The limit of Dynamic maximum heap size (in bytes)")          \
+                                                                        \
+  product(uintx, DynamicMaxHeapShrinkMinFreeRatio, 40,                  \
+          "Minimal ratio of free bytes after dynamic max heap shirnk")  \
+                                                                        \
+  product(size_t, ElasticMaxHeapSize, ScaleForWordSize(96*M),           \
+          "Elastic maximum heap size (in bytes)")                       \
+                                                                        \
+  product(bool, ElasticMaxHeap, false,                                  \
+          "Allow change max heap size during runtime with jcmd")        \
+                                                                        \
+  product(bool, TraceElasticMaxHeap, false,                             \
+          "Trace Elastic Max Heap adjustion logs and failure reasons")  \
+                                                                        \
+  product(uintx, ElasticMaxHeapShrinkMinFreeRatio, 40,                  \
+          "minimal ratio of free bytes after elastic max heap shirnk")  \
+                                                                        \
 
 // end of ARCH_FLAGS
 
diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
index 6c95dc9c8..62da2a0e8 100644
--- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
@@ -68,6 +68,31 @@ static SpinWait get_spin_wait_desc() {
   return SpinWait{};
 }
 
+int VM_Version::get_cpu_model() {
+  int cpu_lines = 0;
+  if (FILE *f = fopen("/proc/cpuinfo", "r")) {
+    char buf[128], *p;
+    while (fgets(buf, sizeof (buf), f) != NULL) {
+      if ((p = strchr(buf, ':')) != NULL) {
+        long v = strtol(p+1, NULL, 0);
+        if (strncmp(buf, "CPU implementer", sizeof "CPU implementer" - 1) == 0) {
+          _cpu = v;
+          cpu_lines++;
+        } else if (strncmp(buf, "CPU variant", sizeof "CPU variant" - 1) == 0) {
+          _variant = v;
+        } else if (strncmp(buf, "CPU part", sizeof "CPU part" - 1) == 0) {
+          if (_model != v)  _model2 = _model;
+          _model = v;
+        } else if (strncmp(buf, "CPU revision", sizeof "CPU revision" - 1) == 0) {
+          _revision = v;
+        }
+      }
+    }
+    fclose(f);
+  }
+  return cpu_lines;
+}
+
 void VM_Version::initialize() {
   _supports_cx8 = true;
   _supports_atomic_getset4 = true;
diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp
index aa7d5db44..a274565e5 100644
--- a/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.hpp
@@ -145,13 +145,21 @@ enum Ampere_CPU_Model {
   static bool supports_##name() { return (_features & CPU_##id) != 0; };
   CPU_FEATURE_FLAGS(CPU_FEATURE_DETECTION)
 #undef CPU_FEATURE_DETECTION
-
+  
+  static int get_cpu_model();
   static int cpu_family()                     { return _cpu; }
   static int cpu_model()                      { return _model; }
   static int cpu_model2()                     { return _model2; }
   static int cpu_variant()                    { return _variant; }
   static int cpu_revision()                   { return _revision; }
 
+  static bool is_hisi_enabled() {
+    if (_cpu == CPU_HISILICON && (_model == 0xd01 || _model == 0xd02)) {
+      return true;
+    }
+    return false;
+  }
+
   static bool model_is(int cpu_model) {
     return _model == cpu_model || _model2 == cpu_model;
   }
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index cf63e9e76..1f8b82361 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -3439,6 +3439,10 @@ bool os::pd_uncommit_memory(char* addr, size_t size, bool exec) {
   return res  != (uintptr_t) MAP_FAILED;
 }
 
+bool os::pd_free_heap_physical_memory(char *addr, size_t bytes) {
+  return madvise(addr, bytes, MADV_DONTNEED) == 0;
+}
+
 static address get_stack_commited_bottom(address bottom, size_t size) {
   address nbot = bottom;
   address ntop = bottom + size;
@@ -4707,7 +4711,7 @@ os::Linux::heap_vector_add_t os::Linux::_heap_vector_add;
 os::Linux::heap_vector_get_next_t os::Linux::_heap_vector_get_next;
 os::Linux::heap_vector_free_t os::Linux::_heap_vector_free;
 
-void os::Linux::load_plugin_library() {
+void os::Linux::load_ACC_library() {
 
 #if INCLUDE_JBOLT
     _jboltLog_precalc = CAST_TO_FN_PTR(jboltLog_precalc_t, dlsym(RTLD_DEFAULT, "JBoltLog_PreCalc"));
@@ -4891,7 +4895,7 @@ jint os::init_2(void) {
   init_adjust_stacksize_for_guard_pages();
 #endif
 
-  Linux::load_plugin_library();
+  Linux::load_ACC_library();
 
   if (UseNUMA || UseNUMAInterleaving) {
     Linux::numa_init();
diff --git a/src/hotspot/os/linux/os_linux.hpp b/src/hotspot/os/linux/os_linux.hpp
index 5c12ca7f0..dcadc2044 100644
--- a/src/hotspot/os/linux/os_linux.hpp
+++ b/src/hotspot/os/linux/os_linux.hpp
@@ -137,7 +137,7 @@ class os::Linux {
   static const char *libc_version()           { return _libc_version; }
   static const char *libpthread_version()     { return _libpthread_version; }
 
-  static void load_plugin_library();
+  static void load_ACC_library();
   static void libpthread_init();
   static void sched_getcpu_init();
   static bool libnuma_init();
@@ -498,7 +498,7 @@ class os::Linux {
       }
      return _heap_vector_add(val, heap_vector, _inserted);
   }
-  
+
   static void* heap_vector_get_next(void* heap_vector, void* heap_vector_node, int &_cnt, void** &_items) {
       if(_heap_vector_get_next == NULL) {
           return NULL;
diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp
index c091c1a00..2edc83bac 100644
--- a/src/hotspot/share/cds/filemap.cpp
+++ b/src/hotspot/share/cds/filemap.cpp
@@ -2093,8 +2093,10 @@ bool FileMapInfo::map_heap_region() {
     address heap_end = (address)heap_range.end();
     address mapped_heap_region_end = (address)_mapped_heap_memregion.end();
     assert(heap_end >= mapped_heap_region_end, "must be");
-    assert(heap_end - mapped_heap_region_end < (intx)(HeapRegion::GrainBytes),
+    if (!Universe::is_dynamic_max_heap_enable()) {
+      assert(heap_end - mapped_heap_region_end < (intx)(HeapRegion::GrainBytes),
            "must be at the top of the heap to avoid fragmentation");
+    }
 #endif
 
     ArchiveHeapLoader::set_mapped();
@@ -2113,6 +2115,9 @@ void FileMapInfo::init_heap_region_relocation() {
 
   address requested_bottom = (address)archive_range.start();
   address heap_end = (address)heap_range.end();
+  if (Universe::is_dynamic_max_heap_enable()) {
+    heap_end = (address)heap_range.start() + MaxHeapSize;
+  }
   assert(is_aligned(heap_end, HeapRegion::GrainBytes), "must be");
 
   // We map the archive heap region at the very top of the heap to avoid fragmentation.
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index 6f8a476e8..77810b3c3 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -788,6 +788,11 @@
   template(toFileURL_signature,                             "(Ljava/lang/String;)Ljava/net/URL;")                 \
   template(url_void_signature,                              "(Ljava/net/URL;)V")                                  \
                                                                                                                   \
+  /* ElasticMaxDirectMemory */                                                                                    \
+  template(java_nio_Bits,                          "java/nio/Bits")                                               \
+  template(updateMaxMemory_name,                   "updateMaxMemory")                                             \
+  template(updateMaxMemory_signature,              "(J)Ljava/lang/String;")                                       \
+                                                                                                                  \
   /* Thread.dump_to_file jcmd */                                                                                  \
   template(jdk_internal_vm_ThreadDumper,           "jdk/internal/vm/ThreadDumper")                                \
   template(dumpThreads_name,                       "dumpThreads")                                                 \
diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp
index 28f850938..cfa7b3785 100644
--- a/src/hotspot/share/gc/g1/g1Arguments.cpp
+++ b/src/hotspot/share/gc/g1/g1Arguments.cpp
@@ -37,6 +37,7 @@
 #include "runtime/globals.hpp"
 #include "runtime/globals_extension.hpp"
 #include "runtime/java.hpp"
+#include "memory/universe.hpp"
 
 static size_t calculate_heap_alignment(size_t space_alignment) {
   size_t card_table_alignment = CardTable::ct_max_alignment_constraint();
@@ -53,7 +54,18 @@ void G1Arguments::initialize_alignments() {
   // There is a circular dependency here. We base the region size on the heap
   // size, but the heap size should be aligned with the region size. To get
   // around this we use the unaligned values for the heap.
-  HeapRegion::setup_heap_region_size(MaxHeapSize);
+
+  if (Universe::is_dynamic_max_heap_enable()) {
+#ifdef AARCH64
+    if (!FLAG_IS_CMDLINE(DynamicMaxHeapSizeLimit) && !FLAG_IS_CMDLINE(ElasticMaxHeapSize)) {
+      guarantee(ElasticMaxHeap, "must be");
+      FLAG_SET_ERGO(DynamicMaxHeapSizeLimit, MaxHeapSize);
+    }
+    HeapRegion::setup_heap_region_size(DynamicMaxHeapSizeLimit);
+#endif //AARCH64
+  } else {
+    HeapRegion::setup_heap_region_size(MaxHeapSize);
+  }
 
   SpaceAlignment = HeapRegion::GrainBytes;
   HeapAlignment = calculate_heap_alignment(SpaceAlignment);
@@ -263,6 +275,11 @@ CollectedHeap* G1Arguments::create_heap() {
 }
 
 size_t G1Arguments::heap_reserved_size_bytes() {
+#ifdef AARCH64
+  if (Universe::is_dynamic_max_heap_enable()) {
+    return DynamicMaxHeapSizeLimit;
+  }
+#endif //AARCH64
   return MaxHeapSize;
 }
 
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
index 32d3b0787..d3ab782f5 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
@@ -2152,6 +2152,12 @@ size_t G1CollectedHeap::unsafe_max_tlab_alloc(Thread* ignored) const {
 }
 
 size_t G1CollectedHeap::max_capacity() const {
+  // Dynamic Max Heap
+  if (Universe::is_dynamic_max_heap_enable()) {
+    size_t cur_size = current_max_heap_size();
+    guarantee(cur_size <= max_regions() * HeapRegion::GrainBytes, "must be");
+    return cur_size;
+  }
   return max_regions() * HeapRegion::GrainBytes;
 }
 
@@ -2916,7 +2922,7 @@ public:
   }
 };
 
-void G1CollectedHeap::rebuild_region_sets(bool free_list_only) {
+void G1CollectedHeap::rebuild_region_sets(bool free_list_only, bool is_dynamic_max_heap_shrink) {
   assert_at_safepoint_on_vm_thread();
 
   if (!free_list_only) {
@@ -2932,7 +2938,10 @@ void G1CollectedHeap::rebuild_region_sets(bool free_list_only) {
   if (!free_list_only) {
     set_used(cl.total_used());
   }
-  assert_used_and_recalculate_used_equal(this);
+  // don't do this assert if is_dynamic_max_heap_shrink
+  if (!is_dynamic_max_heap_shrink) {
+    assert_used_and_recalculate_used_equal(this);
+  }
 }
 
 // Methods for the mutator alloc region
@@ -3170,3 +3179,10 @@ void G1CollectedHeap::finish_codecache_marking_cycle() {
   CodeCache::on_gc_marking_cycle_finish();
   CodeCache::arm_all_nmethods();
 }
+
+bool G1CollectedHeap::change_max_heap(size_t new_size) {
+  assert_heap_not_locked();
+  G1_ChangeMaxHeapOp op(new_size);
+  VMThread::execute(&op);
+  return op.resize_success();
+}
\ No newline at end of file
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
index 6afaeb6e6..73f092c8c 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
@@ -168,6 +168,8 @@
   // Testing classes.
   friend class G1CheckRegionAttrTableClosure;
 
+  friend class G1_ChangeMaxHeapOp;
+
 private:
   // GC Overhead Limit functionality related members.
   //
@@ -206,7 +208,7 @@ private:
   // reflect the contents of the heap. The only exception is the
   // humongous set which was not torn down in the first place. If
   // free_list_only is true, it will only rebuild the free list.
-  void rebuild_region_sets(bool free_list_only);
+  void rebuild_region_sets(bool free_list_only, bool is_dynamic_max_heap_shrink = false);
 
   // Callback for region mapping changed events.
   G1RegionMappingChangedListener _listener;
@@ -1317,6 +1319,28 @@ public:
 
   // Used to print information about locations in the hs_err file.
   bool print_location(outputStream* st, void* addr) const override;
+
+private:
+  // Dynamic Max Heap
+  // expected DynamicMaxHeap size during full gc (temp value)
+  // 0 means do not adjust
+  // min_gen_size <= _expected_dynamic_max_heap_size <= _reserved size.
+  // will be cleared after DynamicMaxHeap VM operation.
+  size_t _exp_dynamic_max_heap_size;
+public:
+  bool change_max_heap(size_t new_size) override;
+  size_t exp_dynamic_max_heap_size() const {
+    NOT_AARCH64(return 0;)
+    AARCH64_ONLY(return _exp_dynamic_max_heap_size;)
+  }
+  void set_exp_dynamic_max_heap_size(size_t size) {
+    guarantee(size <= _reserved.byte_size(), "must be");
+    _exp_dynamic_max_heap_size = size;
+  }
+  void update_gen_max_counter(size_t size) {
+    guarantee(Universe::is_dynamic_max_heap_enable(), "must be");
+    _monitoring_support->update_max_sizes(size);
+  }
 };
 
 // Scoped object that performs common pre- and post-gc heap printing operations.
diff --git a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp
index 8645b8470..f8ec414db 100644
--- a/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp
+++ b/src/hotspot/share/gc/g1/g1HeapSizingPolicy.cpp
@@ -31,6 +31,7 @@
 #include "runtime/globals.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/globalDefinitions.hpp"
+#include "memory/universe.hpp"
 
 G1HeapSizingPolicy* G1HeapSizingPolicy::create(const G1CollectedHeap* g1h, const G1Analytics* analytics) {
   return new G1HeapSizingPolicy(g1h, analytics);
@@ -197,7 +198,7 @@ size_t G1HeapSizingPolicy::young_collection_expansion_amount() {
   return expand_bytes;
 }
 
-static size_t target_heap_capacity(size_t used_bytes, uintx free_ratio) {
+static size_t target_heap_capacity(size_t used_bytes, uintx free_ratio, size_t max_heap_size) {
   const double desired_free_percentage = (double) free_ratio / 100.0;
   const double desired_used_percentage = 1.0 - desired_free_percentage;
 
@@ -207,7 +208,7 @@ static size_t target_heap_capacity(size_t used_bytes, uintx free_ratio) {
   double desired_capacity_d = used_bytes_d / desired_used_percentage;
   // Let's make sure that they are both under the max heap size, which
   // by default will make it fit into a size_t.
-  double desired_capacity_upper_bound = (double) MaxHeapSize;
+  double desired_capacity_upper_bound = (double) max_heap_size;
   desired_capacity_d = MIN2(desired_capacity_d, desired_capacity_upper_bound);
   // We can now safely turn it into size_t's.
   return (size_t) desired_capacity_d;
@@ -226,8 +227,10 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
                                // results.
                                _g1h->eden_regions_count() * HeapRegion::GrainBytes;
 
-  size_t minimum_desired_capacity = target_heap_capacity(used_after_gc, MinHeapFreeRatio);
-  size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, MaxHeapFreeRatio);
+  size_t max_heap_size = _g1h->current_max_heap_size();
+
+  size_t minimum_desired_capacity = target_heap_capacity(used_after_gc, MinHeapFreeRatio, max_heap_size);
+  size_t maximum_desired_capacity = target_heap_capacity(used_after_gc, MaxHeapFreeRatio, max_heap_size);
 
   // This assert only makes sense here, before we adjust them
   // with respect to the min and max heap size.
@@ -239,7 +242,7 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
   // Should not be greater than the heap max size. No need to adjust
   // it with respect to the heap min size as it's a lower bound (i.e.,
   // we'll try to make the capacity larger than it, not smaller).
-  minimum_desired_capacity = MIN2(minimum_desired_capacity, MaxHeapSize);
+  minimum_desired_capacity = MIN2(minimum_desired_capacity, max_heap_size);
   // Should not be less than the heap min size. No need to adjust it
   // with respect to the heap max size as it's an upper bound (i.e.,
   // we'll try to make the capacity smaller than it, not greater).
@@ -257,7 +260,27 @@ size_t G1HeapSizingPolicy::full_collection_resize_amount(bool& expand) {
     expand = true;
     return expand_bytes;
     // No expansion, now see if we want to shrink
-  } else if (capacity_after_gc > maximum_desired_capacity) {
+  } 
+  
+  size_t exp_size = _g1h->exp_dynamic_max_heap_size();
+  if (Universe::is_dynamic_max_heap_enable() &&
+      (exp_size > 0) &&
+      (exp_size < _g1h->capacity()) &&
+      (exp_size >= minimum_desired_capacity) &&
+      (exp_size <= maximum_desired_capacity)) {
+    // shrink to exp_dynamic_max_heap_size when
+    // 1. exp_dynamic_max_heap_size smaller than capacity
+    // 2. exp_dynamic_max_heap_size bigger than minimum_desired_capacity
+    size_t shrink_bytes = _g1h->capacity() - exp_size;
+    log_debug(gc, ergo, heap)("Attempt heap shrinking for dynamic max heap(capacity higher than expected dynamic max heap after Full GC)."
+                              "Capacity: " SIZE_FORMAT "B occupancy: " SIZE_FORMAT "B "
+                              "expected_dynamic_max_heap: " SIZE_FORMAT "B ",
+                              capacity_after_gc, used_after_gc, exp_size);
+    expand = false;
+    return shrink_bytes;
+  }
+  
+  if (capacity_after_gc > maximum_desired_capacity) {
     // Capacity too large, compute shrinking size
     size_t shrink_bytes = capacity_after_gc - maximum_desired_capacity;
 
diff --git a/src/hotspot/share/gc/g1/g1MemoryPool.cpp b/src/hotspot/share/gc/g1/g1MemoryPool.cpp
index 428d2db21..82109656c 100644
--- a/src/hotspot/share/gc/g1/g1MemoryPool.cpp
+++ b/src/hotspot/share/gc/g1/g1MemoryPool.cpp
@@ -27,6 +27,7 @@
 #include "gc/g1/g1MemoryPool.hpp"
 #include "gc/g1/heapRegion.hpp"
 #include "gc/shared/gc_globals.hpp"
+#include "memory/universe.hpp"
 
 G1MemoryPoolSuper::G1MemoryPoolSuper(G1CollectedHeap* g1h,
                                      const char* name,
diff --git a/src/hotspot/share/gc/g1/g1MemoryPool.hpp b/src/hotspot/share/gc/g1/g1MemoryPool.hpp
index 4d7128b77..a3b71f6f2 100644
--- a/src/hotspot/share/gc/g1/g1MemoryPool.hpp
+++ b/src/hotspot/share/gc/g1/g1MemoryPool.hpp
@@ -61,6 +61,13 @@ protected:
                     size_t init_size,
                     size_t max_size,
                     bool support_usage_threshold);
+  size_t max_size() const override {
+    if (Universe::is_dynamic_max_heap_enable()) {
+      G1CollectedHeap* heap = static_cast<G1CollectedHeap*>(Universe::heap());
+      return heap->max_capacity();
+    }
+    return MemoryPool::max_size();
+  }
 };
 
 // Memory pool that represents the G1 eden.
diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
index c4f2c1284..07cfe56c6 100644
--- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
+++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
@@ -323,6 +323,14 @@ void G1MonitoringSupport::update_eden_size() {
   }
 }
 
+void G1MonitoringSupport::update_max_sizes(size_t size) {
+  if (UsePerfData) {
+    _young_gen_counters->update_max_size(size);
+    _old_gen_counters->update_max_size(size);
+    update_sizes();
+  }
+}
+
 MemoryUsage G1MonitoringSupport::eden_space_memory_usage(size_t initial_size, size_t max_size) {
   MutexLocker x(MonitoringSupport_lock, Mutex::_no_safepoint_check_flag);
 
diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp
index 769a6fe83..c42cb6d6c 100644
--- a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp
+++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp
@@ -198,6 +198,9 @@ public:
 
   void update_eden_size();
 
+  // Dynamic Max Heap
+  void update_max_sizes(size_t size);
+
   // Monitoring support used by
   //   MemoryService
   //   jstat counters
diff --git a/src/hotspot/share/gc/g1/g1Policy.cpp b/src/hotspot/share/gc/g1/g1Policy.cpp
index 70b7d796c..5480e60da 100644
--- a/src/hotspot/share/gc/g1/g1Policy.cpp
+++ b/src/hotspot/share/gc/g1/g1Policy.cpp
@@ -49,6 +49,7 @@
 #include "utilities/debug.hpp"
 #include "utilities/growableArray.hpp"
 #include "utilities/pair.hpp"
+#include "memory/universe.hpp"
 
 #include "gc/shared/gcTraceTime.inline.hpp"
 
@@ -97,7 +98,11 @@ void G1Policy::init(G1CollectedHeap* g1h, G1CollectionSet* collection_set) {
 
   assert(Heap_lock->owned_by_self(), "Locking discipline.");
 
-  _young_gen_sizer.adjust_max_new_size(_g1h->max_regions());
+  if (Universe::is_dynamic_max_heap_enable()) {
+    _young_gen_sizer.adjust_max_new_size(static_cast<uint>(_g1h->current_max_heap_size() / HeapRegion::GrainBytes));
+  } else {
+    _young_gen_sizer.adjust_max_new_size(_g1h->max_regions());
+  }
 
   _free_regions_at_end_of_collection = _g1h->num_free_regions();
 
diff --git a/src/hotspot/share/gc/g1/g1VMOperations.cpp b/src/hotspot/share/gc/g1/g1VMOperations.cpp
index bdee54c8a..d969b5d78 100644
--- a/src/hotspot/share/gc/g1/g1VMOperations.cpp
+++ b/src/hotspot/share/gc/g1/g1VMOperations.cpp
@@ -194,3 +194,132 @@ void VM_G1PauseCleanup::work() {
   G1CollectedHeap* g1h = G1CollectedHeap::heap();
   g1h->concurrent_mark()->cleanup();
 }
+
+G1_ChangeMaxHeapOp::G1_ChangeMaxHeapOp(size_t new_max_heap) :
+  VM_ChangeMaxHeapOp(new_max_heap) {
+}
+
+/*
+ * No need calculate young/old size, shrink will adjust young automatically.
+ * ensure young_list_length, _young_list_max_length, _young_list_target_length align.
+ *
+ * 1. check if need perform gc: new_heap_max >= minimum_desired_capacity
+ * 2. perform full GC if necessary
+ * 3. update new limit
+ * 4. validation
+ */
+void G1_ChangeMaxHeapOp::doit() {
+  G1CollectedHeap* heap      = static_cast<G1CollectedHeap*>(Universe::heap());
+  const size_t min_heap_size = MinHeapSize;
+  const size_t max_heap_size = heap->current_max_heap_size();
+  bool is_shrink             = _new_max_heap < max_heap_size;
+
+  // step1. calculate maximum_used_percentage for shrink validity check
+  const double minimum_free_percentage = static_cast<double>(MinHeapFreeRatio) / 100.0;
+  const double maximum_used_percentage = 1.0 - minimum_free_percentage;
+
+  // step2. trigger GC as needed and resize
+  if (is_shrink) {
+    trigger_gc_shrink(_new_max_heap, maximum_used_percentage, max_heap_size);
+  }
+
+  log_debug(dynamic, heap)("G1_ElasticMaxHeapOp: current capacity " SIZE_FORMAT "K, new max heap " SIZE_FORMAT "K",
+                            heap->capacity() / K, _new_max_heap / K);
+
+  // step3. check if can update new limit
+  if (heap->capacity() <= _new_max_heap) {
+    uint dynamic_max_heap_len = static_cast<uint>(_new_max_heap / HeapRegion::GrainBytes);
+    heap->set_current_max_heap_size(_new_max_heap);
+    heap->_hrm.set_dynamic_max_heap_length(dynamic_max_heap_len);
+    // G1 young/old share same max size
+    heap->update_gen_max_counter(_new_max_heap);
+    _resize_success = true;
+    log_debug(dynamic, heap)("G1_ElasticMaxHeapOp success");
+  } else {
+    log_debug(dynamic, heap)("G1_ElasticMaxHeapOp fail");
+  }
+}
+
+bool DynamicMaxHeap_G1CanShrink(double used_after_gc_d, size_t _new_max_heap, double maximum_used_percentage, size_t max_heap_size) {
+  double minimum_desired_capacity_d = used_after_gc_d / maximum_used_percentage;
+  double desired_capacity_upper_bound = static_cast<double>(max_heap_size);
+  minimum_desired_capacity_d = (minimum_desired_capacity_d < desired_capacity_upper_bound) ? minimum_desired_capacity_d : desired_capacity_upper_bound;
+  size_t minimum_desired_capacity = static_cast<size_t>(minimum_desired_capacity_d);
+  minimum_desired_capacity = (minimum_desired_capacity < max_heap_size)? minimum_desired_capacity : max_heap_size;
+  bool can_shrink = (_new_max_heap >= minimum_desired_capacity);
+  return can_shrink;
+}
+
+void G1_ChangeMaxHeapOp::trigger_gc_shrink(size_t _new_max_heap,
+                                      double maximum_used_percentage,
+                                      size_t max_heap_size){
+  G1CollectedHeap* heap      = static_cast<G1CollectedHeap*>(Universe::heap());
+  G1CollectorState* collector_state = heap->collector_state();
+  bool triggered_full_gc = false;
+  bool can_shrink = DynamicMaxHeap_G1CanShrink(static_cast<double>(heap->used()), _new_max_heap, maximum_used_percentage, max_heap_size);
+  if (!can_shrink) {
+    // trigger Young GC
+    collector_state->set_in_young_only_phase(true);
+    collector_state->set_in_young_gc_before_mixed(true);
+    GCCauseSetter gccs(heap, _gc_cause);
+    bool minor_gc_succeeded = heap->do_collection_pause_at_safepoint();
+    if (minor_gc_succeeded) {
+      log_debug(dynamic, heap)("G1_ElasticMaxHeapOp heap after Young GC");
+      LogTarget(Debug, dynamic, heap) lt;
+      if (lt.is_enabled()) {
+        LogStream ls(lt);
+        heap->print_on(&ls);
+      }
+    }
+    can_shrink = DynamicMaxHeap_G1CanShrink(static_cast<double>(heap->used()), _new_max_heap, maximum_used_percentage, max_heap_size);
+    if (!can_shrink) {
+      // trigger Full GC and adjust everything in resize_if_necessary_after_full_collection
+      heap->set_exp_dynamic_max_heap_size(_new_max_heap);
+      heap->do_full_collection(true);
+      log_debug(dynamic, heap)("G1_ElasticMaxHeapOp heap after Full GC");
+      LogTarget(Debug, dynamic, heap) lt;
+      if (lt.is_enabled()) {
+        LogStream ls(lt);
+        heap->print_on(&ls);
+      }
+      heap->set_exp_dynamic_max_heap_size(0);
+      triggered_full_gc = true;
+    }
+  }
+
+  if (!triggered_full_gc) {
+    // there may be two situations when entering this branch:
+    //     1. first check passed, no GC triggered
+    //     2. first check failed, triggered Young GC,
+    //        second check passed
+    // so the shrink has not been completed and it must be valid to shrink
+    g1_shrink_without_full_gc(_new_max_heap);
+  }
+}
+
+void G1_ChangeMaxHeapOp::g1_shrink_without_full_gc(size_t _new_max_heap) {
+  G1CollectedHeap* heap      = static_cast<G1CollectedHeap*>(Universe::heap());
+  size_t capacity_before_shrink = heap->capacity();
+  // _new_max_heap is large enough, do nothing
+  if (_new_max_heap >= capacity_before_shrink) {
+    return;
+  }
+  // Capacity too large, compute shrinking size and shrink
+  size_t shrink_bytes = capacity_before_shrink - _new_max_heap;
+  heap->_verifier->verify_region_sets_optional();
+  heap->_hrm.remove_all_free_regions();
+  heap->shrink_helper(shrink_bytes);
+  heap->rebuild_region_sets(true /* free_list_only */, true /* is_dynamic_max_heap_shrink */);
+  heap->_hrm.verify_optional();
+  heap->_verifier->verify_region_sets_optional();
+  heap->_verifier->verify_after_gc();
+
+  log_debug(dynamic, heap)("G1_ElasticMaxHeapOp: attempt heap shrinking for dynamic max heap %s "
+          "origin capacity " SIZE_FORMAT "K "
+          "new capacity " SIZE_FORMAT "K "
+          "shrink by " SIZE_FORMAT "K",
+           heap->capacity() <= _new_max_heap ? "success" : "fail",
+           capacity_before_shrink / K,
+           heap->capacity() / K,
+           shrink_bytes / K);
+}
\ No newline at end of file
diff --git a/src/hotspot/share/gc/g1/g1VMOperations.hpp b/src/hotspot/share/gc/g1/g1VMOperations.hpp
index cfca9e21d..317c2b3e9 100644
--- a/src/hotspot/share/gc/g1/g1VMOperations.hpp
+++ b/src/hotspot/share/gc/g1/g1VMOperations.hpp
@@ -27,6 +27,7 @@
 
 #include "gc/shared/gcId.hpp"
 #include "gc/shared/gcVMOperations.hpp"
+#include "gc/shared/dynamicMaxHeap.hpp"
 
 // VM_operations for the G1 collector.
 
@@ -109,4 +110,15 @@ public:
   void work() override;
 };
 
+// Change Dynamic Max Heap Size
+class G1_ChangeMaxHeapOp : public VM_ChangeMaxHeapOp {
+public:
+  G1_ChangeMaxHeapOp(size_t new_max_heap);
+  void doit() override;
+  void trigger_gc_shrink(size_t _new_max_heap,
+                   double maximum_used_percentage,
+                   size_t max_heap_size);
+  void g1_shrink_without_full_gc(size_t _new_max_heap);
+};
+
 #endif // SHARE_GC_G1_G1VMOPERATIONS_HPP
diff --git a/src/hotspot/share/gc/g1/heapRegionManager.cpp b/src/hotspot/share/gc/g1/heapRegionManager.cpp
index fc445142b..5d83ad874 100644
--- a/src/hotspot/share/gc/g1/heapRegionManager.cpp
+++ b/src/hotspot/share/gc/g1/heapRegionManager.cpp
@@ -34,6 +34,7 @@
 #include "jfr/jfrEvents.hpp"
 #include "logging/logStream.hpp"
 #include "memory/allocation.hpp"
+#include "memory/universe.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/orderAccess.hpp"
@@ -86,6 +87,8 @@ void HeapRegionManager::initialize(G1RegionToSpaceMapper* heap_storage,
 
   _regions.initialize(heap_storage->reserved(), HeapRegion::GrainBytes);
 
+  _dynamic_max_heap_length = static_cast<uint>(MaxHeapSize / HeapRegion::GrainBytes);
+
   _committed_map.initialize(reserved_length());
 }
 
@@ -313,12 +316,15 @@ uint HeapRegionManager::expand_inactive(uint num_regions) {
 
   do {
     HeapRegionRange regions = _committed_map.next_inactive_range(offset);
-    if (regions.length() == 0) {
+    if (regions.length() == 0 || available() == 0) {
       // No more unavailable regions.
       break;
     }
 
     uint to_expand = MIN2(num_regions - expanded, regions.length());
+    if (Universe::is_dynamic_max_heap_enable()) {
+      to_expand = MIN2(to_expand, available());
+    }
     reactivate_regions(regions.start(), to_expand);
     expanded += to_expand;
     offset = regions.end();
@@ -335,12 +341,15 @@ uint HeapRegionManager::expand_any(uint num_regions, WorkerThreads* pretouch_wor
 
   do {
     HeapRegionRange regions = _committed_map.next_committable_range(offset);
-    if (regions.length() == 0) {
+    if (regions.length() == 0 || available() == 0) {
       // No more unavailable regions.
       break;
     }
 
     uint to_expand = MIN2(num_regions - expanded, regions.length());
+    if (Universe::is_dynamic_max_heap_enable()) {
+      to_expand = MIN2(to_expand, available());
+    }
     expand(regions.start(), to_expand, pretouch_workers);
     expanded += to_expand;
     offset = regions.end();
@@ -352,6 +361,13 @@ uint HeapRegionManager::expand_any(uint num_regions, WorkerThreads* pretouch_wor
 uint HeapRegionManager::expand_by(uint num_regions, WorkerThreads* pretouch_workers) {
   assert(num_regions > 0, "Must expand at least 1 region");
 
+  if (Universe::is_dynamic_max_heap_enable()) {
+    uint available_regions = available();
+    guarantee(dynamic_max_heap_length() >= length(), "The current length must not exceed dynamic max heap length");
+    guarantee(available_regions <= max_length() && available_regions <= dynamic_max_heap_length(), "must be");
+    num_regions = MIN2(num_regions, available_regions);
+  }
+
   // First "undo" any requests to uncommit memory concurrently by
   // reverting such regions to being available.
   uint expanded = expand_inactive(num_regions);
@@ -367,6 +383,14 @@ uint HeapRegionManager::expand_by(uint num_regions, WorkerThreads* pretouch_work
 
 void HeapRegionManager::expand_exact(uint start, uint num_regions, WorkerThreads* pretouch_workers) {
   assert(num_regions != 0, "Need to request at least one region");
+
+  if (Universe::is_dynamic_max_heap_enable()) {
+    uint available_regions = available();
+    guarantee(dynamic_max_heap_length() >= length(), "The current length must not exceed dynamic max heap length");
+    guarantee(available_regions <= max_length() && available_regions <= dynamic_max_heap_length(), "must be");
+    num_regions = MIN2(num_regions, available_regions);
+  }
+
   uint end = start + num_regions;
 
   for (uint i = start; i < end; i++) {
@@ -535,7 +559,7 @@ uint HeapRegionManager::find_highest_free(bool* expanded) {
   // committed, expand at that index.
   for (uint curr = reserved_length(); curr-- > 0;) {
     HeapRegion *hr = _regions.get_by_index(curr);
-    if (hr == nullptr || !is_available(curr)) {
+    if ((hr == nullptr || !is_available(curr)) && available() >= 1) {
       // Found uncommitted and free region, expand to make it available for use.
       expand_exact(curr, 1, nullptr);
       assert(at(curr)->is_free(), "Region (%u) must be available and free after expand", curr);
@@ -559,7 +583,7 @@ bool HeapRegionManager::allocate_containing_regions(MemRegion range, size_t* com
   // Ensure that each G1 region in the range is free, returning false if not.
   // Commit those that are not yet available, and keep count.
   for (uint curr_index = start_index; curr_index <= last_index; curr_index++) {
-    if (!is_available(curr_index)) {
+    if (!is_available(curr_index) && available() >= 1) {
       commits++;
       expand_exact(curr_index, 1, pretouch_workers);
     }
diff --git a/src/hotspot/share/gc/g1/heapRegionManager.hpp b/src/hotspot/share/gc/g1/heapRegionManager.hpp
index ad985e1f8..092e20867 100644
--- a/src/hotspot/share/gc/g1/heapRegionManager.hpp
+++ b/src/hotspot/share/gc/g1/heapRegionManager.hpp
@@ -84,6 +84,9 @@ class HeapRegionManager: public CHeapObj<mtGC> {
   // Internal only. The highest heap region +1 we allocated a HeapRegion instance for.
   uint _allocated_heapregions_length;
 
+  // The max number of regions controlled by Dynamic Max Heap
+  uint _dynamic_max_heap_length;
+
   HeapWord* heap_bottom() const { return _regions.bottom_address_mapped(); }
   HeapWord* heap_end() const {return _regions.end_address_mapped(); }
 
@@ -230,7 +233,12 @@ public:
   }
 
   // Return the number of regions available (uncommitted) regions.
-  uint available() const { return max_length() - length(); }
+  uint available() const {
+    if(Universe::is_dynamic_max_heap_enable()) {
+      return dynamic_max_heap_length() - length();
+    }
+    return max_length() - length();
+  }
 
   // Return the number of regions currently active and available for use.
   uint length() const { return _committed_map.num_active(); }
@@ -241,6 +249,14 @@ public:
   // Return maximum number of regions that heap can expand to.
   uint max_length() const { return reserved_length(); }
 
+  // Return the current maximum number of regions in the heap (dynamic max heap).
+  uint dynamic_max_heap_length() const { return _dynamic_max_heap_length; }
+
+  void set_dynamic_max_heap_length(uint len) {
+    guarantee(len <= max_length(), "must be");
+    _dynamic_max_heap_length = len;
+  }
+
   MemoryUsage get_auxiliary_data_memory_usage() const;
 
   MemRegion reserved() const { return MemRegion(heap_bottom(), heap_end()); }
diff --git a/src/hotspot/share/gc/parallel/parallelArguments.cpp b/src/hotspot/share/gc/parallel/parallelArguments.cpp
index 468dc7bdf..8b1dc4f5f 100644
--- a/src/hotspot/share/gc/parallel/parallelArguments.cpp
+++ b/src/hotspot/share/gc/parallel/parallelArguments.cpp
@@ -36,6 +36,7 @@
 #include "runtime/java.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/powerOfTwo.hpp"
+#include "memory/universe.hpp"
 
 size_t ParallelArguments::conservative_max_heap_alignment() {
   return compute_heap_alignment();
@@ -137,6 +138,11 @@ void ParallelArguments::initialize_heap_flags_and_sizes() {
 }
 
 size_t ParallelArguments::heap_reserved_size_bytes() {
+#ifdef AARCH64
+  if (Universe::is_dynamic_max_heap_enable()) {
+    return DynamicMaxHeapSizeLimit;
+  }
+#endif //AARCH64
   return MaxHeapSize;
 }
 
diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
index d34474225..d97257539 100644
--- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
+++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
@@ -42,6 +42,7 @@
 #include "gc/shared/locationPrinter.inline.hpp"
 #include "gc/shared/scavengableNMethods.hpp"
 #include "gc/shared/suspendibleThreadSet.hpp"
+#include "gc/shared/dynamicMaxHeap.hpp"
 #include "logging/log.hpp"
 #include "memory/iterator.hpp"
 #include "memory/metaspaceCounters.hpp"
@@ -62,6 +63,12 @@ PSAdaptiveSizePolicy* ParallelScavengeHeap::_size_policy = nullptr;
 PSGCAdaptivePolicyCounters* ParallelScavengeHeap::_gc_policy_counters = nullptr;
 
 jint ParallelScavengeHeap::initialize() {
+#ifdef AARCH64
+  if (Universe::is_dynamic_max_heap_enable() && !FLAG_IS_CMDLINE(DynamicMaxHeapSizeLimit) && !FLAG_IS_CMDLINE(ElasticMaxHeapSize)) {
+    guarantee(ElasticMaxHeap, "must be");
+    FLAG_SET_ERGO(DynamicMaxHeapSizeLimit, MaxHeapSize);
+  }
+#endif //AARCH64
   const size_t reserved_heap_size = ParallelArguments::heap_reserved_size_bytes();
 
   ReservedHeapSpace heap_rs = Universe::reserve_heap(reserved_heap_size, HeapAlignment);
@@ -70,9 +77,13 @@ jint ParallelScavengeHeap::initialize() {
 
   initialize_reserved_region(heap_rs);
   // Layout the reserved space for the generations.
-  ReservedSpace old_rs   = heap_rs.first_part(MaxOldSize);
-  ReservedSpace young_rs = heap_rs.last_part(MaxOldSize);
-  assert(young_rs.size() == MaxNewSize, "Didn't reserve all of the heap");
+  size_t max_old_size = MaxOldSize;
+  if (Universe::is_dynamic_max_heap_enable()) {
+    max_old_size = GenArguments::max_old_size(reserved_heap_size);
+  }
+  ReservedSpace old_rs   = heap_rs.first_part(max_old_size);
+  ReservedSpace young_rs = heap_rs.last_part(max_old_size);
+  assert(young_rs.size() == MaxNewSize || Universe::is_dynamic_max_heap_enable(), "Didn't reserve all of the heap");
 
   PSCardTable* card_table = new PSCardTable(heap_rs.region());
   card_table->initialize(old_rs.base(), young_rs.base());
@@ -97,8 +108,8 @@ jint ParallelScavengeHeap::initialize() {
       MaxOldSize,
       "old", 1);
 
-  assert(young_gen()->max_gen_size() == young_rs.size(),"Consistency check");
-  assert(old_gen()->max_gen_size() == old_rs.size(), "Consistency check");
+  assert(young_gen()->max_gen_size() == young_rs.size() || Universe::is_dynamic_max_heap_enable(),"Consistency check");
+  assert(old_gen()->max_gen_size() == old_rs.size() || Universe::is_dynamic_max_heap_enable(), "Consistency check");
 
   double max_gc_pause_sec = ((double) MaxGCPauseMillis)/1000.0;
   double max_gc_minor_pause_sec = ((double) MaxGCMinorPauseMillis)/1000.0;
@@ -211,6 +222,12 @@ bool ParallelScavengeHeap::is_maximal_no_gc() const {
 
 size_t ParallelScavengeHeap::max_capacity() const {
   size_t estimated = reserved_region().byte_size();
+  // Dynamic Max Heap
+  if (Universe::is_dynamic_max_heap_enable()) {
+    // young_gen()->max_size() is also controlled by DynamicMaxHeap
+    guarantee(current_max_heap_size() <= estimated, "must be");
+    estimated = current_max_heap_size();
+  }
   if (UseAdaptiveSizePolicy) {
     estimated -= _size_policy->max_survivor_size(young_gen()->max_gen_size());
   } else {
@@ -888,3 +905,10 @@ void ParallelScavengeHeap::pin_object(JavaThread* thread, oop obj) {
 void ParallelScavengeHeap::unpin_object(JavaThread* thread, oop obj) {
   GCLocker::unlock_critical(thread);
 }
+
+bool ParallelScavengeHeap::change_max_heap(size_t new_size) {
+  assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock");
+  PS_ChangeMaxHeapOp op(new_size);
+  VMThread::execute(&op);
+  return op.resize_success();
+}
diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp
index e71dc9515..5a1191e04 100644
--- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp
+++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp
@@ -41,6 +41,7 @@
 #include "logging/log.hpp"
 #include "utilities/growableArray.hpp"
 #include "utilities/ostream.hpp"
+#include "memory/universe.hpp"
 
 class GCHeapSummary;
 class HeapBlockClaimer;
@@ -276,6 +277,9 @@ class ParallelScavengeHeap : public CollectedHeap {
 
   void pin_object(JavaThread* thread, oop obj) override;
   void unpin_object(JavaThread* thread, oop obj) override;
+
+  // Dynamic Max Heap
+  bool change_max_heap(size_t new_size) override;
 };
 
 // Class that can be used to print information about the
diff --git a/src/hotspot/share/gc/parallel/psGenerationCounters.cpp b/src/hotspot/share/gc/parallel/psGenerationCounters.cpp
index 1b0e8d320..5c070d17e 100644
--- a/src/hotspot/share/gc/parallel/psGenerationCounters.cpp
+++ b/src/hotspot/share/gc/parallel/psGenerationCounters.cpp
@@ -27,6 +27,7 @@
 #include "gc/parallel/psGenerationCounters.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
+#include "memory/universe.hpp"
 
 PSGenerationCounters::PSGenerationCounters(const char* name,
                                        int ordinal, int spaces,
@@ -57,8 +58,15 @@ PSGenerationCounters::PSGenerationCounters(const char* name,
       min_capacity, CHECK);
 
     cname = PerfDataManager::counter_name(_name_space, "maxCapacity");
-    PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes,
+    // Dynamic Max Heap
+    if (Universe::is_dynamic_max_heap_enable()) {
+      _max_size = PerfDataManager::create_variable(SUN_GC, cname, 
+        PerfData::U_Bytes, max_capacity, CHECK);
+    } else {
+      _max_size = NULL;
+      PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes,
       max_capacity, CHECK);
+    }
 
     cname = PerfDataManager::counter_name(_name_space, "capacity");
     _current_size = PerfDataManager::create_variable(SUN_GC, cname,
diff --git a/src/hotspot/share/gc/parallel/psMemoryPool.hpp b/src/hotspot/share/gc/parallel/psMemoryPool.hpp
index 58f39cdc7..3dfbb84d5 100644
--- a/src/hotspot/share/gc/parallel/psMemoryPool.hpp
+++ b/src/hotspot/share/gc/parallel/psMemoryPool.hpp
@@ -40,7 +40,7 @@ public:
 
   MemoryUsage get_memory_usage();
   size_t used_in_bytes() { return _old_gen->used_in_bytes(); }
-  size_t max_size() const { return _old_gen->reserved().byte_size(); }
+  size_t max_size() const { return _old_gen->max_gen_size(); }
 };
 
 class EdenMutableSpacePool : public CollectedMemoryPool {
diff --git a/src/hotspot/share/gc/parallel/psOldGen.cpp b/src/hotspot/share/gc/parallel/psOldGen.cpp
index 8124f018b..7c0f816ec 100644
--- a/src/hotspot/share/gc/parallel/psOldGen.cpp
+++ b/src/hotspot/share/gc/parallel/psOldGen.cpp
@@ -36,11 +36,13 @@
 #include "oops/oop.inline.hpp"
 #include "runtime/java.hpp"
 #include "utilities/align.hpp"
+#include "memory/universe.hpp"
 
 PSOldGen::PSOldGen(ReservedSpace rs, size_t initial_size, size_t min_size,
                    size_t max_size, const char* perf_data_name, int level):
   _min_gen_size(min_size),
-  _max_gen_size(max_size)
+  _max_gen_size(Universe::is_dynamic_max_heap_enable() ? rs.size() : max_size),
+  _cur_max_gen_size(Universe::is_dynamic_max_heap_enable() ? max_size : -1)
 {
   initialize(rs, initial_size, GenAlignment, perf_data_name, level);
 }
@@ -58,6 +60,9 @@ void PSOldGen::initialize_virtual_space(ReservedSpace rs,
                                         size_t alignment) {
 
   _virtual_space = new PSVirtualSpace(rs, alignment);
+  if (Universe::is_dynamic_max_heap_enable()) {
+    _virtual_space->set_dynamic_max_heap_size(_cur_max_gen_size);
+  }
   if (!_virtual_space->expand_by(initial_size)) {
     vm_exit_during_initialization("Could not reserve enough space for "
                                   "object heap");
@@ -66,7 +71,7 @@ void PSOldGen::initialize_virtual_space(ReservedSpace rs,
 
 void PSOldGen::initialize_work(const char* perf_data_name, int level) {
   MemRegion const reserved_mr = reserved();
-  assert(reserved_mr.byte_size() == max_gen_size(), "invariant");
+  assert(reserved_mr.byte_size() == max_gen_size() || Universe::is_dynamic_max_heap_enable(), "invariant");
 
   // Object start stuff: for all reserved memory
   start_array()->initialize(reserved_mr);
diff --git a/src/hotspot/share/gc/parallel/psOldGen.hpp b/src/hotspot/share/gc/parallel/psOldGen.hpp
index c26f251f9..089d6d13f 100644
--- a/src/hotspot/share/gc/parallel/psOldGen.hpp
+++ b/src/hotspot/share/gc/parallel/psOldGen.hpp
@@ -32,6 +32,7 @@
 #include "gc/parallel/spaceCounters.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/safepoint.hpp"
+#include "memory/universe.hpp"
 
 class PSOldGen : public CHeapObj<mtGC> {
   friend class VMStructs;
@@ -48,6 +49,9 @@ class PSOldGen : public CHeapObj<mtGC> {
   const size_t _min_gen_size;
   const size_t _max_gen_size;
 
+  // For Dynamic Max Heap
+  size_t _cur_max_gen_size;
+
   // Block size for parallel iteration
   static const size_t IterateBlockSize = 1024 * 1024;
 
@@ -108,9 +112,27 @@ class PSOldGen : public CHeapObj<mtGC> {
                      (HeapWord*)(_virtual_space->high()));
   }
 
-  size_t max_gen_size() const { return _max_gen_size; }
+  size_t max_gen_size() const {
+    if (Universe::is_dynamic_max_heap_enable()) {
+      guarantee(_cur_max_gen_size <= _max_gen_size && _cur_max_gen_size >= _min_gen_size, "must be");
+      return _cur_max_gen_size;
+    }
+    return _max_gen_size; 
+  }
   size_t min_gen_size() const { return _min_gen_size; }
 
+  // Dynamic Max Heap
+  void set_cur_max_gen_size(size_t new_size) {
+    guarantee(Universe::is_dynamic_max_heap_enable(), "must be");
+    guarantee(new_size <= _max_gen_size && new_size >= _min_gen_size, "must be");
+    guarantee(_max_gen_size == _virtual_space->reserved_size(), "must be");
+    _cur_max_gen_size = new_size;
+    _virtual_space->set_dynamic_max_heap_size(new_size);
+    if (UsePerfData) {
+      _gen_counters->update_max_size(new_size);
+    }
+  }
+
   bool is_in(const void* p) const           {
     return _virtual_space->is_in_committed((void *)p);
   }
diff --git a/src/hotspot/share/gc/parallel/psVMOperations.cpp b/src/hotspot/share/gc/parallel/psVMOperations.cpp
index 47eeffb34..dbd0c2ead 100644
--- a/src/hotspot/share/gc/parallel/psVMOperations.cpp
+++ b/src/hotspot/share/gc/parallel/psVMOperations.cpp
@@ -27,6 +27,7 @@
 #include "gc/parallel/psScavenge.hpp"
 #include "gc/parallel/psVMOperations.hpp"
 #include "gc/shared/gcLocker.hpp"
+#include "gc/shared/genArguments.hpp"
 #include "utilities/dtrace.hpp"
 
 // The following methods are used by the parallel scavenge collector
@@ -76,3 +77,254 @@ void VM_ParallelGCSystemGC::doit() {
     _full_gc_succeeded = PSParallelCompact::invoke(false);
   }
 }
+
+PS_ChangeMaxHeapOp::PS_ChangeMaxHeapOp(size_t new_max_heap) :
+  VM_ChangeMaxHeapOp(new_max_heap)
+{}
+
+bool DynamicMaxHeap_PsOldGenCanShrink(size_t _new_max_heap, size_t old_used_bytes, double min_heap_free_ration, size_t alignment) {
+    double ratio = min_heap_free_ration / 100.0;
+    double ratio_inverse = 1.0 - ratio;
+    double tmp = old_used_bytes * ratio;
+    size_t min_free = static_cast<size_t>(tmp / ratio_inverse);
+    // align_up(min_free, alignment) alignment "must be a power of 2
+    min_free = (min_free + alignment - 1) & ~(alignment - 1);
+    bool can_shrink = (_new_max_heap >= (old_used_bytes + min_free));
+    return can_shrink;
+}
+
+/*
+ * 1. calculate new young/old gen limit size.
+ * 2. trigger Full GC if necessary
+ * 3. check and reset new limitation
+ */
+void PS_ChangeMaxHeapOp::doit() {
+  ParallelScavengeHeap* heap = static_cast<ParallelScavengeHeap*>(Universe::heap());
+  assert(heap->kind() == CollectedHeap::Parallel, "must be a ParallelScavengeHeap");
+
+  // step 1
+  PSOldGen* old_gen      = heap->old_gen();
+  PSYoungGen* young_gen  = heap->young_gen();
+  size_t cur_heap_limit  = heap->current_max_heap_size();
+  size_t cur_old_limit   = old_gen->max_gen_size();
+  size_t cur_young_limit = young_gen->max_gen_size();
+  bool is_shrink         = _new_max_heap < cur_heap_limit;
+
+  const size_t young_reserved_size = young_gen->reserved().byte_size();
+  const size_t young_min_size = young_gen->min_gen_size();
+  const size_t old_reserved_size = old_gen->reserved().byte_size();
+  const size_t old_min_size = old_gen->min_gen_size();
+
+  guarantee(cur_old_limit + cur_young_limit == cur_heap_limit, "must be");
+
+  // fix with young gen size limitation
+  size_t new_young_limit = GenArguments::scale_by_NewRatio_aligned(_new_max_heap, GenAlignment);
+  new_young_limit = MIN2(new_young_limit, young_reserved_size);
+  new_young_limit = MAX2(new_young_limit, young_min_size);
+  // align shrink/expand direction
+  if ((is_shrink && (new_young_limit > cur_young_limit)) ||
+      (!is_shrink && (new_young_limit < cur_young_limit))) {
+    new_young_limit = cur_young_limit;
+  }
+  size_t new_old_limit = _new_max_heap - new_young_limit;
+
+  if (new_old_limit > old_reserved_size) {
+    new_old_limit = old_reserved_size;
+    new_young_limit = _new_max_heap - new_old_limit;
+  }
+
+  // keep the new_old_limit aligned with shrink/expand direction
+  if ((is_shrink && (new_old_limit > cur_old_limit)) ||
+      (!is_shrink && (new_old_limit < cur_old_limit))) {
+    new_old_limit = cur_old_limit;
+    new_young_limit = _new_max_heap - new_old_limit;
+  }
+
+  // After the final calcuation, check the leagle limit
+  if ((new_old_limit < old_min_size) ||
+      (new_old_limit > old_reserved_size) ||
+      (new_young_limit < young_min_size) ||
+      (new_young_limit > young_reserved_size)) {
+    log_debug(dynamic, heap)("PS_ElasticMaxHeapOp abort: can not calculate new legal limit:"
+             " new_old_limit: " SIZE_FORMAT "K, " "old gen min size: " SIZE_FORMAT "K, old gen reserved size: " SIZE_FORMAT "K"
+             " new_young_limit: " SIZE_FORMAT "K, " "young gen min size: " SIZE_FORMAT "K, young gen reserved size: " SIZE_FORMAT "K" ,
+            (new_old_limit / K), (old_min_size / K), (old_reserved_size / K),
+            (new_young_limit / K), (young_min_size / K), (young_reserved_size / K));
+    return;
+  }
+
+  log_debug(dynamic, heap)("PS_ElasticMaxHeapOp plan: "
+    "desired young gen size (" SIZE_FORMAT "K" "->" SIZE_FORMAT "K), "
+    "desired old gen size (" SIZE_FORMAT "K" "->" SIZE_FORMAT "K)",
+    (cur_young_limit / K),
+    (new_young_limit / K),
+    (cur_old_limit / K),
+    (new_old_limit / K));
+  if (is_shrink) {
+    guarantee(new_old_limit <= cur_old_limit && new_young_limit <= cur_young_limit, "must be");
+  } else {
+    guarantee(new_old_limit >= cur_old_limit && new_young_limit >= cur_young_limit, "must be");
+  }
+
+  // step2
+  // Check resize legality
+  if (is_shrink) {
+    // check whether old/young can be resized, trigger full gc as needed
+    double min_heap_free_ration = MinHeapFreeRatio;
+#ifdef AARCH64
+    if (min_heap_free_ration == 0) {
+      min_heap_free_ration = DynamicMaxHeapShrinkMinFreeRatio;
+    }
+#endif //AARCH64
+    bool can_shrink = DynamicMaxHeap_PsOldGenCanShrink(new_old_limit,
+                                                       heap->old_gen()->used_in_bytes(),
+                                                       min_heap_free_ration,
+                                                       heap->old_gen()->virtual_space()->alignment());
+    if (can_shrink) {
+      can_shrink = (new_young_limit >= heap->young_gen()->virtual_space()->committed_size());
+    }
+    if (!can_shrink) {
+      GCCauseSetter gccs(heap, _gc_cause);
+      heap->do_full_collection(true);
+      log_debug(dynamic, heap)("PS_ElasticMaxHeapOp heap after Full GC");
+      LogTarget(Debug, dynamic, heap) lt;
+      if (lt.is_enabled()) {
+        LogStream ls(lt);
+        heap->print_on(&ls);
+      }
+      if (young_gen->used_in_bytes() != 0) {
+        log_debug(dynamic, heap)("PS_ElasticMaxHeapOp abort: young is not empty after full gc");
+        return;
+      }
+    }
+
+    can_shrink = DynamicMaxHeap_PsOldGenCanShrink(new_old_limit,
+                                                  heap->old_gen()->used_in_bytes(),
+                                                  min_heap_free_ration,
+                                                  heap->old_gen()->virtual_space()->alignment());
+    if (!can_shrink) {
+      log_debug(dynamic, heap)("PS_ElasticMaxHeapOp abort: not enough old free for shrink");
+      return;
+    }
+
+    // step3
+    // shrink generation committed size if needed
+    // 1. old gen
+    //    1 old gen can shrink capacity without full gc
+    //    2 old gen have passed shrink valid check since the code is executed here
+    //    3 old gen can shrink capacity if needed
+    // 2. young gen
+    //    1 young gen must shrink capacity after full gc
+    //    2 there may be three situations after shrink valid check in step2
+    //      1) both old gen and young gen have passed the check,
+    //         indicating new_young_limit is big enough,
+    //         there is no need to shrink capacity
+    //      2) old gen failed the check and triggered full gc
+    //      3) young gen failed the check and triggered full gc
+
+    if (old_gen->capacity_in_bytes() > new_old_limit) {
+      size_t desired_free = new_old_limit - old_gen->used_in_bytes();
+      char* old_high = old_gen->virtual_space()->committed_high_addr();
+      old_gen->resize(desired_free);
+      char* new_old_high = old_gen->virtual_space()->committed_high_addr();
+      if (old_gen->capacity_in_bytes() > new_old_limit) {
+        log_debug(dynamic, heap)("PS_ElasticMaxHeapOp abort: resize old fail " SIZE_FORMAT "K",
+                old_gen->capacity_in_bytes() / K);
+        return;
+      }
+      log_debug(dynamic, heap)("PS_ElasticMaxHeapOp continue: shrink old success " SIZE_FORMAT "K",
+              old_gen->capacity_in_bytes() / K);
+      if (old_high > new_old_high) {
+        // shrink is caused by dynamic max heap, free physical memory
+        size_t shrink_bytes = old_high - new_old_high;
+        guarantee((shrink_bytes > 0) && (shrink_bytes % os::vm_page_size() == 0), "should be");
+        bool result = os::free_heap_physical_memory(new_old_high, shrink_bytes);
+        guarantee(result, "free heap physical memory should be successful");
+      }
+    }
+
+    if (young_gen->virtual_space()->committed_size() > new_young_limit) {
+      // entering this branch means full gc must have been triggered
+      guarantee(young_gen->eden_space()->is_empty() &&
+                young_gen->to_space()->is_empty() &&
+                young_gen->from_space()->is_empty(),
+                "must be empty");
+
+      char* young_high = young_gen->virtual_space()->committed_high_addr();
+      if (young_gen->shrink_after_full_gc(new_young_limit) == false) {
+        log_debug(dynamic, heap)("PS_ElasticMaxHeapOp abort: shrink young fail");
+        return;
+      }
+      char* new_young_high = young_gen->virtual_space()->committed_high_addr();
+      log_debug(dynamic, heap)("PS_ElasticMaxHeapOp continue: shrink young success " SIZE_FORMAT "K",
+              young_gen->virtual_space()->committed_size() / K);
+      if (young_high > new_young_high) {
+        // shrink is caused by dynamic max heap, free physical memory
+        size_t shrink_bytes = young_high - new_young_high;
+        guarantee((shrink_bytes > 0) && (shrink_bytes % os::vm_page_size() == 0), "should be");
+        bool result = os::free_heap_physical_memory(new_young_high, shrink_bytes);
+        guarantee(result, "free heap physical memory should be successful");
+      }
+    }
+  }
+  // update young/old gen limit, avoid further expand
+  old_gen->set_cur_max_gen_size(new_old_limit);
+  young_gen->set_cur_max_gen_size(new_young_limit);
+  heap->set_current_max_heap_size(_new_max_heap);
+  _resize_success = true;
+  log_debug(dynamic, heap)("PS_ElasticMaxHeapOp success");
+}
+
+// Resize for DynamicHeapSize, shrink to new_size
+bool PSYoungGen::shrink_after_full_gc(size_t new_size) {
+  const size_t alignment = virtual_space()->alignment();
+  ParallelScavengeHeap* heap = static_cast<ParallelScavengeHeap*>(Universe::heap());
+  size_t orig_size = virtual_space()->committed_size();
+  guarantee(eden_space()->is_empty() && to_space()->is_empty() && from_space()->is_empty(), "must be empty");
+  guarantee(new_size % alignment == 0, "must be");
+  guarantee(new_size < orig_size, "must be");
+
+  // shrink virtual space
+  size_t shrink_bytes = virtual_space()->committed_size() - new_size;
+  bool success = virtual_space()->shrink_by(shrink_bytes);
+  log_debug(dynamic, heap)("PSYoungGen::shrink_after_full_gc: shrink virtual space %s "
+          "orig committed " SIZE_FORMAT "K "
+          "current committed " SIZE_FORMAT "K "
+          "shrink by " SIZE_FORMAT "K",
+          success ? "success" : "fail",
+          orig_size / K,
+          virtual_space()->committed_size() / K,
+          shrink_bytes / K);
+
+  if (!success) {
+    return false;
+  }
+
+  // caculate new eden/survivor size
+  // shrink with same ratio, let size policy adjust later
+  size_t current_survivor_ratio = eden_space()->capacity_in_bytes() / from_space()->capacity_in_bytes();
+  current_survivor_ratio = MAX2(current_survivor_ratio, static_cast<size_t>(1));
+  size_t new_survivor_size = new_size / (current_survivor_ratio + 2);
+  new_survivor_size = align_down(new_survivor_size, SpaceAlignment);
+  new_survivor_size = MAX2(new_survivor_size, SpaceAlignment);
+  size_t new_eden_size = new_size - 2 * new_survivor_size;
+
+  guarantee(new_eden_size % SpaceAlignment == 0, "must be");
+  log_debug(dynamic, heap)("PSYoungGen::shrink_after_full_gc: "
+          "new eden size " SIZE_FORMAT "K "
+          "new survivor size " SIZE_FORMAT "K "
+          "new young gen size " SIZE_FORMAT "K",
+          new_eden_size / K,
+          new_survivor_size / K,
+          new_size / K);
+
+  // setup new eden/survivor space
+  set_space_boundaries(new_eden_size, new_survivor_size);
+  post_resize();
+  LogTarget(Debug, dynamic, heap) lt;
+  if (lt.is_enabled()) {
+    LogStream ls(lt);
+    print_on(&ls);
+  }
+  return true;
+}
\ No newline at end of file
diff --git a/src/hotspot/share/gc/parallel/psVMOperations.hpp b/src/hotspot/share/gc/parallel/psVMOperations.hpp
index cc49eb631..ec1eded11 100644
--- a/src/hotspot/share/gc/parallel/psVMOperations.hpp
+++ b/src/hotspot/share/gc/parallel/psVMOperations.hpp
@@ -28,6 +28,7 @@
 #include "gc/parallel/parallelScavengeHeap.hpp"
 #include "gc/shared/gcCause.hpp"
 #include "gc/shared/gcVMOperations.hpp"
+#include "gc/shared/dynamicMaxHeap.hpp"
 
 class VM_ParallelGCFailedAllocation : public VM_CollectForAllocation {
  public:
@@ -48,4 +49,11 @@ class VM_ParallelGCSystemGC: public VM_GC_Operation {
   bool full_gc_succeeded() const { return _full_gc_succeeded; }
 };
 
+// For ParallelScavengeHeap
+class PS_ChangeMaxHeapOp : public VM_ChangeMaxHeapOp {
+public:
+  PS_ChangeMaxHeapOp(size_t new_max_heap);
+  void doit() override;
+};
+
 #endif // SHARE_GC_PARALLEL_PSVMOPERATIONS_HPP
diff --git a/src/hotspot/share/gc/parallel/psVirtualspace.cpp b/src/hotspot/share/gc/parallel/psVirtualspace.cpp
index f0ccd9a75..0181ab954 100644
--- a/src/hotspot/share/gc/parallel/psVirtualspace.cpp
+++ b/src/hotspot/share/gc/parallel/psVirtualspace.cpp
@@ -31,7 +31,8 @@
 // PSVirtualSpace
 
 PSVirtualSpace::PSVirtualSpace(ReservedSpace rs, size_t alignment) :
-  _alignment(alignment)
+  _alignment(alignment),
+  _dynamic_max_heap_size(0)
 {
   set_reserved(rs);
   set_committed(reserved_low_addr(), reserved_low_addr());
@@ -45,11 +46,13 @@ PSVirtualSpace::PSVirtualSpace():
   _reserved_high_addr(nullptr),
   _committed_low_addr(nullptr),
   _committed_high_addr(nullptr),
-  _special(false) {
+  _special(false),
+  _dynamic_max_heap_size(0) {
 }
 
 // Deprecated.
 void PSVirtualSpace::initialize(ReservedSpace rs) {
+  _dynamic_max_heap_size = 0;
   set_reserved(rs);
   set_committed(reserved_low_addr(), reserved_low_addr());
   DEBUG_ONLY(verify());
diff --git a/src/hotspot/share/gc/parallel/psVirtualspace.hpp b/src/hotspot/share/gc/parallel/psVirtualspace.hpp
index e2cb08160..9becc6e01 100644
--- a/src/hotspot/share/gc/parallel/psVirtualspace.hpp
+++ b/src/hotspot/share/gc/parallel/psVirtualspace.hpp
@@ -27,6 +27,7 @@
 
 #include "memory/allocation.hpp"
 #include "memory/virtualspace.hpp"
+#include "memory/universe.hpp"
 
 // VirtualSpace for the parallel scavenge collector.
 //
@@ -52,6 +53,9 @@ class PSVirtualSpace : public CHeapObj<mtGC> {
   // os::commit_memory() or os::uncommit_memory().
   bool _special;
 
+  // Dynamic Max Heap
+  size_t _dynamic_max_heap_size;
+
  public:
   PSVirtualSpace(ReservedSpace rs, size_t alignment);
 
@@ -88,6 +92,17 @@ class PSVirtualSpace : public CHeapObj<mtGC> {
   virtual bool   expand_by(size_t bytes);
   virtual bool   shrink_by(size_t bytes);
   void           release();
+  // Dynamic Max Heap
+  void set_dynamic_max_heap_size(size_t new_size) {
+    guarantee(new_size <= reserved_size(), "must be");
+    guarantee(new_size >= committed_size(), "must be");
+    _dynamic_max_heap_size = new_size;
+  }
+  size_t dynamic_max_heap_size() const {
+    guarantee(_dynamic_max_heap_size <= reserved_size(), "must be");
+    guarantee(_dynamic_max_heap_size >= committed_size(), "must be");
+    return _dynamic_max_heap_size;
+  }
 
 #ifndef PRODUCT
   // Debugging
@@ -131,6 +146,9 @@ inline size_t PSVirtualSpace::reserved_size() const {
 }
 
 inline size_t PSVirtualSpace::uncommitted_size() const {
+  if (Universe::is_dynamic_max_heap_enable()) {
+    return dynamic_max_heap_size() - committed_size();
+  }
   return reserved_size() - committed_size();
 }
 
@@ -138,6 +156,10 @@ inline void PSVirtualSpace::set_reserved(char* low_addr, char* high_addr, bool s
   _reserved_low_addr = low_addr;
   _reserved_high_addr = high_addr;
   _special = special;
+  if (Universe::is_dynamic_max_heap_enable()) {
+    guarantee(_dynamic_max_heap_size == 0, "resize virtual NYI");
+    _dynamic_max_heap_size = high_addr - low_addr;
+  }
 }
 
 inline void PSVirtualSpace::set_reserved(ReservedSpace rs) {
diff --git a/src/hotspot/share/gc/parallel/psYoungGen.cpp b/src/hotspot/share/gc/parallel/psYoungGen.cpp
index 12a6da8c2..1029d2f5f 100644
--- a/src/hotspot/share/gc/parallel/psYoungGen.cpp
+++ b/src/hotspot/share/gc/parallel/psYoungGen.cpp
@@ -34,6 +34,7 @@
 #include "oops/oop.inline.hpp"
 #include "runtime/java.hpp"
 #include "utilities/align.hpp"
+#include "memory/universe.hpp"
 
 PSYoungGen::PSYoungGen(ReservedSpace rs, size_t initial_size, size_t min_size, size_t max_size) :
   _reserved(),
@@ -42,7 +43,8 @@ PSYoungGen::PSYoungGen(ReservedSpace rs, size_t initial_size, size_t min_size, s
   _from_space(nullptr),
   _to_space(nullptr),
   _min_gen_size(min_size),
-  _max_gen_size(max_size),
+  _max_gen_size(Universe::is_dynamic_max_heap_enable() ? rs.size() : max_size),
+  _cur_max_gen_size(Universe::is_dynamic_max_heap_enable() ? max_size : -1),
   _gen_counters(nullptr),
   _eden_counters(nullptr),
   _from_counters(nullptr),
@@ -56,6 +58,9 @@ void PSYoungGen::initialize_virtual_space(ReservedSpace rs,
                                           size_t alignment) {
   assert(initial_size != 0, "Should have a finite size");
   _virtual_space = new PSVirtualSpace(rs, alignment);
+  if (Universe::is_dynamic_max_heap_enable()) {
+    _virtual_space->set_dynamic_max_heap_size(_cur_max_gen_size);
+  }
   if (!virtual_space()->expand_by(initial_size)) {
     vm_exit_during_initialization("Could not reserve enough space for object heap");
   }
@@ -70,7 +75,7 @@ void PSYoungGen::initialize_work() {
 
   _reserved = MemRegion((HeapWord*)virtual_space()->low_boundary(),
                         (HeapWord*)virtual_space()->high_boundary());
-  assert(_reserved.byte_size() == max_gen_size(), "invariant");
+  assert(_reserved.byte_size() == max_gen_size() || Universe::is_dynamic_max_heap_enable(), "invariant");
 
   MemRegion cmr((HeapWord*)virtual_space()->low(),
                 (HeapWord*)virtual_space()->high());
diff --git a/src/hotspot/share/gc/parallel/psYoungGen.hpp b/src/hotspot/share/gc/parallel/psYoungGen.hpp
index 58b649428..12f7f711f 100644
--- a/src/hotspot/share/gc/parallel/psYoungGen.hpp
+++ b/src/hotspot/share/gc/parallel/psYoungGen.hpp
@@ -30,6 +30,7 @@
 #include "gc/parallel/psGenerationCounters.hpp"
 #include "gc/parallel/psVirtualspace.hpp"
 #include "gc/parallel/spaceCounters.hpp"
+#include "memory/universe.hpp"
 
 class PSYoungGen : public CHeapObj<mtGC> {
   friend class VMStructs;
@@ -48,6 +49,9 @@ class PSYoungGen : public CHeapObj<mtGC> {
   const size_t _min_gen_size;
   const size_t _max_gen_size;
 
+  // For Dynamic Max Heap
+  size_t _cur_max_gen_size;
+
   // Performance counters
   PSGenerationCounters* _gen_counters;
   SpaceCounters*        _eden_counters;
@@ -111,6 +115,9 @@ class PSYoungGen : public CHeapObj<mtGC> {
   //        not allow us to use these values.
   void resize(size_t eden_size, size_t survivor_size);
 
+  // Resize for DynamicHeapSize, shrink to new_size
+  bool shrink_after_full_gc(size_t new_size);
+
   // Size info
   size_t capacity_in_bytes() const;
   size_t used_in_bytes() const;
@@ -121,7 +128,24 @@ class PSYoungGen : public CHeapObj<mtGC> {
   size_t free_in_words() const;
 
   size_t min_gen_size() const { return _min_gen_size; }
-  size_t max_gen_size() const { return _max_gen_size; }
+  size_t max_gen_size() const {
+    if (Universe::is_dynamic_max_heap_enable()) {
+      guarantee(_cur_max_gen_size <= _max_gen_size && _cur_max_gen_size >= min_gen_size(), "must be");
+      return _cur_max_gen_size;
+    }
+    return _max_gen_size;
+  }
+
+  void set_cur_max_gen_size(size_t new_size) {
+    guarantee(Universe::is_dynamic_max_heap_enable(), "must be");
+    guarantee(new_size <= _max_gen_size && new_size >= min_gen_size(), "must be");
+    guarantee(_max_gen_size == _reserved.byte_size(), "must be");
+    _cur_max_gen_size = new_size;
+    _virtual_space->set_dynamic_max_heap_size(new_size);
+    if (UsePerfData) {
+      _gen_counters->update_max_size(new_size);
+    }
+  }
 
   bool is_maximal_no_gc() const {
     return true;  // Never expands except at a GC
diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp
index 89b39195e..ac3a7b8d6 100644
--- a/src/hotspot/share/gc/shared/collectedHeap.cpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.cpp
@@ -243,6 +243,7 @@ CollectedHeap::CollectedHeap() :
   _used_at_last_gc(0),
   _is_stw_gc_active(false),
   _last_whole_heap_examined_time_ns(os::javaTimeNanos()),
+  _current_max_heap_size(MaxHeapSize),
   _total_collections(0),
   _total_full_collections(0),
   _gc_cause(GCCause::_no_gc),
diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp
index e2613c438..bd675760c 100644
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.hpp
@@ -128,6 +128,8 @@ class CollectedHeap : public CHeapObj<mtGC> {
   // time-warp warnings.
   jlong _last_whole_heap_examined_time_ns;
 
+  size_t _current_max_heap_size;
+
   unsigned int _total_collections;          // ... started
   unsigned int _total_full_collections;     // ... started
   NOT_PRODUCT(volatile size_t _promotion_failure_alot_count;)
@@ -529,6 +531,15 @@ class CollectedHeap : public CHeapObj<mtGC> {
   void reset_promotion_should_fail(volatile size_t* count);
   void reset_promotion_should_fail();
 #endif  // #ifndef PRODUCT
+
+public:
+  // Dynamic Max Heap
+  virtual bool change_max_heap(size_t new_size){ return false; }
+  bool check_new_max_heap_validity(size_t new_size, outputStream* st);
+  size_t current_max_heap_size() const { return _current_max_heap_size; }
+  void set_current_max_heap_size(size_t new_size) {
+    _current_max_heap_size = new_size;
+  }
 };
 
 // Class to set and reset the GC cause for a CollectedHeap.
diff --git a/src/hotspot/share/gc/shared/dynamicMaxHeap.cpp b/src/hotspot/share/gc/shared/dynamicMaxHeap.cpp
new file mode 100644
index 000000000..b88323b00
--- /dev/null
+++ b/src/hotspot/share/gc/shared/dynamicMaxHeap.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+#include "precompiled.hpp"
+#include "dynamicMaxHeap.hpp"
+#include "runtime/globals_extension.hpp"
+#include "os_linux.hpp"
+#include "logging/logConfiguration.hpp"
+
+size_t DynamicMaxHeapConfig::_initial_max_heap_size = 0;
+
+VM_ChangeMaxHeapOp::VM_ChangeMaxHeapOp(size_t new_max_heap) :
+  VM_GC_Operation(0, GCCause::_change_max_heap, 0, true) {
+  _new_max_heap = new_max_heap;
+  _resize_success = false;
+}
+
+bool VM_ChangeMaxHeapOp::skip_operation() const {
+  return false;
+}
+
+/*
+ * validity check
+ * new current max heap must be:
+ * 1. >= min_heap_byte_size
+ * 2. <= max_heap_byte_size
+ * 3. not equal with current_max_heap_size
+ *
+*/
+bool CollectedHeap::check_new_max_heap_validity(size_t new_size, outputStream* st) {
+#ifdef AARCH64
+  if (new_size > DynamicMaxHeapSizeLimit) {
+    st->print_cr("%s " SIZE_FORMAT "K exceeds maximum limit " SIZE_FORMAT "K",
+                Universe::dynamic_max_heap_dcmd_name(),
+                (new_size / K),
+                (DynamicMaxHeapSizeLimit / K));
+    return false;
+  }
+#endif
+  if (new_size < MinHeapSize) {
+    st->print_cr("%s " SIZE_FORMAT "K below minimum limit " SIZE_FORMAT "K",
+                Universe::dynamic_max_heap_dcmd_name(),
+                (new_size / K),
+                (MinHeapSize / K));
+    return false;
+  }
+  // don't print log if it is init shrink triggered by DynamicMaxHeapSizeLimit
+  if (new_size == current_max_heap_size()) {
+    st->print_cr("%s " SIZE_FORMAT "K same with current max heap size " SIZE_FORMAT "K",
+                Universe::dynamic_max_heap_dcmd_name(),
+                (new_size / K),
+                (current_max_heap_size() / K));
+    return false;
+  }
+  return true;
+}
+
+/*
+  common check for Dynamic Max Heap
+  1. DynamicMaxHeapSizeLimit/ElasticMaxHeapSize should be used together with Xmx
+  2. only linux aarch hisi
+  3. can not fix new/old size
+  4. must support UseAdaptiveSizePolicy, otherwise all size fixed
+  5. only G1GC/PSGC implemented now
+  6. should larger than Xmx
+*/
+bool DynamicMaxHeapChecker::common_check() {
+#ifdef AARCH64
+  if (!FLAG_IS_CMDLINE(DynamicMaxHeapSizeLimit) && !FLAG_IS_CMDLINE(ElasticMaxHeapSize) && !ElasticMaxHeap) {
+        return false;
+  }
+  if ((FLAG_IS_CMDLINE(DynamicMaxHeapSizeLimit) || FLAG_IS_CMDLINE(ElasticMaxHeapSize)) && !FLAG_IS_CMDLINE(MaxHeapSize)) {
+        warning_and_disable("should be used together with -Xmx/-XX:MaxHeapSize");
+        return false;
+  }
+#endif
+#if !defined(LINUX) || !defined(AARCH64)
+  warning_and_disable("can only be assigned on Linux aarch64");
+  return false;
+#endif
+#ifdef AARCH64
+  VM_Version::get_cpu_model();
+  if (!VM_Version::is_hisi_enabled()) {
+    warning_and_disable("can only be assigned on HiSi now");
+    return false;
+  }
+#endif
+  if (FLAG_IS_CMDLINE(OldSize) || FLAG_IS_CMDLINE(NewSize) || FLAG_IS_CMDLINE(MaxNewSize)) {
+    warning_and_disable("can not be used with -XX:OldSize/-XX:NewSize/-XX:MaxNewSize");
+    return false;
+  }
+  if (!UseAdaptiveSizePolicy) {
+    warning_and_disable("should be used with -XX:+UseAdaptiveSizePolicy");
+    return false;
+  }
+  if (!UseG1GC && !UseParallelGC) {
+    warning_and_disable("should be used with -XX:+UseG1GC/-XX:+UseParallelGC now");
+    return false;
+  }
+#ifdef AARCH64
+  if ((FLAG_IS_CMDLINE(DynamicMaxHeapSizeLimit) || FLAG_IS_CMDLINE(ElasticMaxHeapSize)) && DynamicMaxHeapSizeLimit <= MaxHeapSize) {
+    warning_and_disable("should be larger than -Xmx/-XX:MaxHeapSize");
+    return false;
+  }
+#endif
+  return true;
+}
+
+bool DynamicMaxHeapChecker::check_dynamic_max_heap_size_limit() {
+#ifdef AARCH64
+  if (TraceElasticMaxHeap) {
+    LogConfiguration::configure_stdout(LogLevel::Debug, false, LOG_TAGS(dynamic, heap));
+  }
+  if (FLAG_IS_CMDLINE(ElasticMaxHeapSize)) {
+    FLAG_SET_ERGO(DynamicMaxHeapSizeLimit, ElasticMaxHeapSize);
+  }
+  if (FLAG_IS_CMDLINE(ElasticMaxHeapShrinkMinFreeRatio)) {
+    FLAG_SET_ERGO(DynamicMaxHeapShrinkMinFreeRatio, ElasticMaxHeapShrinkMinFreeRatio);
+  }
+#endif
+  return common_check();
+}
+
+void DynamicMaxHeapChecker::warning_and_disable(const char *reason) {
+#ifdef AARCH64
+  warning("%s feature are not available for reason -XX:%s %s, automatically disabled",
+          Universe::dynamic_max_heap_option_name(),
+          Universe::dynamic_max_heap_size_limit_option_name(),
+          reason);
+  FLAG_SET_DEFAULT(DynamicMaxHeapSizeLimit, ScaleForWordSize(DynamicMaxHeapChecker::_default_dynamic_max_heap_size_limit * M));
+  Universe::set_dynamic_max_heap_enable(false);
+#endif
+}
\ No newline at end of file
diff --git a/src/hotspot/share/gc/shared/dynamicMaxHeap.hpp b/src/hotspot/share/gc/shared/dynamicMaxHeap.hpp
new file mode 100644
index 000000000..a2576337f
--- /dev/null
+++ b/src/hotspot/share/gc/shared/dynamicMaxHeap.hpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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_SHARED_DYNAMIC_MAX_HEAP_OPERATION_HPP
+#define SHARE_VM_GC_IMPLEMENTATION_SHARED_DYNAMIC_MAX_HEAP_OPERATION_HPP
+
+#include "utilities/defaultStream.hpp"
+#include "gc/shared/gcVMOperations.hpp"
+
+class VM_ChangeMaxHeapOp : public VM_GC_Operation {
+public:
+  VM_ChangeMaxHeapOp(size_t new_max_heap);
+  VMOp_Type type() const override {
+    return VMOp_DynamicMaxHeap;
+  }
+  bool resize_success() const {
+    return _resize_success;
+  }
+protected:
+  size_t _new_max_heap;
+  bool   _resize_success;
+private:
+  bool skip_operation() const override;
+};
+
+class DynamicMaxHeapChecker : private AllStatic {
+public:
+  static bool common_check();
+  static bool check_dynamic_max_heap_size_limit();
+  static void warning_and_disable(const char *reason);
+private:
+  static const int _default_dynamic_max_heap_size_limit = 96;
+};
+
+class DynamicMaxHeapConfig : private AllStatic {
+public:
+  static size_t initial_max_heap_size() { return _initial_max_heap_size; }
+  static void set_initial_max_heap_size(size_t new_size) {
+    _initial_max_heap_size = new_size;
+  }
+private:
+  static size_t _initial_max_heap_size;
+};
+#endif // SHARE_VM_GC_IMPLEMENTATION_SHARED_DYNAMIC_MAX_HEAP_OPERATION_HPP
\ No newline at end of file
diff --git a/src/hotspot/share/gc/shared/gcArguments.cpp b/src/hotspot/share/gc/shared/gcArguments.cpp
index 0040f28f1..91389df01 100644
--- a/src/hotspot/share/gc/shared/gcArguments.cpp
+++ b/src/hotspot/share/gc/shared/gcArguments.cpp
@@ -32,6 +32,7 @@
 #include "runtime/globals_extension.hpp"
 #include "utilities/formatBuffer.hpp"
 #include "utilities/macros.hpp"
+#include "memory/universe.hpp"
 
 size_t HeapAlignment = 0;
 size_t SpaceAlignment = 0;
@@ -155,6 +156,14 @@ void GCArguments::initialize_heap_flags_and_sizes() {
   if (!is_aligned(MaxHeapSize, HeapAlignment)) {
     FLAG_SET_ERGO(MaxHeapSize, align_up(MaxHeapSize, HeapAlignment));
   }
+#ifdef AARCH64
+  if (Universe::is_dynamic_max_heap_enable() && !is_aligned(DynamicMaxHeapSizeLimit, HeapAlignment)) {
+    size_t _dynamic_max_heap_size_limit = DynamicMaxHeapSizeLimit;
+    FLAG_SET_ERGO(DynamicMaxHeapSizeLimit, align_up(DynamicMaxHeapSizeLimit, HeapAlignment));
+    log_debug(dynamic, heap)("align the DynamicMaxHeapSizeLimit " SIZE_FORMAT " up to " SIZE_FORMAT " for heap alignment " SIZE_FORMAT ,
+                              _dynamic_max_heap_size_limit, DynamicMaxHeapSizeLimit, HeapAlignment);
+  }
+#endif //AARCH64
 
   if (!FLAG_IS_DEFAULT(InitialHeapSize) && InitialHeapSize > MaxHeapSize) {
     FLAG_SET_ERGO(MaxHeapSize, InitialHeapSize);
diff --git a/src/hotspot/share/gc/shared/gcCause.cpp b/src/hotspot/share/gc/shared/gcCause.cpp
index 426154fdc..ba4276e5b 100644
--- a/src/hotspot/share/gc/shared/gcCause.cpp
+++ b/src/hotspot/share/gc/shared/gcCause.cpp
@@ -66,6 +66,9 @@ const char* GCCause::to_string(GCCause::Cause cause) {
     case _allocation_failure:
       return "Allocation Failure";
 
+    case _change_max_heap:
+      return "Change Max Heap";
+
     case _codecache_GC_threshold:
       return "CodeCache GC Threshold";
 
diff --git a/src/hotspot/share/gc/shared/gcCause.hpp b/src/hotspot/share/gc/shared/gcCause.hpp
index 152ca787f..2a6c73b81 100644
--- a/src/hotspot/share/gc/shared/gcCause.hpp
+++ b/src/hotspot/share/gc/shared/gcCause.hpp
@@ -58,6 +58,7 @@ class GCCause : public AllStatic {
     _no_gc,
     _no_cause_specified,
     _allocation_failure,
+    _change_max_heap,
 
     /* implementation specific */
 
diff --git a/src/hotspot/share/gc/shared/genArguments.cpp b/src/hotspot/share/gc/shared/genArguments.cpp
index 6d569d6f7..a58d8ad34 100644
--- a/src/hotspot/share/gc/shared/genArguments.cpp
+++ b/src/hotspot/share/gc/shared/genArguments.cpp
@@ -31,6 +31,7 @@
 #include "runtime/java.hpp"
 #include "utilities/align.hpp"
 #include "utilities/globalDefinitions.hpp"
+#include "memory/universe.hpp"
 
 size_t MinNewSize = 0;
 
@@ -377,7 +378,13 @@ void GenArguments::assert_flags() {
 void GenArguments::assert_size_info() {
   GCArguments::assert_size_info();
   // GenArguments::initialize_size_info may update the MaxNewSize
-  assert(MaxNewSize < MaxHeapSize, "Ergonomics decided on incompatible maximum young and heap sizes");
+  if (Universe::is_dynamic_max_heap_enable()) {
+#ifdef AARCH64
+    assert(MaxNewSize < MAX2(MaxHeapSize, DynamicMaxHeapSizeLimit), "Ergonomics decided on incompatible maximum young and heap sizes");
+#endif //AARCH64
+  } else {
+    assert(MaxNewSize < MaxHeapSize, "Ergonomics decided on incompatible maximum young and heap sizes");
+  }
   assert(MinNewSize <= NewSize, "Ergonomics decided on incompatible minimum and initial young gen sizes");
   assert(NewSize <= MaxNewSize, "Ergonomics decided on incompatible initial and maximum young gen sizes");
   assert(MinNewSize % GenAlignment == 0, "_min_young_size alignment");
diff --git a/src/hotspot/share/gc/shared/genArguments.hpp b/src/hotspot/share/gc/shared/genArguments.hpp
index a4c62ff5a..2d24cbfd9 100644
--- a/src/hotspot/share/gc/shared/genArguments.hpp
+++ b/src/hotspot/share/gc/shared/genArguments.hpp
@@ -26,7 +26,9 @@
 #define SHARE_GC_SHARED_GENARGUMENTS_HPP
 
 #include "gc/shared/gcArguments.hpp"
+#include "gc/shared/gc_globals.hpp"
 #include "utilities/debug.hpp"
+#include "memory/universe.hpp"
 
 extern size_t MinNewSize;
 
@@ -36,6 +38,7 @@ extern size_t MaxOldSize;
 extern size_t GenAlignment;
 
 class GenArguments : public GCArguments {
+  friend class PS_ChangeMaxHeapOp;
   friend class TestGenCollectorPolicy; // Testing
 private:
   virtual void initialize_alignments();
@@ -51,6 +54,18 @@ private:
 
 protected:
   virtual void initialize_heap_flags_and_sizes();
+public:
+  //dynamic max heap size
+  static size_t max_old_size(size_t size) {
+    if (Universe::is_dynamic_max_heap_enable()) {
+      size_t young_limit = scale_by_NewRatio_aligned(size, GenAlignment);
+      young_limit = MAX3(young_limit, MinNewSize, NewSize);
+      size_t old_limit = size - young_limit;
+      guarantee(old_limit >= MinOldSize && old_limit >= OldSize, "must be");
+      return old_limit;
+    }
+    return MaxOldSize;
+  }
 };
 
 #endif // SHARE_GC_SHARED_GENARGUMENTS_HPP
diff --git a/src/hotspot/share/gc/shared/generationCounters.cpp b/src/hotspot/share/gc/shared/generationCounters.cpp
index 05460ea88..a76fc5339 100644
--- a/src/hotspot/share/gc/shared/generationCounters.cpp
+++ b/src/hotspot/share/gc/shared/generationCounters.cpp
@@ -26,6 +26,7 @@
 #include "gc/shared/generationCounters.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
+#include "memory/universe.hpp"
 #include "runtime/perfData.hpp"
 
 void GenerationCounters::initialize(const char* name, int ordinal, int spaces,
@@ -52,8 +53,15 @@ void GenerationCounters::initialize(const char* name, int ordinal, int spaces,
                                      min_capacity, CHECK);
 
     cname = PerfDataManager::counter_name(_name_space, "maxCapacity");
-    PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes,
-                                     max_capacity, CHECK);
+    // Dynamic Max Heap
+    if (Universe::is_dynamic_max_heap_enable()) {
+      _max_size = PerfDataManager::create_variable(SUN_GC, cname, PerfData::U_Bytes,
+                                                   max_capacity, CHECK);
+    } else {
+      _max_size = NULL;
+      PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes,
+                                       max_capacity, CHECK);
+    }
 
     cname = PerfDataManager::counter_name(_name_space, "capacity");
     _current_size =
@@ -88,3 +96,8 @@ void GenerationCounters::update_all() {
   assert(_virtual_space != nullptr, "otherwise, override this method");
   _current_size->set_value(_virtual_space->committed_size());
 }
+
+void GenerationCounters::update_max_size(size_t size) {
+  guarantee(Universe::is_dynamic_max_heap_enable(), "must be");
+  _max_size->set_value(size);
+}
diff --git a/src/hotspot/share/gc/shared/generationCounters.hpp b/src/hotspot/share/gc/shared/generationCounters.hpp
index 292ea61d4..e21192c8f 100644
--- a/src/hotspot/share/gc/shared/generationCounters.hpp
+++ b/src/hotspot/share/gc/shared/generationCounters.hpp
@@ -40,6 +40,8 @@ private:
                   size_t curr_capacity);
 
  protected:
+ // Dynamic Max Heap
+  PerfVariable*      _max_size;            // max size can be change when Dynamic Max Heap is on
   PerfVariable*      _current_size;
   VirtualSpace*      _virtual_space;
 
@@ -72,6 +74,9 @@ private:
 
   virtual void update_all();
 
+  // Dynamic Max Heap
+  void update_max_size(size_t size);
+
   const char* name_space() const        { return _name_space; }
 
 };
diff --git a/src/hotspot/share/gc/shared/referencePolicy.cpp b/src/hotspot/share/gc/shared/referencePolicy.cpp
index 22ef4eabb..f58f334a7 100644
--- a/src/hotspot/share/gc/shared/referencePolicy.cpp
+++ b/src/hotspot/share/gc/shared/referencePolicy.cpp
@@ -64,6 +64,9 @@ LRUMaxHeapPolicy::LRUMaxHeapPolicy() {
 // Capture state (of-the-VM) information needed to evaluate the policy
 void LRUMaxHeapPolicy::setup() {
   size_t max_heap = MaxHeapSize;
+  if (Universe::is_dynamic_max_heap_enable()) {
+    max_heap = Universe::heap()->current_max_heap_size();
+  }
   max_heap -= Universe::heap()->used_at_last_gc();
   max_heap /= M;
 
diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp
index 8b39ea0a9..4b47cd0bc 100644
--- a/src/hotspot/share/memory/universe.cpp
+++ b/src/hotspot/share/memory/universe.cpp
@@ -168,6 +168,9 @@ OopStorage*     Universe::_vm_global = nullptr;
 
 CollectedHeap*  Universe::_collectedHeap = nullptr;
 
+// Dynamic Max Heap
+bool            Universe::_enable_dynamic_max_heap = false;
+
 objArrayOop Universe::the_empty_class_array ()  {
   return (objArrayOop)_the_empty_class_array.resolve();
 }
diff --git a/src/hotspot/share/memory/universe.hpp b/src/hotspot/share/memory/universe.hpp
index 8b9c5bd62..338257966 100644
--- a/src/hotspot/share/memory/universe.hpp
+++ b/src/hotspot/share/memory/universe.hpp
@@ -29,6 +29,7 @@
 #include "oops/array.hpp"
 #include "oops/oopHandle.hpp"
 #include "runtime/handles.hpp"
+#include "runtime/globals_extension.hpp"
 #include "utilities/growableArray.hpp"
 
 // Universe is a name space holding known system classes and objects in the VM.
@@ -195,6 +196,9 @@ class Universe: AllStatic {
   static int _verify_count;                           // number of verifies done
   static long verify_flags;
 
+  // Dynamic Max Heap
+  static bool _enable_dynamic_max_heap;
+
   static uintptr_t _verify_oop_mask;
   static uintptr_t _verify_oop_bits;
 
@@ -400,6 +404,47 @@ class Universe: AllStatic {
 
   // Compiler support
   static int base_vtable_size()               { return _base_vtable_size; }
+
+  // Dynamic Max Heap
+  static const char* dynamic_max_heap_dcmd_name() {
+#ifdef AARCH64
+    if (FLAG_IS_CMDLINE(ElasticMaxHeapSize)) {
+      return "GC.elastic_max_heap";
+    }
+    if (FLAG_IS_CMDLINE(DynamicMaxHeapSizeLimit)) {
+      return "GC.change_max_heap";
+    }
+#endif //AARCH64
+    return "GC.elastic_max_heap";
+  }
+  static const char* dynamic_max_heap_option_name() {
+#ifdef AARCH64
+    if (FLAG_IS_CMDLINE(ElasticMaxHeapSize)) {
+      return "ElasticMaxHeap";
+    }
+    if (FLAG_IS_CMDLINE(DynamicMaxHeapSizeLimit)) {
+      return "DynamicMaxHeap";
+    }
+#endif //AARCH64
+    return "ElasticMaxHeap";
+  }
+  static const char* dynamic_max_heap_size_limit_option_name() {
+#ifdef AARCH64
+    if (FLAG_IS_CMDLINE(ElasticMaxHeapSize)) {
+      return "ElasticMaxHeapSize";
+    }
+    if (FLAG_IS_CMDLINE(DynamicMaxHeapSizeLimit)) {
+      return "DynamicMaxHeapSizeLimit";
+    }
+#endif //AARCH64
+    return "+ElasticMaxHeap";
+  }
+  static bool is_dynamic_max_heap_enable() {
+    NOT_AARCH64(return false;);
+    AARCH64_ONLY(return _enable_dynamic_max_heap;);
+  }
+
+  static void set_dynamic_max_heap_enable(bool a)         { _enable_dynamic_max_heap = a; }
 };
 
 #endif // SHARE_MEMORY_UNIVERSE_HPP
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index fb9baaeaf..752328925 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -31,6 +31,7 @@
 #include "classfile/stringTable.hpp"
 #include "classfile/symbolTable.hpp"
 #include "compiler/compilerDefinitions.hpp"
+#include "gc/shared/dynamicMaxHeap.hpp"
 #include "gc/shared/gcArguments.hpp"
 #include "gc/shared/gcConfig.hpp"
 #include "gc/shared/stringdedup/stringDedup.hpp"
@@ -1470,6 +1471,20 @@ void Arguments::set_use_compressed_oops() {
   // to use UseCompressedOops are InitialHeapSize and MinHeapSize.
   size_t max_heap_size = MAX3(MaxHeapSize, InitialHeapSize, MinHeapSize);
 
+#ifdef AARCH64
+  // DynamicMaxHeap
+  // 1. align DynamicMaxHeapSizeLimit
+  // 2. use DynamicMaxHeapSizeLimit to check whether compressedOops can enabled
+  bool dynamic_max_heap_enable = DynamicMaxHeapChecker::check_dynamic_max_heap_size_limit();
+  if (dynamic_max_heap_enable) {
+     Universe::set_dynamic_max_heap_enable(true);
+     DynamicMaxHeapConfig::set_initial_max_heap_size((size_t)MaxHeapSize);
+     size_t _heap_alignment = GCArguments::compute_heap_alignment();
+     uintx aligned_max_heap_size_limit = align_up(DynamicMaxHeapSizeLimit, _heap_alignment);
+     FLAG_SET_ERGO(DynamicMaxHeapSizeLimit, aligned_max_heap_size_limit);
+     max_heap_size = MAX2(max_heap_size, DynamicMaxHeapSizeLimit);
+  }
+#endif // AARCH64
   if (max_heap_size <= max_heap_for_compressed_oops()) {
     if (FLAG_IS_DEFAULT(UseCompressedOops)) {
       FLAG_SET_ERGO(UseCompressedOops, true);
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 82a6236c3..55e664a18 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -2030,6 +2030,9 @@ const int ObjectAlignmentInBytes = 8;
   product(bool, StressSecondarySupers, false, DIAGNOSTIC,                   \
           "Use a terrible hash function in order to generate many collisions.") \
                                                                             \
+  product(bool, ElasticMaxDirectMemory, false,                              \
+          "Allow change max direct memory size during runtime with jcmd")   \
+                                                                            \
 
 // end of RUNTIME_FLAGS
 
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index dc0cff5fa..e0cca43b9 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -1971,6 +1971,10 @@ bool os::uncommit_memory(char* addr, size_t bytes, bool executable) {
   return res;
 }
 
+bool os::free_heap_physical_memory(char *addr, size_t bytes) {
+  return pd_free_heap_physical_memory(addr, bytes);
+}
+
 bool os::release_memory(char* addr, size_t bytes) {
   assert_nonempty_range(addr, bytes);
   bool res;
diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
index b9c297cb8..833edab01 100644
--- a/src/hotspot/share/runtime/os.hpp
+++ b/src/hotspot/share/runtime/os.hpp
@@ -205,6 +205,7 @@ class os: AllStatic {
                                          size_t alignment_hint,
                                          bool executable, const char* mesg);
   static bool   pd_uncommit_memory(char* addr, size_t bytes, bool executable);
+  static bool   pd_free_heap_physical_memory(char* addr, size_t bytes);
   static bool   pd_release_memory(char* addr, size_t bytes);
 
   static char*  pd_attempt_map_memory_to_file_at(char* addr, size_t bytes, int file_desc);
@@ -449,6 +450,7 @@ class os: AllStatic {
                                       size_t alignment_hint,
                                       bool executable, const char* mesg);
   static bool   uncommit_memory(char* addr, size_t bytes, bool executable = false);
+  static bool   free_heap_physical_memory(char* addr, size_t bytes);
   static bool   release_memory(char* addr, size_t bytes);
 
   // Does the platform support trimming the native heap?
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index b0bfc0eb2..609248146 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -858,6 +858,15 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
   }
 #endif // INCLUDE_JBOLT
 
+  // Dynamic Max Heap: reset heap initial size to MaxHeapSize
+  if (Universe::is_dynamic_max_heap_enable()) {
+    bool success = Universe::heap()->change_max_heap(MaxHeapSize);
+    if (!success) {
+      log_error(dynamic, heap)("VM failed to initialize heap to Xmx " SIZE_FORMAT "K", (MaxHeapSize / K));
+      vm_exit(1);
+    }
+  }
+
   return JNI_OK;
 }
 
diff --git a/src/hotspot/share/runtime/vmOperation.hpp b/src/hotspot/share/runtime/vmOperation.hpp
index e889c3590..7ddd72021 100644
--- a/src/hotspot/share/runtime/vmOperation.hpp
+++ b/src/hotspot/share/runtime/vmOperation.hpp
@@ -117,7 +117,8 @@
   template(GTestStopSafepoint)                    \
   template(JFROldObject)                          \
   template(JvmtiPostObjectFree)                   \
-  template(RendezvousGCThreads)
+  template(RendezvousGCThreads)                   \
+  template(DynamicMaxHeap)
 
 class Thread;
 class outputStream;
diff --git a/src/hotspot/share/services/diagnosticCommand.cpp b/src/hotspot/share/services/diagnosticCommand.cpp
index f36f78639..f22756eda 100644
--- a/src/hotspot/share/services/diagnosticCommand.cpp
+++ b/src/hotspot/share/services/diagnosticCommand.cpp
@@ -34,6 +34,7 @@
 #include "compiler/compileBroker.hpp"
 #include "compiler/directivesParser.hpp"
 #include "gc/shared/gcVMOperations.hpp"
+#include "gc/shared/gcArguments.hpp"
 #include "jvm.h"
 #include "memory/metaspace/metaspaceDCmd.hpp"
 #include "memory/resourceArea.hpp"
@@ -106,6 +107,9 @@ void DCmd::register_dcmds(){
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RunFinalizationDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapInfoDCmd>(full_export, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<FinalizerInfoDCmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ChangeMaxHeapDCmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ElasticMaxHeapDCmd>(full_export, true, false));
+  DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ElasticMaxDirectMemoryDCmd>(full_export, true, false));
 #if INCLUDE_SERVICES
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false));
   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(full_export, true, false));
@@ -465,6 +469,119 @@ void FinalizerInfoDCmd::execute(DCmdSource source, TRAPS) {
   }
 }
 
+ChangeMaxHeapDCmd::ChangeMaxHeapDCmd(outputStream* output, bool heap) :
+  DCmdWithParser(output, heap),
+  _new_max_heap_size("change_max_heap", "New max size of heap", "MEMORY SIZE", true) {
+  _dcmdparser.add_dcmd_argument(&_new_max_heap_size);
+}
+
+int ChangeMaxHeapDCmd::num_arguments(ChangeMaxHeapDCmd* dcmd) {
+  if (dcmd != NULL) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+
+int ChangeMaxHeapDCmd::num_arguments() {
+  ResourceMark rm;
+  ChangeMaxHeapDCmd* dcmd = new ChangeMaxHeapDCmd(NULL, false);
+  return ChangeMaxHeapDCmd::num_arguments(dcmd);
+}
+
+void ChangeMaxHeapDCmd::execute(DCmdSource source, TRAPS) {
+  if (!Universe::is_dynamic_max_heap_enable()) {
+    output()->print_cr("not supported because -XX:DynamicMaxHeapSizeLimit/-XX:ElasticMaxHeapSize was not specified");
+    return;
+  }
+
+#ifdef AARCH64
+  jlong input_max_heap_size = _new_max_heap_size.value()._size;
+  jlong new_max_heap_size = align_up((size_t)input_max_heap_size, HeapAlignment);
+  if (new_max_heap_size != input_max_heap_size) {
+    output()->print_cr("align the given value " SIZE_FORMAT " up to " SIZE_FORMAT "K for heap alignment " SIZE_FORMAT "K",
+                      input_max_heap_size,
+                      (new_max_heap_size / K),
+                      (HeapAlignment / K));
+  }
+
+  bool is_validate = Universe::heap()->check_new_max_heap_validity(new_max_heap_size, output());
+  if (!is_validate) {
+    output()->print_cr("%s fail", Universe::dynamic_max_heap_dcmd_name());
+    return;
+  }
+  output()->print_cr("%s (" SIZE_FORMAT "K" "->" SIZE_FORMAT "K)(" SIZE_FORMAT "K)",
+                    Universe::dynamic_max_heap_dcmd_name(),
+                    (Universe::heap()->current_max_heap_size() / K),
+                    (new_max_heap_size / K),
+                    (DynamicMaxHeapSizeLimit / K));
+
+  bool success = Universe::heap()->change_max_heap(new_max_heap_size);
+  if (success) {
+    output()->print_cr("%s success", Universe::dynamic_max_heap_dcmd_name());
+  } else {
+    output()->print_cr("%s fail", Universe::dynamic_max_heap_dcmd_name());
+  }
+#endif // AARCH64
+}
+
+ElasticMaxHeapDCmd::ElasticMaxHeapDCmd(outputStream* output, bool heap) :
+  ChangeMaxHeapDCmd(output, heap) {
+}
+
+int ElasticMaxHeapDCmd::num_arguments() {
+  ResourceMark rm;
+  ElasticMaxHeapDCmd* dcmd = new ElasticMaxHeapDCmd(NULL, false);
+  return ChangeMaxHeapDCmd::num_arguments(dcmd);
+}
+
+ElasticMaxDirectMemoryDCmd::ElasticMaxDirectMemoryDCmd(outputStream* output, bool heap) :
+  DCmdWithParser(output, heap),
+  _new_max_direct_memory("elastic_max_direct_memory", "New max size of direct memory", "MEMORY SIZE", true) {
+  _dcmdparser.add_dcmd_argument(&_new_max_direct_memory);
+}
+
+int ElasticMaxDirectMemoryDCmd::num_arguments() {
+  ResourceMark rm;
+  ElasticMaxDirectMemoryDCmd* dcmd = new ElasticMaxDirectMemoryDCmd(NULL, false);
+  if (dcmd != NULL) {
+    DCmdMark mark(dcmd);
+    return dcmd->_dcmdparser.num_arguments();
+  } else {
+    return 0;
+  }
+}
+
+void ElasticMaxDirectMemoryDCmd::execute(DCmdSource source, TRAPS) {
+  if (!ElasticMaxDirectMemory) {
+    output()->print_cr("not supported because -XX:+ElasticMaxDirectMemory was not specified");
+    return;
+  }
+
+  jlong new_max_direct_memory = _new_max_direct_memory.value()._size;
+  Symbol* klass = vmSymbols::java_nio_Bits();
+  Klass* k = SystemDictionary::resolve_or_fail(klass, true, CHECK);
+
+  // invoke the updateMaxMemory method
+  JavaValue result(T_OBJECT);
+  JavaCallArguments args;
+  args.push_long(new_max_direct_memory);
+  JavaCalls::call_static(&result,
+                         k,
+                         vmSymbols::updateMaxMemory_name(),
+                         vmSymbols::updateMaxMemory_signature(),
+                         &args,
+                         CHECK);
+  oop msg = cast_to_oop(result.get_jobject());
+  if (msg != NULL) {
+    char* text = java_lang_String::as_utf8_string(msg);
+    if (text != NULL) {
+      output()->print_cr("%s", text);
+    }
+  }
+}
+
 #if INCLUDE_SERVICES // Heap dumping/inspection supported
 HeapDumpDCmd::HeapDumpDCmd(outputStream* output, bool heap) :
                            DCmdWithParser(output, heap),
diff --git a/src/hotspot/share/services/diagnosticCommand.hpp b/src/hotspot/share/services/diagnosticCommand.hpp
index ad018259f..98ffa4f57 100644
--- a/src/hotspot/share/services/diagnosticCommand.hpp
+++ b/src/hotspot/share/services/diagnosticCommand.hpp
@@ -312,6 +312,59 @@ public:
   virtual void execute(DCmdSource source, TRAPS);
 };
 
+class ChangeMaxHeapDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<MemorySizeArgument> _new_max_heap_size;
+  static int num_arguments(ChangeMaxHeapDCmd* dcmd);
+public:
+  ChangeMaxHeapDCmd(outputStream* output, bool heap);
+  static const char* name() { return "GC.change_max_heap"; }
+  static const char* description() {
+    return "change dynamic max heap size during runtime.";
+  }
+  static const char* impact() {
+    return "Medium";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+      "monitor", NULL};
+      return p;
+  }
+  static int num_arguments();
+  void execute(DCmdSource source, TRAPS) override;
+};
+
+class ElasticMaxHeapDCmd : public ChangeMaxHeapDCmd {
+public:
+  ElasticMaxHeapDCmd(outputStream* output, bool heap);
+  static const char* name() { return "GC.elastic_max_heap"; }
+  static const char* description() {
+    return "try elastic max heap size during runtime.";
+  }
+  static int num_arguments();
+};
+
+class ElasticMaxDirectMemoryDCmd : public DCmdWithParser {
+protected:
+  DCmdArgument<MemorySizeArgument> _new_max_direct_memory;
+public:
+  ElasticMaxDirectMemoryDCmd(outputStream* output, bool heap);
+  static const char* name() { return "GC.elastic_max_direct_memory"; }
+  static const char* description() {
+    return "try elastic max direct memory during runtime.";
+  }
+  static const char* impact() {
+    return "Medium";
+  }
+  static const JavaPermission permission() {
+    JavaPermission p = {"java.lang.management.ManagementPermission",
+      "monitor", NULL};
+      return p;
+  }
+  static int num_arguments();
+  void execute(DCmdSource source, TRAPS) override;
+};
+
 #if INCLUDE_SERVICES   // Heap dumping supported
 // See also: dump_heap in attachListener.cpp
 class HeapDumpDCmd : public DCmdWithParser {
diff --git a/src/java.base/share/classes/java/nio/Bits.java b/src/java.base/share/classes/java/nio/Bits.java
index b11cb4947..9b2b3d23a 100644
--- a/src/java.base/share/classes/java/nio/Bits.java
+++ b/src/java.base/share/classes/java/nio/Bits.java
@@ -210,6 +210,32 @@ class Bits {                            // package-private
         assert cnt >= 0 && reservedMem >= 0 && totalCap >= 0;
     }
 
+    static String updateMaxMemory(long newSize) {
+        long reservedMem = RESERVED_MEMORY.get();
+        StringBuilder sb = new StringBuilder();
+        if (newSize >= reservedMem) {
+            sb.append("GC.elastic_max_direct_memory (");
+            sb.append(MAX_MEMORY / 1024).append("K->");
+            sb.append(newSize / 1024).append("K)");
+            sb.append("\n");
+            sb.append("GC.elastic_max_direct_memory success");
+            // update VM.maxDirectMemory and Bits.MAX_MEMORY
+            // specially, if shrink, in a multi-threaded scenario,
+            // VM.maxDirectMemory() may be inconsistent with the actual direct memory usage.
+            // the new maxMemory will take effect the next time direct memory is allocated.
+            VM.setMaxDirectMemory(newSize);
+            MAX_MEMORY = VM.maxDirectMemory();
+        } else {
+            sb.append("GC.elastic_max_direct_memory ");
+            sb.append(newSize / 1024).append("K below current reserved direct memory ");
+            sb.append(reservedMem / 1024).append("K");
+            sb.append("\n");
+            sb.append("GC.elastic_max_direct_memory fail");
+        }
+        String output = sb.toString();
+        return output;
+    }
+
     static final BufferPool BUFFER_POOL = new BufferPool() {
         @Override
         public String getName() {
diff --git a/src/java.base/share/classes/jdk/internal/misc/VM.java b/src/java.base/share/classes/jdk/internal/misc/VM.java
index 3e7351f63..f1a585e42 100644
--- a/src/java.base/share/classes/jdk/internal/misc/VM.java
+++ b/src/java.base/share/classes/jdk/internal/misc/VM.java
@@ -145,6 +145,12 @@ public class VM {
         return directMemory;
     }
 
+    // ElasticMaxDirectMemory
+    // update max direct memory size
+    public static void setMaxDirectMemory(long size) {
+        directMemory = size;
+    }
+
     // User-controllable flag that determines if direct buffers should be page
     // aligned. The "-XX:+PageAlignDirectMemory" option can be used to force
     // buffers, allocated by ByteBuffer.allocateDirect, to be page aligned.
diff --git a/test/hotspot/jtreg/gc/TestSmallHeap.java b/test/hotspot/jtreg/gc/TestSmallHeap.java
index fd6c5860f..5d711e38b 100644
--- a/test/hotspot/jtreg/gc/TestSmallHeap.java
+++ b/test/hotspot/jtreg/gc/TestSmallHeap.java
@@ -104,7 +104,7 @@ public class TestSmallHeap {
         analyzer.shouldHaveExitValue(0);
 
         expectedMaxHeap = Math.max(expectedMaxHeap, minMaxHeap);
-        long maxHeapSize = Long.parseLong(analyzer.firstMatch("MaxHeapSize.+=\\s+(\\d+)",1));
+        long maxHeapSize = Long.parseLong(analyzer.firstMatch("\\s+MaxHeapSize\\s+=\\s+(\\d+)",1));
         long actualHeapSize = Long.parseLong(analyzer.firstMatch(VerifyHeapSize.actualMsg + "(\\d+)",1));
         Asserts.assertEQ(maxHeapSize, expectedMaxHeap);
         Asserts.assertLessThanOrEqual(actualHeapSize, maxHeapSize);
diff --git a/test/hotspot/jtreg/gc/arguments/TestMaxRAMFlags.java b/test/hotspot/jtreg/gc/arguments/TestMaxRAMFlags.java
index 13bb36ecc..330e923c2 100644
--- a/test/hotspot/jtreg/gc/arguments/TestMaxRAMFlags.java
+++ b/test/hotspot/jtreg/gc/arguments/TestMaxRAMFlags.java
@@ -90,7 +90,7 @@ public class TestMaxRAMFlags {
   }
 
   private static String getFlagValue(String flag, String where) {
-    Matcher m = Pattern.compile(flag + "\\s+:?=\\s+\\d+").matcher(where);
+    Matcher m = Pattern.compile("\\s+" + flag + "\\s+:?=\\s+\\d+").matcher(where);
     if (!m.find()) {
       throw new RuntimeException("Could not find value for flag " + flag + " in output string");
     }
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/BasicTest.java b/test/hotspot/jtreg/gc/dynamicmaxheap/BasicTest.java
new file mode 100644
index 000000000..96d73e29f
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/BasicTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023, 2024 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+package gc.dynamicmaxheap;
+
+/*
+ * @test
+ * @summary Test Basic Elastic Max Heap resize
+ * @requires (os.family == "linux") & (os.arch == "aarch64")
+ * @library /test/lib
+ * @build gc.dynamicmaxheap.TestBase
+ * @compile test_classes/NotActiveHeap.java
+ * @run driver gc.dynamicmaxheap.BasicTest
+ */
+
+import java.lang.reflect.Field;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Asserts;
+
+public class BasicTest extends TestBase {
+    public static void main(String[] args) throws Exception {
+        test("-XX:+UseParallelGC");
+        test("-XX:+UseG1GC");
+    }
+
+    private static void test(String heap_type_or_process_count) throws Exception {
+        String architecture = System.getProperty("os.arch");
+        // Xms = 100M - 1B, Xmx = 600M - 1B, ElasticMaxHeapSize = 1G - 1B
+        // unaligned arguments should be fine
+        ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(heap_type_or_process_count, "-XX:+ElasticMaxHeap", "-Xms104857599", "-Xmx629145599", "-XX:ElasticMaxHeapSize=1073741823", "NotActiveHeap");
+        Process p = pb.start();
+        try {
+            long pid = p.pid();
+            System.out.println(pid);
+
+            // shrink to 500M should be fine for any GC
+            String[] contains1 = {
+                "GC.elastic_max_heap success",
+                "GC.elastic_max_heap (",
+            };
+            resizeAndCheck(pid, "500M", contains1, null);
+
+            // expand to 800M should be fine for any GC
+            String[] contains2 = {
+                "GC.elastic_max_heap success",
+                "GC.elastic_max_heap (",
+            };
+            resizeAndCheck(pid, "800M", contains2, null);
+
+            // expand to 2G should fail
+            String[] contains3 = {
+                "GC.elastic_max_heap fail",
+                "2097152K exceeds maximum limit",
+            };
+            resizeAndCheck(pid, "2G", contains3, null);
+
+            // epxand to 1G should be fine
+            String[] contains4 = {
+                "GC.elastic_max_heap success",
+                "GC.elastic_max_heap (",
+            };
+            resizeAndCheck(pid, "1G", contains4, null);
+
+            // shrink to 300M should be fine
+            // unaligned arguments should be fine, new_size = 300M -1B
+            String[] contains5 = {
+                "GC.elastic_max_heap success",
+                "GC.elastic_max_heap (",
+            };
+            resizeAndCheck(pid, "314572799", contains5, null);
+        } finally {
+            p.destroy();
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/DirectMemoryBasicTest.java b/test/hotspot/jtreg/gc/dynamicmaxheap/DirectMemoryBasicTest.java
new file mode 100644
index 000000000..5c13e8de4
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/DirectMemoryBasicTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023, 2024 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test Basic Elastic Max Direct Memory resize
+ * @requires (os.family == "linux") & (os.arch == "aarch64")
+ * @library /test/lib
+ * @build gc.dynamicmaxheap.TestBase
+ * @compile test_classes/NotActiveDirectMemory.java
+ * @run driver gc.dynamicmaxheap.DirectMemoryBasicTest
+ */
+
+package gc.dynamicmaxheap;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Asserts;
+
+public class DirectMemoryBasicTest extends TestBase {
+    public static void main(String[] args) throws Exception {
+        test("-XX:+UseParallelGC");
+        test("-XX:+UseG1GC");
+    }
+
+    private static void test(String heap_type) throws Exception {
+        String architecture = System.getProperty("os.arch");
+        ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(heap_type,
+                                                                  "-XX:+ElasticMaxDirectMemory",
+                                                                  "-Xms100M",
+                                                                  "-Xmx100M",
+                                                                  "-XX:MaxDirectMemorySize=200M",
+                                                                  "NotActiveDirectMemory");
+        Process p = pb.start();
+        long pid;
+        try {
+            pid = p.pid();
+            System.out.println(pid);
+
+            // NotActiveDirectMemory will alloc 100M direct memory
+            // expand to 300M should be fine for any GC
+            String[] contains1 = {
+                "GC.elastic_max_direct_memory (",
+                "GC.elastic_max_direct_memory success"
+            };
+            resizeAndCheck(pid, "300M", contains1, null, "GC.elastic_max_direct_memory");
+
+            // shrink to 50M should be fail for any GC,
+            String[] contains2 = {
+                "below current reserved direct memory",
+                "GC.elastic_max_direct_memory fail"
+            };
+            resizeAndCheck(pid, "50M", contains2, null, "GC.elastic_max_direct_memory");
+        } finally {
+            p.destroy();
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/LimitDirectMemoryTest.java b/test/hotspot/jtreg/gc/dynamicmaxheap/LimitDirectMemoryTest.java
new file mode 100644
index 000000000..61266a1e0
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/LimitDirectMemoryTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023, 2024 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @summary Test max direct memory can take effect after resize
+ * @requires (os.family == "linux") & (os.arch == "aarch64")
+ * @library /test/lib
+ * @build gc.dynamicmaxheap.TestBase
+ * @compile test_classes/LimitDirectMemoryTestBasic.java
+ * @run driver gc.dynamicmaxheap.LimitDirectMemoryTest
+ */
+
+package gc.dynamicmaxheap;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class LimitDirectMemoryTest extends TestBase {
+    public static void main(String[] args) throws Exception {
+        // Test1
+        // init max direct memory is 200M
+        // expand to 300M and alloc 300M direct memory should be fine
+        String[] contains1 = {
+            "allocation finish!"
+        };
+        String[] not_contains1 = {
+            "java.lang.OutOfMemoryError: Cannot reserve "
+        };
+        Test("-XX:+UseParallelGC", "300M", "300", contains1, not_contains1);
+        Test("-XX:+UseG1GC", "300M", "300", contains1, not_contains1);
+        
+        // Test2
+        // init max direct memory is 200M
+        // shrink to 50M and alloc 100M direct memory should oom
+        String[] contains2 = {
+            "java.lang.OutOfMemoryError: Cannot reserve "
+        };
+        String[] not_contains2 = {
+            "allocation finish!"
+        };
+        Test("-XX:+UseParallelGC", "50M", "100", contains2, not_contains2);
+        Test("-XX:+UseG1GC", "50M", "100", contains2, not_contains2); 
+    }
+
+    private static void Test(String heap_type, String new_size, String alloc_size, String[] contains, String[] not_contains) throws Exception {
+        ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(heap_type,
+                                                                  "-Dtest.jdk=" + System.getProperty("test.jdk"),
+                                                                  "-XX:+ElasticMaxDirectMemory",
+                                                                  "-XX:MaxDirectMemorySize=200M",
+                                                                  "-Xms100M",
+                                                                  "-Xmx100M",
+                                                                  "LimitDirectMemoryTestBasic",
+                                                                  new_size,
+                                                                  alloc_size);
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        CheckOutput(output, contains, not_contains);
+    }
+
+    public static void CheckOutput(OutputAnalyzer output, String[] contains, String[] not_contains) throws Exception {
+        System.out.println(output.getOutput());
+        if (contains != null) {
+            for (String s : contains) {
+                output.shouldContain(s);
+            }
+        }
+        if (not_contains != null) {
+            for (String s : not_contains) {
+                output.shouldNotContain(s);
+            }
+        }
+    }
+}
+
+    
\ No newline at end of file
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/MemoryPoolTest.java b/test/hotspot/jtreg/gc/dynamicmaxheap/MemoryPoolTest.java
new file mode 100644
index 000000000..7f854b125
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/MemoryPoolTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023, 2024 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+package gc.dynamicmaxheap;
+
+import java.lang.management.*;
+import java.util.*;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Asserts;
+
+/**
+ * @test MemoryPoolTest
+ * @summary test MemoryPool MemoryUsage returns correct max size
+ * @requires (os.family == "linux") & (os.arch == "aarch64")
+ * @library /test/lib
+ * @build gc.dynamicmaxheap.TestBase
+ * @run main/othervm -Xms50M -Xmx2G -XX:+ElasticMaxHeap -XX:+UseParallelGC -Xlog:dynamic+heap=debug gc.dynamicmaxheap.MemoryPoolTest
+ * @run main/othervm -Xms50M -Xmx2G -XX:+ElasticMaxHeap -XX:+UseG1GC -Xlog:dynamic+heap=debug gc.dynamicmaxheap.MemoryPoolTest
+ */
+public class MemoryPoolTest extends TestBase {
+    static Object[] root_array;
+    static final long M = 1024L * 1024L;
+    static long edenMaxSize;
+    static long survivorMaxSize;
+    static long oldGenMaxSize;
+    public static void main(String[] args) throws Exception {
+        String architecture = System.getProperty("os.arch");
+        long pid = ProcessTools.getProcessId();
+        /*
+         * Steps: start with 2G heap
+         * 1. start and allocate about 1G object
+         * 2. get MemoryPool and usage as expected
+         * 3. launch jcmd resize to 100M and expect success
+         * 4. get MemoryPool and usage as expected
+         */
+        alloc_and_free(1024L * 1024L * 1024L);
+        MemoryMXBean mem = ManagementFactory.getMemoryMXBean();
+        MemoryUsage usage = mem.getHeapMemoryUsage();
+        long max = usage.getMax() / M;
+        long committed = usage.getCommitted() / M;
+        long used = usage.getUsed() / M;
+        System.out.println("After alloc -- Heap Max: " + max +
+                           "M, Committed: " + committed +
+                           "M, Used: " + used + "M");
+        Asserts.assertGT(max, 1024L);
+        Asserts.assertGTE(max, committed);
+        Asserts.assertGTE(committed, used);
+
+        // check eden, survivor and old memory pool after alloc
+        get_memory_info();
+        System.out.println("After alloc -- Eden Max: " + edenMaxSize +
+                           "M, Survivor Max: " + survivorMaxSize +
+                           "M, Old Max: " + oldGenMaxSize + "M");
+        long orig_old = oldGenMaxSize;
+        long orig_eden = edenMaxSize;
+        Asserts.assertGT(edenMaxSize + survivorMaxSize + oldGenMaxSize, 1024L);
+        Asserts.assertGTE(max, oldGenMaxSize);
+        Asserts.assertGTE(oldGenMaxSize, edenMaxSize);
+        Asserts.assertGTE(oldGenMaxSize, survivorMaxSize);
+
+        root_array = null; // release
+
+        // shrink to 500M should be fine for any GC
+        String[] contains1 = {
+            "GC.elastic_max_heap (",
+            "GC.elastic_max_heap success"
+        };
+        resizeAndCheck(pid, "100M", contains1, null);
+        mem = ManagementFactory.getMemoryMXBean();
+        usage = mem.getHeapMemoryUsage();
+        max = usage.getMax() / M;
+        committed = usage.getCommitted() / M;
+        used = usage.getUsed() / M;
+        System.out.println("After resize -- Heap Max: " + max +
+                           "M, Committed: " + committed +
+                           "M, Used: " + used + "M");
+        long target_size = 100L;
+        if (architecture.equals("aarch64")) {
+            target_size = 128L;
+        }
+        Asserts.assertLTE(max, target_size);
+        Asserts.assertGTE(max, committed);
+        Asserts.assertGTE(committed, used);
+
+        // check eden, survivor and old memory pool after resize
+        get_memory_info();
+        System.out.println("After resize -- Eden Max: " + edenMaxSize +
+                           "M, Survivor Max: " + survivorMaxSize +
+                           "M, Old Max: " + oldGenMaxSize + "M");
+        Asserts.assertLT(oldGenMaxSize, orig_old);
+        Asserts.assertLTE(edenMaxSize, orig_eden);
+    }
+
+    static void alloc_and_free(long size) {
+        // suppose compressed
+        // each int array size is 8(MarkOop + len) + 4 * len
+        // each object is 1k int[254]
+        int root_len = (int)(size / 1024L);
+        root_array = new Object[root_len];
+        for (int i = 0; i < root_len; i++) {
+            root_array[i] = new int[254];
+        }
+    }
+
+    static void get_memory_info() {
+        List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans();
+        for (MemoryPoolMXBean memoryPoolMXBean : memoryPoolMXBeans) {
+            String name = memoryPoolMXBean.getName();
+            MemoryUsage usage = memoryPoolMXBean.getUsage();
+            long max_size = usage.getMax() / M;
+            if (name.contains("Eden")) {
+                edenMaxSize = max_size;
+            } else if (name.contains("Survivor")) {
+                survivorMaxSize = max_size;
+            } else if (name.contains("Old")) {
+                oldGenMaxSize = max_size;
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/OptionsCheck.java b/test/hotspot/jtreg/gc/dynamicmaxheap/OptionsCheck.java
new file mode 100644
index 000000000..13c4120b0
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/OptionsCheck.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+package gc.dynamicmaxheap;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Asserts;
+
+/**
+ * @test OptionsCheck
+ * @summary test invalid options combinations with elastic max heap
+ * @requires (os.family == "linux") & (os.arch == "aarch64")
+ * @library /test/lib
+ * @build gc.dynamicmaxheap.TestBase
+ * @run driver gc.dynamicmaxheap.OptionsCheck
+ */
+public class OptionsCheck extends TestBase {
+    public static void main(String[] args) throws Exception {
+        String[] key_output = {
+            "can not be used with",
+        };
+        String[] key_output2 = {
+            "should be used with",
+        };
+        LaunchAndCheck(key_output, null, "-XX:+ElasticMaxHeap", "-Xmn200M", "-version");
+        LaunchAndCheck(key_output, null, "-XX:+ElasticMaxHeap", "-XX:MaxNewSize=300M", "-version");
+        LaunchAndCheck(key_output, null, "-XX:+ElasticMaxHeap", "-XX:OldSize=1G", "-version");
+        LaunchAndCheck(key_output2, null, "-XX:+ElasticMaxHeap", "-XX:-UseAdaptiveSizePolicy", "-version");
+        String[] contains1 = {
+            "-XX:ElasticMaxHeapSize should be used together with -Xmx/-XX:MaxHeapSize"
+        };
+        LaunchAndCheck(contains1, null, "-XX:+ElasticMaxHeap", "-XX:ElasticMaxHeapSize=100M", "-version");
+        String[] contains2 = {
+            "-XX:ElasticMaxHeapSize should be larger than -Xmx/-XX:MaxHeapSize"
+        };
+        LaunchAndCheck(contains2, null, "-XX:+ElasticMaxHeap", "-XX:ElasticMaxHeapSize=1G", "-Xmx2G", "-version");
+    }
+
+    public static void LaunchAndCheck(String[] contains, String[] not_contains, String... command) throws Exception {
+        ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(command);
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        System.out.println(output.getOutput());
+        if (contains != null) {
+            for (String s : contains) {
+                output.shouldContain(s);
+            }
+        }
+        if (not_contains != null) {
+            for (String s : contains) {
+                output.shouldNotContain(s);
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/RuntimeMemoryTest.java b/test/hotspot/jtreg/gc/dynamicmaxheap/RuntimeMemoryTest.java
new file mode 100644
index 000000000..d4d3b9b71
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/RuntimeMemoryTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023, 2024 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+package gc.dynamicmaxheap;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Asserts;
+
+/**
+ * @test RuntimeMemoryTest
+ * @summary test java.lang.Runtime max memory and total memory
+ * @requires (os.family == "linux") & (os.arch == "aarch64")
+ * @library /test/lib
+ * @build gc.dynamicmaxheap.TestBase
+ * @run main/othervm -Xms50M -Xmx2G -XX:+ElasticMaxHeap -XX:+UseParallelGC gc.dynamicmaxheap.RuntimeMemoryTest
+ * @run main/othervm -Xms50M -Xmx2G -XX:+ElasticMaxHeap -XX:+UseG1GC gc.dynamicmaxheap.RuntimeMemoryTest
+ */
+public class RuntimeMemoryTest extends TestBase {
+    static Object[] root_array;
+    static final long M = 1024L * 1024L;
+    public static void main(String[] args) throws Exception {
+        String architecture = System.getProperty("os.arch");
+        long pid = ProcessTools.getProcessId();
+        /*
+         * Steps: start with 2G heap
+         * 1. start and allocate about 1G object
+         * 2. get totalMemory/maxMemory/freeMemory as expected
+         * 3. launch jcmd resize to 100M and expect success
+         * 4. get totalMemory/maxMemory/freeMemory as expected
+         */
+        Runtime r = Runtime.getRuntime();
+        // GC rarely happens between these call, should align with size
+        long max = r.maxMemory() / M;
+        long total = r.totalMemory() / M;
+        long free = r.freeMemory() / M;
+        System.out.println("Before alloc -- Max: " + max + "M, Total: " + total + "M, Free: " + free + "M");
+        alloc_and_free(1024L * 1024L * 1024L);
+
+        max = r.maxMemory() / M;
+        total = r.totalMemory() / M;
+        free = r.freeMemory() / M;
+        root_array = null; // release
+        System.out.println("After alloc -- Max: " + max + "M, Total: " + total + "M, Free: " + free + "M");
+        Asserts.assertGT(max, 1024L);
+        Asserts.assertGTE(max, total);
+        Asserts.assertGT(max, free);
+
+        // shrink to 500M should be fine for any GC
+        String[] contains1 = {
+            "GC.elastic_max_heap (",
+            "GC.elastic_max_heap success"
+        };
+        resizeAndCheck(pid, "100M", contains1, null);
+        max = r.maxMemory() / M;
+        total = r.totalMemory() / M;
+        free = r.freeMemory() / M;
+        System.out.println("After resize -- Max: " + max + "M, Total: " + total + "M, Free: " + free + "M");
+        long target_size = 101L;
+        if (architecture.equals("aarch64")) {
+            target_size = 129L;
+        }
+        Asserts.assertLT(max, target_size);
+        Asserts.assertGTE(max, total);
+        Asserts.assertGT(max, free);
+    }
+
+    static void alloc_and_free(long size) {
+        // suppose compressed
+        // each int array size is 8(MarkOop + len) + 4 * len
+        // each object is 1k int[254]
+        int root_len = (int)(size / 1024L);
+        root_array = new Object[root_len];
+        for (int i = 0; i < root_len; i++) {
+            root_array[i] = new int[254];
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/TestBase.java b/test/hotspot/jtreg/gc/dynamicmaxheap/TestBase.java
new file mode 100644
index 000000000..42023e9bb
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/TestBase.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+package gc.dynamicmaxheap;
+
+import java.lang.reflect.Field;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Asserts;
+
+public class TestBase {
+    // start jcmd and check output string
+    public static void resizeAndCheck(long pid, String new_size, String[] contains, String[] not_contains) throws Exception {
+        resizeAndCheck(pid, new_size, contains, not_contains, "GC.elastic_max_heap");
+    }
+    public static void resizeAndCheck(long pid, String new_size, String[] contains, String[] not_contains, String type) throws Exception {
+        ProcessBuilder pb = new ProcessBuilder();
+        pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), Long.toString(pid), type, new_size});
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        System.out.println(output.getOutput());
+        if (contains != null) {
+            for (String s : contains) {
+                output.shouldContain(s);
+            }
+        }
+        if (not_contains != null) {
+            for (String s : not_contains) {
+                output.shouldNotContain(s);
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/LimitDirectMemoryTestBasic.java b/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/LimitDirectMemoryTestBasic.java
new file mode 100644
index 000000000..75b657539
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/LimitDirectMemoryTestBasic.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.JDKToolFinder;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
+
+public class LimitDirectMemoryTestBasic {
+    public static void main(String[] args) throws Exception {
+        long pid = ProcessTools.getProcessId();
+        String new_size = args[0];
+        int alloc_size = Integer.parseInt(args[1]);
+
+        resize(pid, new_size);
+
+        try {
+            // alloc direct memory
+            int single_alloc_size = 1 * 1024 * 1024;
+            ByteBuffer[] buffers = new ByteBuffer[alloc_size];
+            for (int i = 0; i < alloc_size; i++) {
+                buffers[i] = ByteBuffer.allocateDirect(single_alloc_size);
+            }
+        } catch (OutOfMemoryError e) {
+            System.out.println(e);
+            throw e;
+        }
+        System.out.println("allocation finish!");   
+    }
+
+    static void resize(long pid, String new_size) {
+        try {
+            Process process = Runtime.getRuntime().exec(JDKToolFinder.getJDKTool("jcmd") + " " + pid + " GC.elastic_max_direct_memory " + new_size);
+            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+            String line;
+            while ((line = reader.readLine()) != null) {
+                System.out.println(line);
+            }
+            reader.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/NotActiveDirectMemory.java b/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/NotActiveDirectMemory.java
new file mode 100644
index 000000000..0cec96c67
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/NotActiveDirectMemory.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+import java.nio.ByteBuffer;
+
+public class NotActiveDirectMemory {
+    public static void main(String[] args) throws Exception {
+        // alloc 100M direct memory
+        try {
+            int single_alloc_size = 1 * 1024 * 1024;
+            ByteBuffer[] buffers = new ByteBuffer[100];
+            for (int i = 0; i < 100; i++) {
+                buffers[i] = ByteBuffer.allocateDirect(single_alloc_size);
+            }
+        } catch (OutOfMemoryError e) {
+            System.out.println(e);
+            throw e;
+        }
+        System.out.println("allocation finish!");   
+        while (true) {
+            Thread.sleep(1000);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/NotActiveHeap.java b/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/NotActiveHeap.java
new file mode 100644
index 000000000..e4c29854e
--- /dev/null
+++ b/test/hotspot/jtreg/gc/dynamicmaxheap/test_classes/NotActiveHeap.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. 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.
+ *
+ * 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.
+ */
+
+public class NotActiveHeap {
+    public static void main(String[] args) throws Exception {
+        while (true) {
+            Thread.sleep(1000);
+        }
+    }
+}
-- 
2.50.1