8241071: Generation of classes.jsa with -Xshare:dump is not deterministic

Reviewed-by: dholmes, stuefe
diff --git a/make/hotspot/symbols/symbols-unix b/make/hotspot/symbols/symbols-unix
index 50a16fa..5c17fd8 100644
--- a/make/hotspot/symbols/symbols-unix
+++ b/make/hotspot/symbols/symbols-unix
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2016, 2020, Oracle and/or its affiliates. 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
@@ -122,6 +122,7 @@
 JVM_GetPrimitiveArrayElement
 JVM_GetProperties
 JVM_GetProtectionDomain
+JVM_GetRandomSeedForCDSDump
 JVM_GetRecordComponents
 JVM_GetSimpleBinaryName
 JVM_GetStackAccessControlContext
diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp
index c253065..5d254da 100644
--- a/src/hotspot/share/classfile/symbolTable.cpp
+++ b/src/hotspot/share/classfile/symbolTable.cpp
@@ -176,6 +176,11 @@
 }
 
 void SymbolTable::delete_symbol(Symbol* sym) {
+  if (Arguments::is_dumping_archive()) {
+    // Do not delete symbols as we may be in the middle of preparing the
+    // symbols for dumping.
+    return;
+  }
   if (sym->is_permanent()) {
     MutexLocker ml(SymbolArena_lock, Mutex::_no_safepoint_check_flag); // Protect arena
     // Deleting permanent symbol should not occur very often (insert race condition),
@@ -221,12 +226,18 @@
 
   Symbol* sym;
   if (Arguments::is_dumping_archive()) {
+    // Need to make all symbols permanent -- or else some symbols may be GC'ed
+    // during the archive dumping code that's executed outside of a safepoint.
     c_heap = false;
   }
   if (c_heap) {
     // refcount starts as 1
     sym = new (len) Symbol((const u1*)name, len, 1);
     assert(sym != NULL, "new should call vm_exit_out_of_memory if C_HEAP is exhausted");
+  } else if (DumpSharedSpaces) {
+    // See comments inside Symbol::operator new(size_t, int)
+    sym = new (len) Symbol((const u1*)name, len, PERM_REFCOUNT);
+    assert(sym != NULL, "new should call vm_exit_out_of_memory if failed to allocate symbol during DumpSharedSpaces");
   } else {
     // Allocate to global arena
     MutexLocker ml(SymbolArena_lock, Mutex::_no_safepoint_check_flag); // Protect arena
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
index 3c9cae9..ad1d7f7 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -174,10 +174,22 @@
   }
 };
 
+inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) {
+  if (DumpSharedSpaces) {
+    // Deterministic archive contents
+    uintx delta = k->name() - MetaspaceShared::symbol_rs_base();
+    return primitive_hash<uintx>(delta);
+  } else {
+    // Deterministic archive is not possible because classes can be loaded
+    // in multiple threads.
+    return primitive_hash<InstanceKlass*>(k);
+  }
+}
+
 class DumpTimeSharedClassTable: public ResourceHashtable<
   InstanceKlass*,
   DumpTimeSharedClassInfo,
-  primitive_hash<InstanceKlass*>,
+  &DumpTimeSharedClassTable_hash,
   primitive_equals<InstanceKlass*>,
   15889, // prime number
   ResourceObj::C_HEAP>
diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
index 6db1b26..331924f 100644
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -176,6 +176,9 @@
 JNIEXPORT void JNICALL
 JVM_InitializeFromArchive(JNIEnv* env, jclass cls);
 
+JNIEXPORT jlong JNICALL
+JVM_GetRandomSeedForCDSDump();
+
 /*
  * java.lang.Throwable
  */
diff --git a/src/hotspot/share/memory/dynamicArchive.cpp b/src/hotspot/share/memory/dynamicArchive.cpp
index 41cd492..ed6143b 100644
--- a/src/hotspot/share/memory/dynamicArchive.cpp
+++ b/src/hotspot/share/memory/dynamicArchive.cpp
@@ -503,14 +503,13 @@
   void write_archive(char* serialized_data);
 
   void init_first_dump_space(address reserved_bottom) {
-    address first_space_base = reserved_bottom;
     DumpRegion* mc_space = MetaspaceShared::misc_code_dump_space();
     DumpRegion* rw_space = MetaspaceShared::read_write_dump_space();
 
     // Use the same MC->RW->RO ordering as in the base archive.
-    MetaspaceShared::init_shared_dump_space(mc_space, first_space_base);
+    MetaspaceShared::init_shared_dump_space(mc_space);
     _current_dump_space = mc_space;
-    _last_verified_top = first_space_base;
+    _last_verified_top = reserved_bottom;
     _num_dump_regions_used = 1;
   }
 
diff --git a/src/hotspot/share/memory/filemap.cpp b/src/hotspot/share/memory/filemap.cpp
index 789c6ac..b3d74ed 100644
--- a/src/hotspot/share/memory/filemap.cpp
+++ b/src/hotspot/share/memory/filemap.cpp
@@ -1177,6 +1177,10 @@
   _mapped_base = NULL;
 }
 
