diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
index 6871f6bd6a816..15c7160fdcdcd 100644
--- a/src/hotspot/os/aix/os_aix.cpp
+++ b/src/hotspot/os/aix/os_aix.cpp
@@ -2441,7 +2441,7 @@ void os::init(void) {
 
   // For now UseLargePages is just ignored.
   FLAG_SET_ERGO(UseLargePages, false);
-  _page_sizes[0] = 0;
+  _page_sizes.add(Aix::_page_size);
 
   // debug trace
   trcVerbose("os::vm_page_size %s", describe_pagesize(os::vm_page_size()));
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index 733e2b7e0e72d..c9aec705b9ffa 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -2093,7 +2093,7 @@ void os::init(void) {
   if (Bsd::page_size() == -1) {
     fatal("os_bsd.cpp: os::init: sysconf failed (%s)", os::strerror(errno));
   }
-  init_page_sizes((size_t) Bsd::page_size());
+  _page_sizes.add(Bsd::page_size());
 
   Bsd::initialize_system_info();
 
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index 15d1dff4d84c8..78ad6492c0594 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -3769,9 +3769,7 @@ size_t os::Linux::setup_large_page_size() {
 
   const size_t default_page_size = (size_t)Linux::page_size();
   if (_large_page_size > default_page_size) {
-    _page_sizes[0] = _large_page_size;
-    _page_sizes[1] = default_page_size;
-    _page_sizes[2] = 0;
+    _page_sizes.add(_large_page_size);
   }
 
   return _large_page_size;
@@ -4399,7 +4397,7 @@ void os::init(void) {
     fatal("os_linux.cpp: os::init: sysconf failed (%s)",
           os::strerror(errno));
   }
-  init_page_sizes((size_t) Linux::page_size());
+  _page_sizes.add(Linux::page_size());
 
   Linux::initialize_system_info();
 
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index 3342ecf781756..0c67dda4281f5 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -3118,12 +3118,9 @@ void os::large_page_init() {
   }
 
   _large_page_size = large_page_init_decide_size();
-
   const size_t default_page_size = (size_t) vm_page_size();
   if (_large_page_size > default_page_size) {
-    _page_sizes[0] = _large_page_size;
-    _page_sizes[1] = default_page_size;
-    _page_sizes[2] = 0;
+    _page_sizes.add(_large_page_size);
   }
 
   UseLargePages = _large_page_size != 0;
@@ -4166,7 +4163,7 @@ void os::init(void) {
 
   win32::initialize_system_info();
   win32::setmode_streams();
-  init_page_sizes((size_t) win32::vm_page_size());
+  _page_sizes.add(win32::vm_page_size());
 
   // This may be overridden later when argument processing is done.
   FLAG_SET_ERGO(UseLargePagesIndividualAllocation, false);
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index 4d702eb94699c..3e312ecb082a0 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -64,8 +64,10 @@
 #include "services/nmtCommon.hpp"
 #include "services/threadService.hpp"
 #include "utilities/align.hpp"
+#include "utilities/count_trailing_zeros.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/events.hpp"
+#include "utilities/powerOfTwo.hpp"
 
 # include <signal.h>
 # include <errno.h>
@@ -75,7 +77,7 @@ address           os::_polling_page       = NULL;
 volatile unsigned int os::_rand_seed      = 1234567;
 int               os::_processor_count    = 0;
 int               os::_initial_active_processor_count = 0;
-size_t            os::_page_sizes[os::page_sizes_max];
+os::PageSizes     os::_page_sizes;
 
 #ifndef PRODUCT
 julong os::num_mallocs = 0;         // # of calls to malloc/realloc
@@ -1390,8 +1392,8 @@ size_t os::page_size_for_region(size_t region_size, size_t min_pages, bool must_
   if (UseLargePages) {
     const size_t max_page_size = region_size / min_pages;
 
-    for (size_t i = 0; _page_sizes[i] != 0; ++i) {
-      const size_t page_size = _page_sizes[i];
+    for (size_t page_size = page_sizes().largest(); page_size != 0;
+         page_size = page_sizes().next_smaller(page_size)) {
       if (page_size <= max_page_size) {
         if (!must_be_aligned || is_aligned(region_size, page_size)) {
           return page_size;
@@ -1536,19 +1538,6 @@ const char* os::errno_name(int e) {
   return errno_to_string(e, true);
 }
 
-void os::trace_page_sizes(const char* str, const size_t* page_sizes, int count) {
-  LogTarget(Info, pagesize) log;
-  if (log.is_enabled()) {
-    LogStream out(log);
-
-    out.print("%s: ", str);
-    for (int i = 0; i < count; ++i) {
-      out.print(" " SIZE_FORMAT, page_sizes[i]);
-    }
-    out.cr();
-  }
-}
-
 #define trace_page_size_params(size) byte_size_in_exact_unit(size), exact_unit_for_byte_size(size)
 
 void os::trace_page_sizes(const char* str,
@@ -1857,3 +1846,73 @@ void os::naked_sleep(jlong millis) {
   }
   naked_short_sleep(millis);
 }
+
+
+////// Implementation of PageSizes
+
+void os::PageSizes::add(size_t page_size) {
+  assert(is_power_of_2(page_size), "page_size must be a power of 2: " SIZE_FORMAT_HEX, page_size);
+  _v |= page_size;
+}
+
+bool os::PageSizes::contains(size_t page_size) const {
+  assert(is_power_of_2(page_size), "page_size must be a power of 2: " SIZE_FORMAT_HEX, page_size);
+  return (_v & page_size) != 0;
+}
+
+size_t os::PageSizes::next_smaller(size_t page_size) const {
+  assert(is_power_of_2(page_size), "page_size must be a power of 2: " SIZE_FORMAT_HEX, page_size);
+  size_t v2 = _v & (page_size - 1);
+  if (v2 == 0) {
+    return 0;
+  }
+  return round_down_power_of_2(v2);
+}
+
+size_t os::PageSizes::next_larger(size_t page_size) const {
+  assert(is_power_of_2(page_size), "page_size must be a power of 2: " SIZE_FORMAT_HEX, page_size);
+  if (page_size == max_power_of_2<size_t>()) { // Shift by 32/64 would be UB
+    return 0;
+  }
+  // Remove current and smaller page sizes
+  size_t v2 = _v & ~(page_size + (page_size - 1));
+  if (v2 == 0) {
+    return 0;
+  }
+  return (size_t)1 << count_trailing_zeros(v2);
+}
+
+size_t os::PageSizes::largest() const {
+  const size_t max = max_power_of_2<size_t>();
+  if (contains(max)) {
+    return max;
+  }
+  return next_smaller(max);
+}
+
+size_t os::PageSizes::smallest() const {
+  // Strictly speaking the set should not contain sizes < os::vm_page_size().
+  // But this is not enforced.
+  return next_larger(1);
+}
+
+void os::PageSizes::print_on(outputStream* st) const {
+  bool first = true;
+  for (size_t sz = smallest(); sz != 0; sz = next_larger(sz)) {
+    if (first) {
+      first = false;
+    } else {
+      st->print_raw(", ");
+    }
+    if (sz < M) {
+      st->print(SIZE_FORMAT "k", sz / K);
+    } else if (sz < G) {
+      st->print(SIZE_FORMAT "M", sz / M);
+    } else {
+      st->print(SIZE_FORMAT "G", sz / G);
+    }
+  }
+  if (first) {
+    st->print("empty");
+  }
+}
diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
index e38e1a068b43c..3d6b5405c7b3c 100644
--- a/src/hotspot/share/runtime/os.hpp
+++ b/src/hotspot/share/runtime/os.hpp
@@ -100,19 +100,30 @@ class os: AllStatic {
 #endif
 
  public:
-  enum { page_sizes_max = 9 }; // Size of _page_sizes array (8 plus a sentinel)
+
+  // A simple value class holding a set of page sizes (similar to sigset_t)
+  class PageSizes {
+    size_t _v; // actually a bitmap.
+  public:
+    PageSizes() : _v(0) {}
+    void add(size_t pagesize);
+    bool contains(size_t pagesize) const;
+    // Given a page size, return the next smaller page size in this set, or 0.
+    size_t next_smaller(size_t pagesize) const;
+    // Given a page size, return the next larger page size in this set, or 0.
+    size_t next_larger(size_t pagesize) const;
+    // Returns the largest page size in this set, or 0 if set is empty.
+    size_t largest() const;
+    // Returns the smallest page size in this set, or 0 if set is empty.
+    size_t smallest() const;
+    // Prints one line of comma separated, human readable page sizes, "empty" if empty.
+    void print_on(outputStream* st) const;
+  };
 
  private:
   static OSThread*          _starting_thread;
   static address            _polling_page;
- public:
-  static size_t             _page_sizes[page_sizes_max];
-
- private:
-  static void init_page_sizes(size_t default_page_size) {
-    _page_sizes[0] = default_page_size;
-    _page_sizes[1] = 0; // sentinel
-  }
+  static PageSizes          _page_sizes;
 
   static char*  pd_reserve_memory(size_t bytes);
 
@@ -274,6 +285,10 @@ class os: AllStatic {
   // Return the default page size.
   static int    vm_page_size();
 
+  // The set of page sizes which the VM is allowed to use (may be a subset of
+  //  the page sizes actually available on the platform).
+  static const PageSizes& page_sizes() { return _page_sizes; }
+
   // Returns the page size to use for a region of memory.
   // region_size / min_pages will always be greater than or equal to the
   // returned value. The returned value will divide region_size.
@@ -285,10 +300,7 @@ class os: AllStatic {
   static size_t page_size_for_region_unaligned(size_t region_size, size_t min_pages);
 
   // Return the largest page size that can be used
-  static size_t max_page_size() {
-    // The _page_sizes array is sorted in descending order.
-    return _page_sizes[0];
-  }
+  static size_t max_page_size() { return page_sizes().largest(); }
 
   // Return a lower bound for page sizes. Also works before os::init completed.
   static size_t min_page_size() { return 4 * K; }
diff --git a/test/hotspot/gtest/runtime/test_os.cpp b/test/hotspot/gtest/runtime/test_os.cpp
index 4795a78407ec0..8319a07ff0cc5 100644
--- a/test/hotspot/gtest/runtime/test_os.cpp
+++ b/test/hotspot/gtest/runtime/test_os.cpp
@@ -85,29 +85,29 @@ TEST_VM(os, page_size_for_region_alignment) {
 TEST_VM(os, page_size_for_region_unaligned) {
   if (UseLargePages) {
     // Given exact page size, should return that page size.
-    for (size_t i = 0; os::_page_sizes[i] != 0; i++) {
-      size_t expected = os::_page_sizes[i];
-      size_t actual = os::page_size_for_region_unaligned(expected, 1);
-      ASSERT_EQ(expected, actual);
+    for (size_t s = os::page_sizes().largest(); s != 0; s = os::page_sizes().next_smaller(s)) {
+      size_t actual = os::page_size_for_region_unaligned(s, 1);
+      ASSERT_EQ(s, actual);
     }
 
     // Given slightly larger size than a page size, return the page size.
-    for (size_t i = 0; os::_page_sizes[i] != 0; i++) {
-      size_t expected = os::_page_sizes[i];
-      size_t actual = os::page_size_for_region_unaligned(expected + 17, 1);
-      ASSERT_EQ(expected, actual);
+    for (size_t s = os::page_sizes().largest(); s != 0; s = os::page_sizes().next_smaller(s)) {
+      size_t actual = os::page_size_for_region_unaligned(s + 17, 1);
+      ASSERT_EQ(s, actual);
     }
 
     // Given a slightly smaller size than a page size,
     // return the next smaller page size.
-    if (os::_page_sizes[1] > os::_page_sizes[0]) {
-      size_t expected = os::_page_sizes[0];
-      size_t actual = os::page_size_for_region_unaligned(os::_page_sizes[1] - 17, 1);
-      ASSERT_EQ(actual, expected);
+    for (size_t s = os::page_sizes().largest(); s != 0; s = os::page_sizes().next_smaller(s)) {
+      const size_t expected = os::page_sizes().next_smaller(s);
+      if (expected != 0) {
+        size_t actual = os::page_size_for_region_unaligned(expected - 17, 1);
+        ASSERT_EQ(actual, expected);
+      }
     }
 
     // Return small page size for values less than a small page.
-    size_t small_page = small_page_size();
+    size_t small_page = os::page_sizes().smallest();
     size_t actual = os::page_size_for_region_unaligned(small_page - 17, 1);
     ASSERT_EQ(small_page, actual);
   }
@@ -363,7 +363,7 @@ static address reserve_multiple(int num_stripes, size_t stripe_len) {
   // .. release it...
   EXPECT_TRUE(os::release_memory((char*)p, total_range_len));
   // ... re-reserve in the same spot multiple areas...
-  for (int stripe = 0; stripe < num_stripes; stripe ++) {
+  for (int stripe = 0; stripe < num_stripes; stripe++) {
     address q = p + (stripe * stripe_len);
     q = (address)os::attempt_reserve_memory_at((char*)q, stripe_len);
     EXPECT_NE(q, (address)NULL);
@@ -383,7 +383,7 @@ static address reserve_one_commit_multiple(int num_stripes, size_t stripe_len) {
   size_t total_range_len = num_stripes * stripe_len;
   address p = (address)os::reserve_memory(total_range_len);
   EXPECT_NE(p, (address)NULL);
-  for (int stripe = 0; stripe < num_stripes; stripe ++) {
+  for (int stripe = 0; stripe < num_stripes; stripe++) {
     address q = p + (stripe * stripe_len);
     if (stripe % 2 == 0) {
       EXPECT_TRUE(os::commit_memory((char*)q, stripe_len, false));
@@ -396,7 +396,7 @@ static address reserve_one_commit_multiple(int num_stripes, size_t stripe_len) {
 // Release a range allocated with reserve_multiple carefully, to not trip mapping
 // asserts on Windows in os::release_memory()
 static void carefully_release_multiple(address start, int num_stripes, size_t stripe_len) {
-  for (int stripe = 0; stripe < num_stripes; stripe ++) {
+  for (int stripe = 0; stripe < num_stripes; stripe++) {
     address q = start + (stripe * stripe_len);
     EXPECT_TRUE(os::release_memory((char*)q, stripe_len));
   }
@@ -574,7 +574,7 @@ TEST_VM(os, find_mapping_3) {
     address p = reserve_multiple(4, stripe_len);
     ASSERT_NE(p, (address)NULL);
     PRINT_MAPPINGS("E");
-    for (int stripe = 0; stripe < 4; stripe ++) {
+    for (int stripe = 0; stripe < 4; stripe++) {
       ASSERT_TRUE(os::win32::find_mapping(p + (stripe * stripe_len), &mapping_info));
       ASSERT_EQ(mapping_info.base, p + (stripe * stripe_len));
       ASSERT_EQ(mapping_info.regions, 1);
@@ -587,3 +587,78 @@ TEST_VM(os, find_mapping_3) {
   }
 }
 #endif // _WIN32
+
+TEST_VM(os, os_pagesizes) {
+  ASSERT_EQ(os::min_page_size(), 4 * K);
+  ASSERT_LE(os::min_page_size(), (size_t)os::vm_page_size());
+  // The vm_page_size should be the smallest in the set of allowed page sizes
+  // (contract says "default" page size but a lot of code actually assumes
+  //  this to be the smallest page size; notable, deliberate exception is
+  //  AIX which can have smaller page sizes but those are not part of the
+  //  page_sizes() set).
+  ASSERT_EQ(os::page_sizes().smallest(), (size_t)os::vm_page_size());
+  // The large page size, if it exists, shall be part of the set
+  if (UseLargePages) {
+    ASSERT_GT(os::large_page_size(), (size_t)os::vm_page_size());
+    ASSERT_TRUE(os::page_sizes().contains(os::large_page_size()));
+  }
+  os::page_sizes().print_on(tty);
+  tty->cr();
+}
+
+static const int min_page_size_log2 = exact_log2(os::min_page_size());
+static const int max_page_size_log2 = (int)BitsPerWord;
+
+TEST_VM(os, pagesizes_test_range) {
+  for (int bit = min_page_size_log2; bit < max_page_size_log2; bit++) {
+    for (int bit2 = min_page_size_log2; bit2 < max_page_size_log2; bit2++) {
+      const size_t s =  (size_t)1 << bit;
+      const size_t s2 = (size_t)1 << bit2;
+      os::PageSizes pss;
+      ASSERT_EQ((size_t)0, pss.smallest());
+      ASSERT_EQ((size_t)0, pss.largest());
+      // one size set
+      pss.add(s);
+      ASSERT_TRUE(pss.contains(s));
+      ASSERT_EQ(s, pss.smallest());
+      ASSERT_EQ(s, pss.largest());
+      ASSERT_EQ(pss.next_larger(s), (size_t)0);
+      ASSERT_EQ(pss.next_smaller(s), (size_t)0);
+      // two set
+      pss.add(s2);
+      ASSERT_TRUE(pss.contains(s2));
+      if (s2 < s) {
+        ASSERT_EQ(s2, pss.smallest());
+        ASSERT_EQ(s, pss.largest());
+        ASSERT_EQ(pss.next_larger(s2), (size_t)s);
+        ASSERT_EQ(pss.next_smaller(s2), (size_t)0);
+        ASSERT_EQ(pss.next_larger(s), (size_t)0);
+        ASSERT_EQ(pss.next_smaller(s), (size_t)s2);
+      } else if (s2 > s) {
+        ASSERT_EQ(s, pss.smallest());
+        ASSERT_EQ(s2, pss.largest());
+        ASSERT_EQ(pss.next_larger(s), (size_t)s2);
+        ASSERT_EQ(pss.next_smaller(s), (size_t)0);
+        ASSERT_EQ(pss.next_larger(s2), (size_t)0);
+        ASSERT_EQ(pss.next_smaller(s2), (size_t)s);
+      }
+      for (int bit3 = min_page_size_log2; bit3 < max_page_size_log2; bit3++) {
+        const size_t s3 = (size_t)1 << bit3;
+        ASSERT_EQ(s3 == s || s3 == s2, pss.contains(s3));
+      }
+    }
+  }
+}
+
+TEST_VM(os, pagesizes_test_print) {
+  os::PageSizes pss;
+  const size_t sizes[] = { 16 * K, 64 * K, 128 * K, 1 * M, 4 * M, 1 * G, 2 * G, 0 };
+  static const char* const expected = "16k, 64k, 128k, 1M, 4M, 1G, 2G";
+  for (int i = 0; sizes[i] != 0; i++) {
+    pss.add(sizes[i]);
+  }
+  char buffer[256];
+  stringStream ss(buffer, sizeof(buffer));
+  pss.print_on(&ss);
+  ASSERT_EQ(strcmp(expected, buffer), 0);
+}