diff a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
@@ -30,10 +30,11 @@
 #include "runtime/java.hpp"
 #include "runtime/os.inline.hpp"
 #include "runtime/vm_version.hpp"
 #include "utilities/formatBuffer.hpp"
 #include "utilities/macros.hpp"
+#include "utilities/ostream.hpp"
 
 int VM_Version::_cpu;
 int VM_Version::_model;
 int VM_Version::_model2;
 int VM_Version::_variant;
@@ -46,10 +47,12 @@
 int VM_Version::_initial_sve_vector_length;
 int VM_Version::_max_supported_sve_vector_length;
 bool VM_Version::_rop_protection;
 uintptr_t VM_Version::_pac_mask;
 
+const char* VM_Version::_features_names[MAX_CPU_FEATURES] = { nullptr };
+
 SpinWait VM_Version::_spin_wait;
 
 static SpinWait get_spin_wait_desc() {
   if (strcmp(OnSpinWaitInst, "nop") == 0) {
     return SpinWait(SpinWait::NOP, OnSpinWaitInstCount);
@@ -67,10 +70,15 @@
 
   return SpinWait{};
 }
 
 void VM_Version::initialize() {
+#define SET_CPU_FEATURE_NAME(id, name, bit) \
+  _features_names[bit] = XSTR(name);
+  CPU_FEATURE_FLAGS(SET_CPU_FEATURE_NAME)
+#undef SET_CPU_FEATURE_NAME
+
   _supports_atomic_getset4 = true;
   _supports_atomic_getadd4 = true;
   _supports_atomic_getset8 = true;
   _supports_atomic_getadd8 = true;
 
@@ -201,11 +209,11 @@
     }
   }
 
   // Cortex A53
   if (_cpu == CPU_ARM && model_is(0xd03)) {
-    _features |= CPU_A53MAC;
+    set_feature(CPU_A53MAC);
     if (FLAG_IS_DEFAULT(UseSIMDForArrayEquals)) {
       FLAG_SET_DEFAULT(UseSIMDForArrayEquals, false);
     }
   }
 
@@ -241,11 +249,11 @@
     if (FLAG_IS_DEFAULT(AlwaysMergeDMB)) {
       FLAG_SET_DEFAULT(AlwaysMergeDMB, false);
     }
   }
 
-  if (_features & (CPU_FP | CPU_ASIMD)) {
+  if (supports_feature(CPU_FP) || supports_feature(CPU_ASIMD)) {
     if (FLAG_IS_DEFAULT(UseSignumIntrinsic)) {
       FLAG_SET_DEFAULT(UseSignumIntrinsic, true);
     }
   }
 
@@ -404,33 +412,33 @@
   } else if (UseGHASHIntrinsics) {
     warning("GHASH intrinsics are not available on this CPU");
     FLAG_SET_DEFAULT(UseGHASHIntrinsics, false);
   }
 