+static const char* region_names[] = {
+  "mc", "rw", "ro", "bm", "ca0", "ca1", "oa0", "oa1"
+};
+
 void FileMapInfo::write_region(int region, char* base, size_t size,
                                bool read_only, bool allow_exec) {
   Arguments::assert_is_dumping_archive();
@@ -1184,6 +1188,9 @@
   FileMapRegion* si = space_at(region);
   char* target_base;
 
+  const int num_regions = sizeof(region_names)/sizeof(region_names[0]);
+  assert(0 <= region && region < num_regions, "sanity");
+
   if (region == MetaspaceShared::bm) {
     target_base = NULL; // always NULL for bm region.
   } else {
@@ -1197,11 +1204,13 @@
 
   si->set_file_offset(_file_offset);
   char* requested_base = (target_base == NULL) ? NULL : target_base + MetaspaceShared::final_delta();
-  log_debug(cds)("Shared file region  %d: " SIZE_FORMAT_HEX_W(08)
-                 " bytes, addr " INTPTR_FORMAT " file offset " SIZE_FORMAT_HEX_W(08),
-                 region, size, p2i(requested_base), _file_offset);
-
   int crc = ClassLoader::crc32(0, base, (jint)size);
+  if (size > 0) {
+    log_debug(cds)("Shared file region (%-3s)  %d: " SIZE_FORMAT_W(8)
+                   " bytes, addr " INTPTR_FORMAT " file offset " SIZE_FORMAT_HEX_W(08)
+                   " crc 0x%08x",
+                   region_names[region], region, size, p2i(requested_base), _file_offset, crc);
+  }
   si->init(region, target_base, size, read_only, allow_exec, crc);
 
   if (base != NULL) {
@@ -1246,8 +1255,6 @@
     write_oopmaps(open_oopmaps, curr_offset, buffer);
   }
 
-  log_debug(cds)("ptrmap = " INTPTR_FORMAT " (" SIZE_FORMAT " bytes)",
-                 p2i(buffer), size_in_bytes);
   write_region(MetaspaceShared::bm, (char*)buffer, size_in_bytes, /*read_only=*/true, /*allow_exec=*/false);
 }
 
@@ -1297,23 +1304,20 @@
   }
 
   size_t total_size = 0;
-  for (int i = first_region_id, arr_idx = 0;
-           i < first_region_id + max_num_regions;
-           i++, arr_idx++) {
+  for (int i = 0; i < max_num_regions; i++) {
     char* start = NULL;
     size_t size = 0;
-    if (arr_idx < arr_len) {
-      start = (char*)heap_mem->at(arr_idx).start();
-      size = heap_mem->at(arr_idx).byte_size();
+    if (i < arr_len) {
+      start = (char*)heap_mem->at(i).start();
+      size = heap_mem->at(i).byte_size();
       total_size += size;
     }
 
-    log_debug(cds)("Archive heap region %d: " INTPTR_FORMAT " - " INTPTR_FORMAT " = " SIZE_FORMAT_W(8) " bytes",
-                   i, p2i(start), p2i(start + size), size);
-    write_region(i, start, size, false, false);
+    int region_idx = i + first_region_id;
+    write_region(region_idx, start, size, false, false);
     if (size > 0) {
-      space_at(i)->init_oopmap(oopmaps->at(arr_idx)._offset,
-                               oopmaps->at(arr_idx)._oopmap_size_in_bits);
+      space_at(region_idx)->init_oopmap(oopmaps->at(i)._offset,
+                                        oopmaps->at(i)._oopmap_size_in_bits);
     }
   }
   return total_size;
diff --git a/src/hotspot/share/memory/heapShared.cpp b/src/hotspot/share/memory/heapShared.cpp
index 22ef0b3..7eaaf74 100644
--- a/src/hotspot/share/memory/heapShared.cpp
+++ b/src/hotspot/share/memory/heapShared.cpp
@@ -142,6 +142,10 @@
   if (archived_oop != NULL) {
     Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(obj), cast_from_oop<HeapWord*>(archived_oop), len);
     MetaspaceShared::relocate_klass_ptr(archived_oop);
+    // Clear age -- it might have been set if a GC happened during -Xshare:dump
+    markWord mark = archived_oop->mark_raw();
+    mark = mark.set_age(0);
+    archived_oop->set_mark_raw(mark);
     ArchivedObjectCache* cache = archived_object_cache();
     cache->put(obj, archived_oop);
     log_debug(cds, heap)("Archived heap object " PTR_FORMAT " ==> " PTR_FORMAT,
diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp
index cbea64f..5bc8df5 100644
--- a/src/hotspot/share/memory/metaspaceShared.cpp
+++ b/src/hotspot/share/memory/metaspaceShared.cpp
@@ -77,6 +77,8 @@
 
 ReservedSpace MetaspaceShared::_shared_rs;
 VirtualSpace MetaspaceShared::_shared_vs;
+ReservedSpace MetaspaceShared::_symbol_rs;
+VirtualSpace MetaspaceShared::_symbol_vs;
 MetaspaceSharedStats MetaspaceShared::_stats;
 bool MetaspaceShared::_has_error_classes;
 bool MetaspaceShared::_archive_loading_failed = false;
@@ -120,21 +122,24 @@
     MetaspaceShared::report_out_of_space(_name, newtop - _top);
     ShouldNotReachHere();
   }
-  uintx delta;
-  if (DynamicDumpSharedSpaces) {
-    delta = DynamicArchive::object_delta_uintx(newtop);
-  } else {
-    delta = MetaspaceShared::object_delta_uintx(newtop);
-  }
-  if (delta > MAX_SHARED_DELTA) {
-    // This is just a sanity check and should not appear in any real world usage. This
-    // happens only if you allocate more than 2GB of shared objects and would require
-    // millions of shared classes.
-    vm_exit_during_initialization("Out of memory in the CDS archive",
-                                  "Please reduce the number of shared classes.");
+
+  if (_rs == MetaspaceShared::shared_rs()) {
+    uintx delta;
+    if (DynamicDumpSharedSpaces) {
+      delta = DynamicArchive::object_delta_uintx(newtop);
+    } else {
+      delta = MetaspaceShared::object_delta_uintx(newtop);
+    }
+    if (delta > MAX_SHARED_DELTA) {
+      // This is just a sanity check and should not appear in any real world usage. This
+      // happens only if you allocate more than 2GB of shared objects and would require
+      // millions of shared classes.
+      vm_exit_during_initialization("Out of memory in the CDS archive",
+                                    "Please reduce the number of shared classes.");
+    }
   }
 
-  MetaspaceShared::commit_shared_space_to(newtop);
+  MetaspaceShared::commit_to(_rs, _vs, newtop);
   _top = newtop;
   return _top;
 }
@@ -172,26 +177,35 @@
   }
 }
 
