diff a/src/hotspot/share/oops/compressedKlass.hpp b/src/hotspot/share/oops/compressedKlass.hpp
--- a/src/hotspot/share/oops/compressedKlass.hpp
+++ b/src/hotspot/share/oops/compressedKlass.hpp
@@ -32,23 +32,34 @@
 class Klass;
 
 // If compressed klass pointers then use narrowKlass.
 typedef juint  narrowKlass;
 
-const int LogKlassAlignmentInBytes = 3;
-const int KlassAlignmentInBytes    = 1 << LogKlassAlignmentInBytes;
-
-// Maximal size of compressed class space. Above this limit compression is not possible.
-// Also upper bound for placement of zero based class space. (Class space is further limited
-// to be < 3G, see arguments.cpp.)
-const  uint64_t KlassEncodingMetaspaceMax = (uint64_t(max_juint) + 1) << LogKlassAlignmentInBytes;
-
 // For UseCompressedClassPointers.
 class CompressedKlassPointers : public AllStatic {
   friend class VMStructs;
   friend class ArchiveBuilder;
 
+  // Tiny-class-pointer mode
+  static int _tiny_cp; // -1, 0=true, 1=false
+
+  // We use a different narrow Klass pointer geometry depending on
+  // whether we run in standard mode or in compact-object-header-mode (Lilliput):
+  // In Lilliput, we use smaller-than-32-bit class pointers ("tiny classpointer mode")
+
+  // Narrow klass pointer bits for an unshifted narrow Klass pointer.
+  static constexpr int narrow_klass_pointer_bits_legacy = 32;
+  static constexpr int narrow_klass_pointer_bits_tinycp = 22;
+
+  static int _narrow_klass_pointer_bits;
+
+  // The maximum shift we can use for standard mode and for TinyCP mode
+  static constexpr int max_shift_legacy = 3;
+  static constexpr int max_shift_tinycp = 10;
+
+  static int _max_shift;
+
   static address _base;
   static int _shift;
 
   // Together with base, this defines the address range within which Klass
   //  structures will be located: [base, base+range). While the maximal
@@ -61,19 +72,73 @@
   static char* reserve_address_space_X(uintptr_t from, uintptr_t to, size_t size, size_t alignment, bool aslr);
   static char* reserve_address_space_for_unscaled_encoding(size_t size, bool aslr);
   static char* reserve_address_space_for_zerobased_encoding(size_t size, bool aslr);
   static char* reserve_address_space_for_16bit_move(size_t size, bool aslr);
 
-  DEBUG_ONLY(static void assert_is_valid_encoding(address addr, size_t len, address base, int shift);)
+  // Returns the highest address expressable with an unshifted narrow Klass pointer
+  inline static uintptr_t highest_unscaled_address();
+
+  static bool pd_initialize(address addr, size_t len);
+
+#ifdef ASSERT
+  // For sanity checks: Klass range
+  static address _klass_range_start;
+  static address _klass_range_end;
+  // For sanity checks: lowest, highest valid narrow klass ids != null
+  static narrowKlass _lowest_valid_narrow_klass_id;
+  static narrowKlass _highest_valid_narrow_klass_id;
+  static void calc_lowest_highest_narrow_klass_id();
+  static void sanity_check_after_initialization();
+#endif // ASSERT
+
+  template <typename T>
+  static inline void check_init(T var) {
+    assert(var != (T)-1, "Not yet initialized");
+  }
 
   static inline Klass* decode_not_null_without_asserts(narrowKlass v, address base, int shift);
   static inline Klass* decode_not_null(narrowKlass v, address base, int shift);
 
   static inline narrowKlass encode_not_null(Klass* v, address base, int shift);
 
 public:
 
+  // Initialization sequence:
+  // 1) Parse arguments. The following arguments take a role:
+  //      - UseCompressedClassPointers
+  //      - UseCompactObjectHeaders
+  //      - Xshare on off dump
+  //      - CompressedClassSpaceSize
+  // 2) call pre_initialize(): depending on UseCompactObjectHeaders, defines the limits of narrow Klass pointer
+  //    geometry (how many bits, the max. possible shift)
+  // 3) .. from here on, narrow_klass_pointer_bits() and max_shift() can be used
+  // 4) call reserve_address_space_for_compressed_classes() either from CDS initialization or, if CDS is off,
+  //    from metaspace initialization. Reserves space for class space + CDS, attempts to reserve such that
+  //    we later can use a "good" encoding scheme. Reservation is highly CPU-specific.
+  // 5) Initialize the narrow Klass encoding scheme by determining encoding base and shift:
+  //   5a) if CDS=on: Calls initialize_for_given_encoding() with the reservation base from step (4) and the
+  //       CDS-intrinsic setting for shift; here, we don't have any freedom to deviate from the base.
+  //   5b) if CDS=off: Calls initialize() - here, we have more freedom and, if we want, can choose an encoding
+  //       base that differs from the reservation base from step (4). That allows us, e.g., to later use
+  //       zero-based encoding.
+  // 6) ... from now on, we can use base() and shift().
+
+  // Called right after argument parsing; defines narrow klass pointer geometry limits
+  static void pre_initialize();
+
+  static bool tiny_classpointer_mode()   { check_init(_tiny_cp); return (_tiny_cp == 1); }
+
+  // The number of bits a narrow Klass pointer has;
+  static int narrow_klass_pointer_bits() { check_init(_narrow_klass_pointer_bits); return _narrow_klass_pointer_bits; }
+
+  // The maximum possible shift; the actual shift employed later can be smaller (see initialize())
+  static int max_shift()                 { check_init(_max_shift); return _max_shift; }
+
+  // Returns the maximum encoding range that can be covered with the currently
+  // choosen nKlassID geometry (nKlass bit size, max shift)
+  static size_t max_encoding_range_size();
+
   // Reserve a range of memory that is to contain Klass strucutures which are referenced by narrow Klass IDs.
   // If optimize_for_zero_base is true, the implementation will attempt to reserve optimized for zero-based encoding.
   static char* reserve_address_space_for_compressed_classes(size_t size, bool aslr, bool optimize_for_zero_base);
 
   // Given a klass range [addr, addr+len) and a given encoding scheme, assert that this scheme covers the range, then
@@ -89,13 +154,20 @@
   //  structures outside this range).
   static void initialize(address addr, size_t len);
 
   static void     print_mode(outputStream* st);
 
-  static address  base()               { return  _base; }
-  static size_t   range()              { return  _range; }
-  static int      shift()              { return  _shift; }
+  // Can only be used after initialization
+  static address  base()             { check_init(_base); return  _base; }
+  static size_t   range()            { check_init(_range); return  _range; }
+  static int      shift()            { check_init(_shift); return  _shift; }
+
+  // Returns the alignment a Klass* is guaranteed to have.
+  // Note: *Not* the same as 1 << shift ! Klass are always guaranteed to be at least 64-bit aligned,
+  // so this will return 8 even if shift is 0.
+  static int klass_alignment_in_bytes() { return nth_bit(MAX2(3, _shift)); }
+  static int klass_alignment_in_words() { return klass_alignment_in_bytes() / BytesPerWord; }
 
   static bool is_null(Klass* v)      { return v == nullptr; }
   static bool is_null(narrowKlass v) { return v == 0; }
 
   // Versions without asserts
@@ -103,10 +175,21 @@
   static inline Klass* decode_without_asserts(narrowKlass v);
 
   static inline Klass* decode_not_null(narrowKlass v);
   static inline Klass* decode(narrowKlass v);
 
+  static inline narrowKlass encode_not_null_without_asserts(Klass* k, address narrow_base, int shift);
   static inline narrowKlass encode_not_null(Klass* v);
   static inline narrowKlass encode(Klass* v);
+
+#ifdef ASSERT
+  // Given a Klass* k and an encoding (base, shift), check that k can be encoded
+  inline static void check_valid_klass(const Klass* k, address base, int shift);
+  // Given a Klass* k, check that k can be encoded with the current encoding
+  inline static void check_valid_klass(const Klass* k);
+  // Given a narrow Klass ID, check that it is valid according to current encoding
+  inline static void check_valid_narrow_klass_id(narrowKlass nk);
+#endif
+
 };
 
 #endif // SHARE_OOPS_COMPRESSEDKLASS_HPP