-  if (_features & CPU_ASIMD) {
+  if (supports_feature(CPU_ASIMD)) {
     if (FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) {
       UseChaCha20Intrinsics = true;
     }
   } else if (UseChaCha20Intrinsics) {
     if (!FLAG_IS_DEFAULT(UseChaCha20Intrinsics)) {
       warning("ChaCha20 intrinsic requires ASIMD instructions");
     }
     FLAG_SET_DEFAULT(UseChaCha20Intrinsics, false);
   }
 
-  if (_features & CPU_ASIMD) {
+  if (supports_feature(CPU_ASIMD)) {
       if (FLAG_IS_DEFAULT(UseKyberIntrinsics)) {
           UseKyberIntrinsics = true;
       }
   } else if (UseKyberIntrinsics) {
       if (!FLAG_IS_DEFAULT(UseKyberIntrinsics)) {
           warning("Kyber intrinsics require ASIMD instructions");
       }
       FLAG_SET_DEFAULT(UseKyberIntrinsics, false);
   }
 
-  if (_features & CPU_ASIMD) {
+  if (supports_feature(CPU_ASIMD)) {
       if (FLAG_IS_DEFAULT(UseDilithiumIntrinsics)) {
           UseDilithiumIntrinsics = true;
       }
   } else if (UseDilithiumIntrinsics) {
       if (!FLAG_IS_DEFAULT(UseDilithiumIntrinsics)) {
@@ -627,36 +635,29 @@
 
   check_virtualizations();
 
   // Sync SVE related CPU features with flags
   if (UseSVE < 2) {
-    _features &= ~CPU_SVE2;
-    _features &= ~CPU_SVEBITPERM;
+    clear_feature(CPU_SVE2);
+    clear_feature(CPU_SVEBITPERM);
   }
   if (UseSVE < 1) {
-    _features &= ~CPU_SVE;
+    clear_feature(CPU_SVE);
   }
 
   // Construct the "features" string
-  char buf[512];
-  int buf_used_len = os::snprintf_checked(buf, sizeof(buf), "0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision);
+  stringStream ss(512);
+  ss.print("0x%02x:0x%x:0x%03x:%d", _cpu, _variant, _model, _revision);
   if (_model2) {
-    os::snprintf_checked(buf + buf_used_len, sizeof(buf) - buf_used_len, "(0x%03x)", _model2);
+    ss.print("(0x%03x)", _model2);
   }
-  size_t features_offset = strnlen(buf, sizeof(buf));
-#define ADD_FEATURE_IF_SUPPORTED(id, name, bit)                 \
-  do {                                                          \
-    if (VM_Version::supports_##name()) strcat(buf, ", " #name); \
-  } while(0);
-  CPU_FEATURE_FLAGS(ADD_FEATURE_IF_SUPPORTED)
-#undef ADD_FEATURE_IF_SUPPORTED
-
-  _cpu_info_string = os::strdup(buf);
+  ss.print(", ");
+  int features_offset = (int)ss.size();
+  insert_features_names(_features, ss);
 
-  _features_string = extract_features_string(_cpu_info_string,
-                                             strnlen(_cpu_info_string, sizeof(buf)),
-                                             features_offset);
+  _cpu_info_string = ss.as_string(true);
+  _features_string = _cpu_info_string + features_offset;
 }
 
 #if defined(LINUX)
 static bool check_info_file(const char* fpath,
                             const char* virt1, VirtualizationType vt1,
@@ -723,5 +724,53 @@
   desc_len = (int)strlen(_cpu_desc);
   snprintf(_cpu_desc + desc_len, CPU_DETAILED_DESC_BUF_SIZE - desc_len, " %s", _cpu_info_string);
 
   _initialized = true;
 }
+
+void VM_Version::insert_features_names(uint64_t features, stringStream& ss) {
+  int i = 0;
+  ss.join([&]() {
+    const char* str = nullptr;
+    while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
+      if (supports_feature((VM_Version::Feature_Flag)i)) {
+        str = _features_names[i];
+      }
+      i += 1;
+    }
+    return str;
+  }, ", ");
+}
+
+void VM_Version::get_cpu_features_name(void* features_buffer, stringStream& ss) {
+  uint64_t features = *(uint64_t*)features_buffer;
+  insert_features_names(features, ss);
+}
+
+void VM_Version::get_missing_features_name(void* features_buffer, stringStream& ss) {
+  uint64_t features_to_test = *(uint64_t*)features_buffer;
+  int i = 0;
+  ss.join([&]() {
+    const char* str = nullptr;
+    while ((i < MAX_CPU_FEATURES) && (str == nullptr)) {
+      Feature_Flag flag = (Feature_Flag)i;
+      if (supports_feature(features_to_test, flag) && !supports_feature(flag)) {
+        str = _features_names[i];
+      }
+      i += 1;
+    }
+    return str;
+  }, ", ");
+}
+
+int VM_Version::cpu_features_size() {
+  return sizeof(_features);
+}
+
+void VM_Version::store_cpu_features(void* buf) {
+  *(uint64_t*)buf = _features;
+}
+
+bool VM_Version::supports_features(void* features_buffer) {
+  uint64_t features_to_test = *(uint64_t*)features_buffer;
+  return (_features & features_to_test) == features_to_test;
+}