+void DumpRegion::init(ReservedSpace* rs, VirtualSpace* vs) {
+  _rs = rs;
+  _vs = vs;
+  // Start with 0 committed bytes. The memory will be committed as needed by
+  // MetaspaceShared::commit_to().
+  if (!_vs->initialize(*_rs, 0)) {
+    fatal("Unable to allocate memory for shared space");
+  }
+  _base = _top = _rs->base();
+  _end = _rs->end();
+}
+
 void DumpRegion::pack(DumpRegion* next) {
   assert(!is_packed(), "sanity");
   _end = (char*)align_up(_top, Metaspace::reserve_alignment());
   _is_packed = true;
   if (next != NULL) {
+    next->_rs = _rs;
+    next->_vs = _vs;
     next->_base = next->_top = this->_end;
-    next->_end = MetaspaceShared::shared_rs()->end();
+    next->_end = _rs->end();
   }
 }
 
-static DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw");
+static DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw"), _symbol_region("symbols");
 static size_t _total_closed_archive_region_size = 0, _total_open_archive_region_size = 0;
 
-void MetaspaceShared::init_shared_dump_space(DumpRegion* first_space, address first_space_bottom) {
-  // Start with 0 committed bytes. The memory will be committed as needed by
-  // MetaspaceShared::commit_shared_space_to().
-  if (!_shared_vs.initialize(_shared_rs, 0)) {
-    fatal("Unable to allocate memory for shared space");
-  }
-  first_space->init(&_shared_rs, (char*)first_space_bottom);
+void MetaspaceShared::init_shared_dump_space(DumpRegion* first_space) {
+  first_space->init(&_shared_rs, &_shared_vs);
 }
 
 DumpRegion* MetaspaceShared::misc_code_dump_space() {
@@ -211,6 +225,10 @@
   current->pack(next);
 }
 
+char* MetaspaceShared::symbol_space_alloc(size_t num_bytes) {
+  return _symbol_region.allocate(num_bytes);
+}
+
 char* MetaspaceShared::misc_code_space_alloc(size_t num_bytes) {
   return _mc_region.allocate(num_bytes);
 }
@@ -320,6 +338,14 @@
   SharedBaseAddress = (size_t)_shared_rs.base();
   log_info(cds)("Allocated shared space: " SIZE_FORMAT " bytes at " PTR_FORMAT,
                 _shared_rs.size(), p2i(_shared_rs.base()));
+
+  size_t symbol_rs_size = LP64_ONLY(3 * G) NOT_LP64(128 * M);
+  _symbol_rs = ReservedSpace(symbol_rs_size);
+  if (!_symbol_rs.is_reserved()) {
+    vm_exit_during_initialization("Unable to reserve memory for symbols",
+                                  err_msg(SIZE_FORMAT " bytes.", symbol_rs_size));
+  }
+  _symbol_region.init(&_symbol_rs, &_symbol_vs);
 }
 
 // Called by universe_post_init()
@@ -397,33 +423,37 @@
   }
 }
 
-void MetaspaceShared::commit_shared_space_to(char* newtop) {
+void MetaspaceShared::commit_to(ReservedSpace* rs, VirtualSpace* vs, char* newtop) {
   Arguments::assert_is_dumping_archive();
-  char* base = _shared_rs.base();
+  char* base = rs->base();
   size_t need_committed_size = newtop - base;
-  size_t has_committed_size = _shared_vs.committed_size();
+  size_t has_committed_size = vs->committed_size();
   if (need_committed_size < has_committed_size) {
     return;
   }
 
   size_t min_bytes = need_committed_size - has_committed_size;
   size_t preferred_bytes = 1 * M;
-  size_t uncommitted = _shared_vs.reserved_size() - has_committed_size;
+  size_t uncommitted = vs->reserved_size() - has_committed_size;
 
   size_t commit =MAX2(min_bytes, preferred_bytes);
   commit = MIN2(commit, uncommitted);
   assert(commit <= uncommitted, "sanity");
 
-  bool result = _shared_vs.expand_by(commit, false);
-  ArchivePtrMarker::expand_ptr_end((address*)_shared_vs.high());
+  bool result = vs->expand_by(commit, false);
+  if (rs == &_shared_rs) {
+    ArchivePtrMarker::expand_ptr_end((address*)vs->high());
+  }
 
   if (!result) {
     vm_exit_during_initialization(err_msg("Failed to expand shared space to " SIZE_FORMAT " bytes",
                                           need_committed_size));
   }
 
-  log_debug(cds)("Expanding shared spaces by " SIZE_FORMAT_W(7) " bytes [total " SIZE_FORMAT_W(9)  " bytes ending at %p]",
-                 commit, _shared_vs.actual_committed_size(), _shared_vs.high());
+  assert(rs == &_shared_rs || rs == &_symbol_rs, "must be");
+  const char* which = (rs == &_shared_rs) ? "shared" : "symbol";
+  log_debug(cds)("Expanding %s spaces by " SIZE_FORMAT_W(7) " bytes [total " SIZE_FORMAT_W(9)  " bytes ending at %p]",
+                 which, commit, vs->actual_committed_size(), vs->high());
 }
 
 void MetaspaceShared::initialize_ptr_marker(CHeapBitMap* ptrmap) {
@@ -506,6 +536,10 @@
 // is run at a safepoint just before exit, this is the entire set of classes.
 static GrowableArray<Klass*>* _global_klass_objects;
 
+static int global_klass_compare(Klass** a, Klass **b) {
+  return a[0]->name()->fast_compare(b[0]->name());
+}
+
 GrowableArray<Klass*>* MetaspaceShared::collected_klasses() {
   return _global_klass_objects;
 }
@@ -1331,7 +1365,14 @@
       RefRelocator ext_reloc;
       iterate_roots(&ext_reloc);
     }
-
+    {
+      log_info(cds)("Fixing symbol identity hash ... ");
+      os::init_random(0x12345678);
+      GrowableArray<Symbol*>* symbols = _ssc->get_sorted_symbols();
+      for (int i=0; i<symbols->length(); i++) {
+        symbols->at(i)->update_identity_hash();
+      }
+    }
 #ifdef ASSERT
     {
       log_info(cds)("Verifying external roots ... ");
@@ -1364,6 +1405,21 @@
   }
 
   static void iterate_roots(MetaspaceClosure* it) {
+    // To ensure deterministic contents in the archive, we just need to ensure that
+    // we iterate the MetsapceObjs in a deterministic order. It doesn't matter where
+    // the MetsapceObjs are located originally, as they are copied sequentially into
+    // the archive during the iteration.
+    //
+    // The only issue here is that the symbol table and the system directories may be
+    // randomly ordered, so we copy the symbols and klasses into two arrays and sort
+    // them deterministically.
+    //
+    // During -Xshare:dump, the order of Symbol creation is strictly determined by
+    // the SharedClassListFile (class loading is done in a single thread and the JIT
+    // is disabled). Also, Symbols are allocated in monotonically increasing addresses
+    // (see Symbol::operator new(size_t, int)). So if we iterate the Symbols by
+    // ascending address order, we ensure that all Symbols are copied into deterministic
+    // locations in the archive.
     GrowableArray<Symbol*>* symbols = _ssc->get_sorted_symbols();
     for (int i=0; i<symbols->length(); i++) {
       it->push(symbols->adr_at(i));
@@ -1525,6 +1581,7 @@
   _global_klass_objects = new GrowableArray<Klass*>(1000);
   CollectClassesClosure collect_classes;
   ClassLoaderDataGraph::loaded_classes_do(&collect_classes);
+  _global_klass_objects->sort(global_klass_compare);
 
   print_class_stats();
 
@@ -1558,8 +1615,10 @@
   _ro_region.pack();
 
   // The vtable clones contain addresses of the current process.
-  // We don't want to write these addresses into the archive.
+  // We don't want to write these addresses into the archive. Same for i2i buffer.
   MetaspaceShared::zero_cpp_vtable_clones_for_writing();
+  memset(MetaspaceShared::i2i_entry_code_buffers(), 0,
+         MetaspaceShared::i2i_entry_code_buffers_size());
 
   // relocate the data so that it can be mapped to Arguments::default_SharedBaseAddress()
   // without runtime relocation.
@@ -1631,7 +1690,7 @@
   _mc_region.print(total_reserved);
   _rw_region.print(total_reserved);
   _ro_region.print(total_reserved);
-  print_bitmap_region_stats(bitmap_reserved, total_reserved);
+  print_bitmap_region_stats(bitmap_used, total_reserved);
   print_heap_region_stats(_closed_archive_heap_regions, "ca", total_reserved);
   print_heap_region_stats(_open_archive_heap_regions, "oa", total_reserved);
 
@@ -1640,8 +1699,8 @@
 }
 
 void VM_PopulateDumpSharedSpace::print_bitmap_region_stats(size_t size, size_t total_size) {
-  log_debug(cds)("bm  space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [100.0%% used] at " INTPTR_FORMAT,
-                 size, size/double(total_size)*100.0, size, p2i(NULL));
+  log_debug(cds)("bm  space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [100.0%% used]",
+                 size, size/double(total_size)*100.0, size);
 }
 
 void VM_PopulateDumpSharedSpace::print_heap_region_stats(GrowableArray<MemRegion> *heap_mem,
diff --git a/src/hotspot/share/memory/metaspaceShared.hpp b/src/hotspot/share/memory/metaspaceShared.hpp
index 3de86c5..4a4ee70 100644
--- a/src/hotspot/share/memory/metaspaceShared.hpp
+++ b/src/hotspot/share/memory/metaspaceShared.hpp
@@ -63,6 +63,8 @@
   char* _top;
   char* _end;
   bool _is_packed;
+  ReservedSpace* _rs;
+  VirtualSpace* _vs;
 
 public:
   DumpRegion(const char* name) : _name(name), _base(NULL), _top(NULL), _end(NULL), _is_packed(false) {}
@@ -85,19 +87,7 @@
   void print(size_t total_bytes) const;
   void print_out_of_space_msg(const char* failing_region, size_t needed_bytes);
 
-  void init(const ReservedSpace* rs, char* base) {
-    if (base == NULL) {
-      base = rs->base();
-    }
-    assert(rs->contains(base), "must be");
-    _base = _top = base;
-    _end = rs->end();
-  }
-  void init(char* b, char* t, char* e) {
-    _base = b;
-    _top = t;
-    _end = e;
-  }
+  void init(ReservedSpace* rs, VirtualSpace* vs);
 
   void pack(DumpRegion* next = NULL);
 
@@ -178,6 +168,8 @@
   // CDS support
   static ReservedSpace _shared_rs;
   static VirtualSpace _shared_vs;
+  static ReservedSpace _symbol_rs;
+  static VirtualSpace _symbol_vs;
   static int _max_alignment;
   static MetaspaceSharedStats _stats;
   static bool _has_error_classes;
@@ -222,11 +214,15 @@
     NOT_CDS(return NULL);
   }
 
+  static Symbol* symbol_rs_base() {
+    return (Symbol*)_symbol_rs.base();
+  }
+
   static void set_shared_rs(ReservedSpace rs) {
     CDS_ONLY(_shared_rs = rs);
   }
 
-  static void commit_shared_space_to(char* newtop) NOT_CDS_RETURN;
+  static void commit_to(ReservedSpace* rs, VirtualSpace* vs, char* newtop) NOT_CDS_RETURN;
   static void initialize_dumptime_shared_and_meta_spaces() NOT_CDS_RETURN;
   static void initialize_runtime_shared_and_meta_spaces() NOT_CDS_RETURN;
   static void post_initialize(TRAPS) NOT_CDS_RETURN;
@@ -302,7 +298,7 @@
 #if INCLUDE_CDS
   static ReservedSpace reserve_shared_space(size_t size, char* requested_address = NULL);
   static size_t reserved_space_alignment();
-  static void init_shared_dump_space(DumpRegion* first_space, address first_space_bottom = NULL);
+  static void init_shared_dump_space(DumpRegion* first_space);
   static DumpRegion* misc_code_dump_space();
   static DumpRegion* read_write_dump_space();
   static DumpRegion* read_only_dump_space();
@@ -312,7 +308,10 @@
   static void rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread* thread, InstanceKlass* ik);
 #endif
 
-  // Allocate a block of memory from the "mc", "ro", or "rw" regions.
+  // Allocate a block of memory from the temporary "symbol" region.
+  static char* symbol_space_alloc(size_t num_bytes);
+
+  // Allocate a block of memory from the "mc" or "ro" regions.
   static char* misc_code_space_alloc(size_t num_bytes);
   static char* read_only_space_alloc(size_t num_bytes);
 
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index 6fabc30..a6b0b87 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -505,10 +505,6 @@
   assert(is_instance_klass(), "is layout incorrect?");
   assert(size_helper() == parser.layout_size(), "incorrect size_helper?");
 
-  if (Arguments::is_dumping_archive()) {
-    SystemDictionaryShared::init_dumptime_info(this);
-  }
-
   // Set biased locking bit for all instances of this class; it will be
   // cleared if revocation occurs too often for this type
   if (UseBiasedLocking && BiasedLocking::enabled()) {
diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp
index 5f67159..8d093a2 100644
--- a/src/hotspot/share/oops/klass.cpp
+++ b/src/hotspot/share/oops/klass.cpp
@@ -29,6 +29,7 @@
 #include "classfile/javaClasses.hpp"
 #include "classfile/moduleEntry.hpp"
 #include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "gc/shared/collectedHeap.inline.hpp"
 #include "logging/log.hpp"
@@ -79,6 +80,10 @@
 void Klass::set_name(Symbol* n) {
   _name = n;
   if (_name != NULL) _name->increment_refcount();
+
+  if (Arguments::is_dumping_archive() && is_instance_klass()) {
+    SystemDictionaryShared::init_dumptime_info(InstanceKlass::cast(this));
+  }
 }
 
 bool Klass::is_subclass_of(const Klass* k) const {
diff --git a/src/hotspot/share/oops/symbol.cpp b/src/hotspot/share/oops/symbol.cpp
index 356d9bd..4b13cb1 100644
--- a/src/hotspot/share/oops/symbol.cpp
+++ b/src/hotspot/share/oops/symbol.cpp
@@ -30,6 +30,7 @@
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
 #include "memory/allocation.inline.hpp"
+#include "memory/metaspaceShared.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "oops/symbol.hpp"
@@ -56,6 +57,20 @@
 }
 
 void* Symbol::operator new(size_t sz, int len) throw() {
+ if (DumpSharedSpaces) {
+    // To get deterministic output from -Xshare:dump, we ensure that Symbols are allocated in
+    // increasing addresses. When the symbols are copied into the archive, we preserve their
+    // relative address order (see SortedSymbolClosure in metaspaceShared.cpp)
+    //
+    // We cannot use arena because arena chunks are allocated by the OS. As a result, for example,
+    // the archived symbol of "java/lang/Object" may sometimes be lower than "java/lang/String", and
+    // sometimes be higher. This would cause non-deterministic contents in the archive.
+   DEBUG_ONLY(static void* last = 0);
+   void* p = (void*)MetaspaceShared::symbol_space_alloc(size(len)*wordSize);
+   assert(p > last, "must increase monotonically");
+   DEBUG_ONLY(last = p);
+   return p;
+ }
   int alloc_size = size(len)*wordSize;
   address res = (address) AllocateHeap(alloc_size, mtSymbol);
   return res;
@@ -72,11 +87,21 @@
   FreeHeap(p);
 }
 
+#if INCLUDE_CDS
+void Symbol::update_identity_hash() {
+  // This is called at a safepoint during dumping of a static CDS archive. The caller should have
+  // called os::init_random() with a deterministic seed and then iterate all archived Symbols in
+  // a deterministic order.
+  assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
+  _hash_and_refcount =  pack_hash_and_refcount((short)os::random(), PERM_REFCOUNT);
+}
+
 void Symbol::set_permanent() {
   // This is called at a safepoint during dumping of a dynamic CDS archive.
   assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
   _hash_and_refcount =  pack_hash_and_refcount(extract_hash(_hash_and_refcount), PERM_REFCOUNT);
 }
+#endif
 
 // ------------------------------------------------------------------
 // Symbol::index_of
diff --git a/src/hotspot/share/oops/symbol.hpp b/src/hotspot/share/oops/symbol.hpp
index 789214f..18bc0ae 100644
--- a/src/hotspot/share/oops/symbol.hpp
+++ b/src/hotspot/share/oops/symbol.hpp
@@ -168,7 +168,8 @@
   bool is_permanent() const {
     return (refcount() == PERM_REFCOUNT);
   }
-  void set_permanent();
+  void update_identity_hash() NOT_CDS_RETURN;
+  void set_permanent() NOT_CDS_RETURN;
   void make_permanent();
 
   // Function char_at() returns the Symbol's selected u1 byte as a char type.
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index ca52fef..600f58e 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -3733,6 +3733,29 @@
   HeapShared::initialize_from_archived_subgraph(k);
 JVM_END
 
+JVM_ENTRY_NO_ENV(jlong, JVM_GetRandomSeedForCDSDump())
+  JVMWrapper("JVM_GetRandomSeedForCDSDump");
+  if (DumpSharedSpaces) {
+    const char* release = Abstract_VM_Version::vm_release();
+    const char* dbg_level = Abstract_VM_Version::jdk_debug_level();
+    const char* version = VM_Version::internal_vm_info_string();
+    jlong seed = (jlong)(java_lang_String::hash_code((const jbyte*)release, (int)strlen(release)) ^
+                         java_lang_String::hash_code((const jbyte*)dbg_level, (int)strlen(dbg_level)) ^
+                         java_lang_String::hash_code((const jbyte*)version, (int)strlen(version)));
+    seed += (jlong)Abstract_VM_Version::vm_major_version();
+    seed += (jlong)Abstract_VM_Version::vm_minor_version();
+    seed += (jlong)Abstract_VM_Version::vm_security_version();
+    seed += (jlong)Abstract_VM_Version::vm_patch_version();
+    if (seed == 0) { // don't let this ever be zero.
+      seed = 0x87654321;
+    }
+    log_debug(cds)("JVM_GetRandomSeedForCDSDump() = " JLONG_FORMAT, seed);
+    return seed;
+  } else {
+    return 0;
+  }
+JVM_END
+
 // Returns an array of all live Thread objects (VM internal JavaThreads,
 // jvmti agent threads, and JNI attaching threads  are skipped)
 // See CR 6404306 regarding JNI attaching threads
diff --git a/src/java.base/share/classes/java/util/ImmutableCollections.java b/src/java.base/share/classes/java/util/ImmutableCollections.java
index 91fe4c6..ccf92e2 100644
--- a/src/java.base/share/classes/java/util/ImmutableCollections.java
+++ b/src/java.base/share/classes/java/util/ImmutableCollections.java
@@ -64,14 +64,22 @@
     private static final boolean REVERSE;
     static {
         // to generate a reasonably random and well-mixed SALT, use an arbitrary
-        // value (a slice of pi), multiply with the System.nanoTime, then pick
+        // value (a slice of pi), multiply with a random seed, then pick
         // the mid 32-bits from the product. By picking a SALT value in the
         // [0 ... 0xFFFF_FFFFL == 2^32-1] range, we ensure that for any positive
         // int N, (SALT32L * N) >> 32 is a number in the [0 ... N-1] range. This
         // property will be used to avoid more expensive modulo-based
         // calculations.
         long color = 0x243F_6A88_85A3_08D3L; // slice of pi
-        long seed = System.nanoTime();
+
+        // When running with -Xshare:dump, the VM will supply a "random" seed that's
+        // derived from the JVM build/version, so can we generate the exact same
+        // CDS archive for the same JDK build. This makes it possible to verify the
+        // consistency of the JDK build.
+        long seed = VM.getRandomSeedForCDSDump();
+        if (seed == 0) {
+          seed = System.nanoTime();
+        }
         SALT32L = (int)((color * seed) >> 16) & 0xFFFF_FFFFL;
         // use the lowest bit to determine if we should reverse iteration
         REVERSE = (SALT32L & 1) == 0;
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 4de92b3..168706b 100644
--- a/src/java.base/share/classes/jdk/internal/misc/VM.java
+++ b/src/java.base/share/classes/jdk/internal/misc/VM.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2020, Oracle and/or its affiliates. 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
@@ -421,6 +421,8 @@
      */
     public static native void initializeFromArchive(Class<?> c);
 
+    public static native long getRandomSeedForCDSDump();
+
     /**
      * Provides access to information on buffer usage.
      */
diff --git a/src/java.base/share/native/libjava/VM.c b/src/java.base/share/native/libjava/VM.c
index 3ef64cc..a4bd4b6 100644
--- a/src/java.base/share/native/libjava/VM.c
+++ b/src/java.base/share/native/libjava/VM.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2020, Oracle and/or its affiliates. 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
@@ -61,3 +61,8 @@
                                                 jclass c) {
     JVM_InitializeFromArchive(env, c);
 }
+
+JNIEXPORT jlong JNICALL
+Java_jdk_internal_misc_VM_getRandomSeedForCDSDump(JNIEnv *env, jclass ignore) {
+    return JVM_GetRandomSeedForCDSDump();
+}
diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
index fe78b3b..bcd942a 100644
--- a/test/hotspot/jtreg/TEST.groups
+++ b/test/hotspot/jtreg/TEST.groups
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2020, Oracle and/or its affiliates. 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
@@ -348,7 +348,8 @@
   runtime/modules/PatchModule/PatchModuleCDS.java \
   runtime/modules/PatchModule/PatchModuleClassList.java \
   runtime/NMT \
-  serviceability/sa
+  serviceability/sa \
+ -runtime/cds/DeterministicDump.java
 
 # A subset of AppCDS tests to be run in tier1
 tier1_runtime_appcds = \
diff --git a/test/hotspot/jtreg/runtime/cds/DeterministicDump.java b/test/hotspot/jtreg/runtime/cds/DeterministicDump.java
new file mode 100644
index 0000000..4a7e7c4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/cds/DeterministicDump.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. 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.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8241071
+ * @summary The same JDK build should always generate the same archive file (no randomness).
+ * @requires vm.cds
+ * @library /test/lib
+ * @run driver DeterministicDump
+ */
+
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+public class DeterministicDump {
+    public static void main(String[] args) throws Exception {
+        for (int c = 0; c < 2; c++) { //  oop/klass compression
+            String sign = (c == 0) ?  "+" : "-";
+            String coop = "-XX:" + sign + "UseCompressedOops";
+            String ckls = "-XX:" + sign + "UseCompressedClassPointers";
+
+            if (!Platform.is64bit()) {
+                coop = "-showversion"; // no-op
+                ckls = "-showversion"; // no-op
+            }
+
+            for (int gc = 0; gc < 2; gc++) { // should we trigger GC during dump
+                for (int i = 0; i < 2; i++) {
+                    String metaspaceSize = "-showversion"; // no-op
+                    if (gc == 1 && i == 1) {
+                        // This will cause GC to happen after we've allocated 1MB of metaspace objects
+                        // while processing the built-in SharedClassListFile.
+                        metaspaceSize = "-XX:MetaspaceSize=1M";
+                    }
+                    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+                                      coop, ckls, metaspaceSize,
+                                      "-XX:SharedArchiveFile=SharedArchiveFile" + i + ".jsa",
+                                      "-Xshare:dump", "-Xlog:cds=debug");
+                    OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "SharedArchiveFile" + i);
+                    CDSTestUtils.checkDump(out);
+                }
+                compare("SharedArchiveFile0.jsa", "SharedArchiveFile1.jsa");
+            }
+        }
+    }
+
+    static void compare(String file0, String file1) throws Exception {
+        byte[] buff0 = new byte[4096];
+        byte[] buff1 = new byte[4096];
+        try (FileInputStream in0 = new FileInputStream(file0);
+             FileInputStream in1 = new FileInputStream(file1)) {
+            int total = 0;
+            while (true) {
+                int n0 = read(in0, buff0);
+                int n1 = read(in1, buff1);
+                if (n0 != n1) {
+                    throw new RuntimeException("File contents (file sizes?) are different after " + total + " bytes; n0 = "
+                                               + n0 + ", n1 = " + n1);
+                }
+                if (n0 == 0) {
+                    System.out.println("File contents are the same: " + total + " bytes");
+                    break;
+                }
+                for (int i = 0; i < n0; i++) {
+                    byte b0 = buff0[i];
+                    byte b1 = buff1[i];
+                    if (b0 != b1) {
+                        throw new RuntimeException("File content different at byte #" + (total + i) + ", b0 = " + b0 + ", b1 = " + b1);
+                    }
+                }
+                total += n0;
+            }
+        }
+    }
+
+    static int read(FileInputStream in, byte[] buff) throws IOException {
+        int total = 0;
+        while (total < buff.length) {
+            int n = in.read(buff, total, buff.length - total);
+            if (n <= 0) {
+                return total;
+            }
+            total += n;
+        }
+
+        return total;
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/cds/SpaceUtilizationCheck.java b/test/hotspot/jtreg/runtime/cds/SpaceUtilizationCheck.java
index 187fb1f..470e0d6 100644
--- a/test/hotspot/jtreg/runtime/cds/SpaceUtilizationCheck.java
+++ b/test/hotspot/jtreg/runtime/cds/SpaceUtilizationCheck.java
@@ -43,6 +43,7 @@
 import java.lang.Integer;
 
 public class SpaceUtilizationCheck {
+    // For the MC/RW/RO regions:
     // [1] Each region must have strictly less than
     //     WhiteBox.metaspaceReserveAlignment() bytes of unused space.
     // [2] There must be no gap between two consecutive regions.
@@ -64,11 +65,21 @@
         opts.addSuffix(extra_options);
         OutputAnalyzer output = CDSTestUtils.createArchive(opts);
         CDSTestUtils.checkDump(output);
-        Pattern pattern = Pattern.compile("(..) *space: *([0-9]+).* out of *([0-9]+) bytes .* at 0x([0-9a0-f]+)");
+        Pattern pattern = Pattern.compile("(..)  space: *([0-9]+).* out of *([0-9]+) bytes .* at 0x([0-9a0-f]+)");
         WhiteBox wb = WhiteBox.getWhiteBox();
         long reserve_alignment = wb.metaspaceReserveAlignment();
         System.out.println("Metaspace::reserve_alignment() = " + reserve_alignment);
 
+        // Look for output like this. The pattern will only match the first 3 regions, which is what we need to check
+        //
+        // [4.682s][debug][cds] mc  space:     24912 [  0.2% of total] out of     28672 bytes [ 86.9% used] at 0x0000000800000000
+        // [4.682s][debug][cds] rw  space:   4391632 [ 33.7% of total] out of   4395008 bytes [ 99.9% used] at 0x0000000800007000
+        // [4.682s][debug][cds] ro  space:   7570632 [ 58.0% of total] out of   7573504 bytes [100.0% used] at 0x0000000800438000
+        // [4.682s][debug][cds] bm  space:    213528 [  1.6% of total] out of    213528 bytes [100.0% used]
+        // [4.682s][debug][cds] ca0 space:    507904 [  3.9% of total] out of    507904 bytes [100.0% used] at 0x00000000fff00000
+        // [4.682s][debug][cds] oa0 space:    327680 [  2.5% of total] out of    327680 bytes [100.0% used] at 0x00000000ffe00000
+        // [4.682s][debug][cds] total    :  13036288 [100.0% of total] out of  13049856 bytes [ 99.9% used]
+
         long last_region = -1;
         Hashtable<String,String> checked = new Hashtable<>();
         for (String line : output.getStdout().split("\n")) {
@@ -76,13 +87,8 @@
                 Matcher matcher = pattern.matcher(line);
                 if (matcher.find()) {
                     String name = matcher.group(1);
-                    if (name.equals("bm")) {
-                      // Bitmap space does not have a requested address.
-                      break;
-                    } else {
-                      System.out.println("Checking " + name + " in : " + line);
-                      checked.put(name, name);
-                    }
+                    System.out.println("Checking " + name + " in : " + line);
+                    checked.put(name, name);
                     long used = Long.parseLong(matcher.group(2));
                     long capacity = Long.parseLong(matcher.group(3));
                     long address = Long.parseLong(matcher.group(4), 16);