8150607: Clean up CompactHashtable
authoriklam
Sun, 17 Apr 2016 19:15:52 -0700
changeset 37995 92aec042a43b
parent 37994 1a816b464178
child 37999 be1f77668cf1
8150607: Clean up CompactHashtable Summary: refactored code, and added test cases for serviceability agent Reviewed-by: jiangli, ccheung
hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/SymbolTable.java
hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/CompactHashTable.java
hotspot/src/share/vm/classfile/compactHashtable.cpp
hotspot/src/share/vm/classfile/compactHashtable.hpp
hotspot/src/share/vm/classfile/compactHashtable.inline.hpp
hotspot/src/share/vm/classfile/stringTable.cpp
hotspot/src/share/vm/classfile/stringTable.hpp
hotspot/src/share/vm/classfile/symbolTable.cpp
hotspot/src/share/vm/classfile/symbolTable.hpp
hotspot/src/share/vm/classfile/systemDictionaryShared.hpp
hotspot/src/share/vm/memory/iterator.hpp
hotspot/src/share/vm/memory/metaspaceShared.cpp
hotspot/src/share/vm/memory/metaspaceShared.hpp
hotspot/src/share/vm/runtime/vmStructs.cpp
hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java
hotspot/test/runtime/SharedArchiveFile/SASymbolTableTest.java
hotspot/test/runtime/SharedArchiveFile/SASymbolTableTestAgent.java
hotspot/test/runtime/SharedArchiveFile/SASymbolTableTestAttachee.java
--- a/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/SymbolTable.java	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/SymbolTable.java	Sun Apr 17 19:15:52 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -85,6 +85,12 @@
       tables. */
   public Symbol probe(byte[] name) {
     long hashValue = hashSymbol(name);
+
+    Symbol s = sharedTable.probe(name, hashValue);
+    if (s != null) {
+      return s;
+    }
+
     for (HashtableEntry e = (HashtableEntry) bucket(hashToIndex(hashValue)); e != null; e = (HashtableEntry) e.next()) {
       if (e.hash() == hashValue) {
          Symbol sym = Symbol.create(e.literalValue());
@@ -94,7 +100,7 @@
       }
     }
 
-    return sharedTable.probe(name, hashValue);
+    return null;
   }
 
   public interface SymbolVisitor {
--- a/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/CompactHashTable.java	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/CompactHashTable.java	Sun Apr 17 19:15:52 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -44,21 +44,23 @@
     Type type = db.lookupType("SymbolCompactHashTable");
     baseAddressField = type.getAddressField("_base_address");
     bucketCountField = type.getCIntegerField("_bucket_count");
-    tableEndOffsetField = type.getCIntegerField("_table_end_offset");
+    entryCountField = type.getCIntegerField("_entry_count");
     bucketsField = type.getAddressField("_buckets");
-    uintSize = db.lookupType("juint").getSize();
+    entriesField = type.getAddressField("_entries");
+    uintSize = db.lookupType("u4").getSize();
   }
 
   // Fields
   private static CIntegerField bucketCountField;
-  private static CIntegerField tableEndOffsetField;
+  private static CIntegerField entryCountField;
   private static AddressField  baseAddressField;
   private static AddressField  bucketsField;
+  private static AddressField  entriesField;
   private static long uintSize;
 
   private static int BUCKET_OFFSET_MASK = 0x3FFFFFFF;
   private static int BUCKET_TYPE_SHIFT = 30;
-  private static int COMPACT_BUCKET_TYPE = 1;
+  private static int VALUE_ONLY_BUCKET_TYPE = 1;
 
   public CompactHashTable(Address addr) {
     super(addr);
@@ -68,12 +70,8 @@
     return (int)bucketCountField.getValue(addr);
   }
 
-  private int tableEndOffset() {
-    return (int)tableEndOffsetField.getValue(addr);
-  }
-
-  private boolean isCompactBucket(int bucket_info) {
-    return (bucket_info >> BUCKET_TYPE_SHIFT) == COMPACT_BUCKET_TYPE;
+  private boolean isValueOnlyBucket(int bucket_info) {
+    return (bucket_info >> BUCKET_TYPE_SHIFT) == VALUE_ONLY_BUCKET_TYPE;
   }
 
   private int bucketOffset(int bucket_info) {
@@ -81,9 +79,8 @@
   }
 
   public Symbol probe(byte[] name, long hash) {
-
-    if (bucketCount() == 0) {
-      // The table is invalid, so don't try to lookup
+    if (bucketCount() <= 0) {
+      // This CompactHashTable is not in use
       return null;
     }
 
@@ -91,34 +88,33 @@
     Symbol  sym;
     Address baseAddress = baseAddressField.getValue(addr);
     Address bucket = bucketsField.getValue(addr);
-    Address bucketEnd = bucket;
     long index = hash % bucketCount();
     int bucketInfo = (int)bucket.getCIntegerAt(index * uintSize, uintSize, true);
     int bucketOffset = bucketOffset(bucketInfo);
     int nextBucketInfo = (int)bucket.getCIntegerAt((index+1) * uintSize, uintSize, true);
     int nextBucketOffset = bucketOffset(nextBucketInfo);
 
-    bucket = bucket.addOffsetTo(bucketOffset * uintSize);
+    Address entry = entriesField.getValue(addr).addOffsetTo(bucketOffset * uintSize);
 
-    if (isCompactBucket(bucketInfo)) {
-      symOffset = bucket.getCIntegerAt(0, uintSize, true);
+    if (isValueOnlyBucket(bucketInfo)) {
+      symOffset = entry.getCIntegerAt(0, uintSize, true);
       sym = Symbol.create(baseAddress.addOffsetTo(symOffset));
       if (sym.equals(name)) {
         return sym;
       }
     } else {
-      bucketEnd = bucket.addOffsetTo(nextBucketOffset * uintSize);
-      while (bucket.lessThan(bucketEnd)) {
-        long symHash = bucket.getCIntegerAt(0, uintSize, true);
+      Address entryMax = entriesField.getValue(addr).addOffsetTo(nextBucketOffset * uintSize);
+      while (entry.lessThan(entryMax)) {
+        long symHash = entry.getCIntegerAt(0, uintSize, true);
         if (symHash == hash) {
-          symOffset = bucket.getCIntegerAt(uintSize, uintSize, true);
+          symOffset = entry.getCIntegerAt(uintSize, uintSize, true);
           Address symAddr = baseAddress.addOffsetTo(symOffset);
           sym = Symbol.create(symAddr);
           if (sym.equals(name)) {
             return sym;
           }
         }
-        bucket = bucket.addOffsetTo(2 * uintSize);
+        entry = entry.addOffsetTo(2 * uintSize);
       }
     }
     return null;
--- a/hotspot/src/share/vm/classfile/compactHashtable.cpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp	Sun Apr 17 19:15:52 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "classfile/compactHashtable.inline.hpp"
 #include "classfile/javaClasses.hpp"
+#include "memory/metadataFactory.hpp"
 #include "memory/metaspaceShared.hpp"
 #include "prims/jvm.h"
 #include "utilities/numberSeq.hpp"
@@ -34,270 +35,259 @@
 //
 // The compact hash table writer implementations
 //
-CompactHashtableWriter::CompactHashtableWriter(int table_type,
-                                               int num_entries,
+CompactHashtableWriter::CompactHashtableWriter(int num_buckets,
                                                CompactHashtableStats* stats) {
   assert(DumpSharedSpaces, "dump-time only");
-  _type = table_type;
-  _num_entries = num_entries;
-  _num_buckets = number_of_buckets(_num_entries);
-  _buckets = NEW_C_HEAP_ARRAY(Entry*, _num_buckets, mtSymbol);
-  memset(_buckets, 0, sizeof(Entry*) * _num_buckets);
-
-  /* bucket sizes table */
-  _bucket_sizes = NEW_C_HEAP_ARRAY(juint, _num_buckets, mtSymbol);
-  memset(_bucket_sizes, 0, sizeof(juint) * _num_buckets);
+  _num_buckets = num_buckets;
+  _num_entries = 0;
+  _buckets = NEW_C_HEAP_ARRAY(GrowableArray<Entry>*, _num_buckets, mtSymbol);
+  for (int i=0; i<_num_buckets; i++) {
+    _buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray<Entry>(0, true, mtSymbol);
+  }
 
-  stats->hashentry_count = _num_entries;
-  // Compact buckets' entries will have only the 4-byte offset, but
-  // we don't know how many there will be at this point. So use a
-  // conservative estimate here. The size is adjusted later when we
-  // write out the buckets.
-  stats->hashentry_bytes = _num_entries * 8;
-  stats->bucket_count    = _num_buckets;
-  stats->bucket_bytes    = (_num_buckets + 1) * (sizeof(juint));
+  stats->bucket_count = _num_buckets;
+  stats->bucket_bytes = (_num_buckets + 1) * (sizeof(u4));
   _stats = stats;
-
-  // See compactHashtable.hpp for table layout
-  _required_bytes = sizeof(juint) * 2; // _base_address, written as 2 juints
-  _required_bytes+= sizeof(juint) +    // num_entries
-                    sizeof(juint) +    // num_buckets
-                    stats->hashentry_bytes +
-                    stats->bucket_bytes;
+  _compact_buckets = NULL;
+  _compact_entries = NULL;
+  _num_empty_buckets = 0;
+  _num_value_only_buckets = 0;
+  _num_other_buckets = 0;
 }
 
 CompactHashtableWriter::~CompactHashtableWriter() {
   for (int index = 0; index < _num_buckets; index++) {
-    Entry* next = NULL;
-    for (Entry* tent = _buckets[index]; tent; tent = next) {
-      next = tent->next();
-      delete tent;
-    }
+    GrowableArray<Entry>* bucket = _buckets[index];
+    delete bucket;
   }
 
-  FREE_C_HEAP_ARRAY(juint, _bucket_sizes);
-  FREE_C_HEAP_ARRAY(Entry*, _buckets);
-}
-
-// Calculate the number of buckets in the temporary hash table
-int CompactHashtableWriter::number_of_buckets(int num_entries) {
-  const int buksize = (int)SharedSymbolTableBucketSize;
-  int num_buckets = (num_entries + buksize - 1) / buksize;
-  num_buckets = (num_buckets + 1) & (~0x01);
-
-  return num_buckets;
+  FREE_C_HEAP_ARRAY(GrowableArray<Entry>*, _buckets);
 }
 
 // Add a symbol entry to the temporary hash table
-void CompactHashtableWriter::add(unsigned int hash, Entry* entry) {
+void CompactHashtableWriter::add(unsigned int hash, u4 value) {
   int index = hash % _num_buckets;
-  entry->set_next(_buckets[index]);
-  _buckets[index] = entry;
-  _bucket_sizes[index] ++;
+  _buckets[index]->append_if_missing(Entry(hash, value));
+  _num_entries++;
 }
 
-// Write the compact table's bucket infos
-juint* CompactHashtableWriter::dump_table(juint* p, juint** first_bucket,
-                                          NumberSeq* summary) {
-  int index;
-  juint* compact_table = p;
-  // Compute the start of the buckets, include the compact_bucket_infos table
-  // and the table end offset.
-  juint offset = _num_buckets + 1;
-  *first_bucket = compact_table + offset;
+void CompactHashtableWriter::allocate_table() {
+  int entries_space = 0;
+  for (int index = 0; index < _num_buckets; index++) {
+    GrowableArray<Entry>* bucket = _buckets[index];
+    int bucket_size = bucket->length();
+    if (bucket_size == 1) {
+      entries_space++;
+    } else {
+      entries_space += 2 * bucket_size;
+    }
+  }
 
-  for (index = 0; index < _num_buckets; index++) {
-    int bucket_size = _bucket_sizes[index];
+  if (entries_space & ~BUCKET_OFFSET_MASK) {
+    vm_exit_during_initialization("CompactHashtableWriter::allocate_table: Overflow! "
+                                  "Too many entries.");
+  }
+
+  Thread* THREAD = VMThread::vm_thread();
+  ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
+  _compact_buckets = MetadataFactory::new_array<u4>(loader_data, _num_buckets + 1, THREAD);
+  _compact_entries = MetadataFactory::new_array<u4>(loader_data, entries_space, THREAD);
+
+  _stats->hashentry_count = _num_entries;
+  _stats->hashentry_bytes = entries_space * sizeof(u4);
+}
+
+// Write the compact table's buckets
+void CompactHashtableWriter::dump_table(NumberSeq* summary) {
+  u4 offset = 0;
+  for (int index = 0; index < _num_buckets; index++) {
+    GrowableArray<Entry>* bucket = _buckets[index];
+    int bucket_size = bucket->length();
     if (bucket_size == 1) {
       // bucket with one entry is compacted and only has the symbol offset
-      compact_table[index] = BUCKET_INFO(offset, COMPACT_BUCKET_TYPE);
-      offset += bucket_size; // each entry contains symbol offset only
+      _compact_buckets->at_put(index, BUCKET_INFO(offset, VALUE_ONLY_BUCKET_TYPE));
+
+      Entry ent = bucket->at(0);
+      _compact_entries->at_put(offset++, ent.value());
+      _num_value_only_buckets++;
     } else {
       // regular bucket, each entry is a symbol (hash, offset) pair
-      compact_table[index] = BUCKET_INFO(offset, REGULAR_BUCKET_TYPE);
-      offset += bucket_size * 2; // each hash entry is 2 juints
-    }
-    if (offset & ~BUCKET_OFFSET_MASK) {
-      vm_exit_during_initialization("CompactHashtableWriter::dump_table: Overflow! "
-                                    "Too many symbols.");
+      _compact_buckets->at_put(index, BUCKET_INFO(offset, REGULAR_BUCKET_TYPE));
+
+      for (int i=0; i<bucket_size; i++) {
+        Entry ent = bucket->at(i);
+        _compact_entries->at_put(offset++, u4(ent.hash())); // write entry hash
+        _compact_entries->at_put(offset++, ent.value());
+      }
+      if (bucket_size == 0) {
+        _num_empty_buckets++;
+      } else {
+        _num_other_buckets++;
+      }
     }
     summary->add(bucket_size);
   }
-  // Mark the end of the table
-  compact_table[_num_buckets] = BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE);
 
-  return compact_table;
+  // Mark the end of the buckets
+  _compact_buckets->at_put(_num_buckets, BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE));
+  assert(offset == (u4)_compact_entries->length(), "sanity");
 }
 
-// Write the compact table's entries
-juint* CompactHashtableWriter::dump_buckets(juint* compact_table, juint* p,
-                                            NumberSeq* summary) {
-  uintx base_address = 0;
-  uintx max_delta = 0;
-  int num_compact_buckets = 0;
-  if (_type == CompactHashtable<Symbol*, char>::_symbol_table) {
-    base_address = uintx(MetaspaceShared::shared_rs()->base());
-    max_delta    = uintx(MetaspaceShared::shared_rs()->size());
-    assert(max_delta <= MAX_SHARED_DELTA, "range check");
-  } else {
-    assert((_type == CompactHashtable<oop, char>::_string_table), "unknown table");
-    assert(UseCompressedOops, "UseCompressedOops is required");
-  }
-
-  assert(p != NULL, "sanity");
-  for (int index = 0; index < _num_buckets; index++) {
-    juint count = 0;
-    int bucket_size = _bucket_sizes[index];
-    int bucket_type = BUCKET_TYPE(compact_table[index]);
-
-    if (bucket_size == 1) {
-      assert(bucket_type == COMPACT_BUCKET_TYPE, "Bad bucket type");
-      num_compact_buckets ++;
-    }
-    for (Entry* tent = _buckets[index]; tent;
-         tent = tent->next()) {
-      if (bucket_type == REGULAR_BUCKET_TYPE) {
-        *p++ = juint(tent->hash()); // write entry hash
-      }
-      if (_type == CompactHashtable<Symbol*, char>::_symbol_table) {
-        uintx deltax = uintx(tent->value()) - base_address;
-        assert(deltax < max_delta, "range check");
-        juint delta = juint(deltax);
-        *p++ = delta; // write entry offset
-      } else {
-        *p++ = oopDesc::encode_heap_oop(tent->string());
-      }
-      count ++;
-    }
-    assert(count == _bucket_sizes[index], "sanity");
-  }
-
-  // Adjust the hashentry_bytes in CompactHashtableStats. Each compact
-  // bucket saves 4-byte.
-  _stats->hashentry_bytes -= num_compact_buckets * 4;
-
-  return p;
-}
 
 // Write the compact table
-void CompactHashtableWriter::dump(char** top, char* end) {
+void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table_name) {
   NumberSeq summary;
-  char* old_top = *top;
-  juint* p = (juint*)(*top);
-
-  uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
+  allocate_table();
+  dump_table(&summary);
 
-  // Now write the following at the beginning of the table:
-  //      base_address (uintx)
-  //      num_entries  (juint)
-  //      num_buckets  (juint)
-  *p++ = high(base_address);
-  *p++ = low (base_address); // base address
-  *p++ = _num_entries;  // number of entries in the table
-  *p++ = _num_buckets;  // number of buckets in the table
-
-  juint* first_bucket = NULL;
-  juint* compact_table = dump_table(p, &first_bucket, &summary);
-  juint* bucket_end = dump_buckets(compact_table, first_bucket, &summary);
-
-  assert(bucket_end <= (juint*)end, "cannot write past end");
-  *top = (char*)bucket_end;
+  int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes;
+  address base_address = address(MetaspaceShared::shared_rs()->base());
+  cht->init(base_address,  _num_entries, _num_buckets,
+            _compact_buckets->data(), _compact_entries->data());
 
   if (PrintSharedSpaces) {
     double avg_cost = 0.0;
     if (_num_entries > 0) {
-      avg_cost = double(_required_bytes)/double(_num_entries);
+      avg_cost = double(table_bytes)/double(_num_entries);
     }
     tty->print_cr("Shared %s table stats -------- base: " PTR_FORMAT,
-                  table_name(), (intptr_t)base_address);
+                  table_name, (intptr_t)base_address);
     tty->print_cr("Number of entries       : %9d", _num_entries);
-    tty->print_cr("Total bytes used        : %9d", (int)((*top) - old_top));
+    tty->print_cr("Total bytes used        : %9d", table_bytes);
     tty->print_cr("Average bytes per entry : %9.3f", avg_cost);
     tty->print_cr("Average bucket size     : %9.3f", summary.avg());
     tty->print_cr("Variance of bucket size : %9.3f", summary.variance());
     tty->print_cr("Std. dev. of bucket size: %9.3f", summary.sd());
-    tty->print_cr("Maximum bucket size     : %9d", (int)summary.maximum());
+    tty->print_cr("Empty buckets           : %9d", _num_empty_buckets);
+    tty->print_cr("Value_Only buckets      : %9d", _num_value_only_buckets);
+    tty->print_cr("Other buckets           : %9d", _num_other_buckets);
   }
 }
 
-const char* CompactHashtableWriter::table_name() {
-  switch (_type) {
-  case CompactHashtable<Symbol*, char>::_symbol_table: return "symbol";
-  case CompactHashtable<oop, char>::_string_table: return "string";
-  default:
-    ;
-  }
-  return "unknown";
+/////////////////////////////////////////////////////////////
+//
+// Customization for dumping Symbol and String tables
+
+void CompactSymbolTableWriter::add(unsigned int hash, Symbol *symbol) {
+  address base_address = address(MetaspaceShared::shared_rs()->base());
+  uintx max_delta = uintx(MetaspaceShared::shared_rs()->size());
+  assert(max_delta <= MAX_SHARED_DELTA, "range check");
+
+  uintx deltax = address(symbol) - base_address;
+  assert(deltax < max_delta, "range check");
+  u4 delta = u4(deltax);
+
+  CompactHashtableWriter::add(hash, delta);
+}
+
+void CompactStringTableWriter::add(unsigned int hash, oop string) {
+  CompactHashtableWriter::add(hash, oopDesc::encode_heap_oop(string));
+}
+
+void CompactSymbolTableWriter::dump(CompactHashtable<Symbol*, char> *cht) {
+  CompactHashtableWriter::dump(cht, "symbol");
+}
+
+void CompactStringTableWriter::dump(CompactHashtable<oop, char> *cht) {
+  CompactHashtableWriter::dump(cht, "string");
 }
 
 /////////////////////////////////////////////////////////////
 //
 // The CompactHashtable implementation
 //
-template <class T, class N> const char* CompactHashtable<T, N>::init(
-                           CompactHashtableType type, const char* buffer) {
-  assert(!DumpSharedSpaces, "run-time only");
-  _type = type;
-  juint*p = (juint*)buffer;
-  juint upper = *p++;
-  juint lower = *p++;
-  _base_address = uintx(jlong_from(upper, lower));
-  _entry_count = *p++;
-  _bucket_count = *p++;
-  _buckets = p;
-  _table_end_offset = BUCKET_OFFSET(p[_bucket_count]); // located at the end of the bucket_info table
 
-  juint *end = _buckets + _table_end_offset;
-  return (const char*)end;
+void SimpleCompactHashtable::serialize(SerializeClosure* soc) {
+  soc->do_ptr((void**)&_base_address);
+  soc->do_u4(&_entry_count);
+  soc->do_u4(&_bucket_count);
+  soc->do_ptr((void**)&_buckets);
+  soc->do_ptr((void**)&_entries);
 }
 
-template <class T, class N> void CompactHashtable<T, N>::symbols_do(SymbolClosure *cl) {
+bool SimpleCompactHashtable::exists(u4 value) {
   assert(!DumpSharedSpaces, "run-time only");
-  for (juint i = 0; i < _bucket_count; i ++) {
-    juint bucket_info = _buckets[i];
-    juint bucket_offset = BUCKET_OFFSET(bucket_info);
-    int   bucket_type = BUCKET_TYPE(bucket_info);
-    juint* bucket = _buckets + bucket_offset;
-    juint* bucket_end = _buckets;
+
+  if (_entry_count == 0) {
+    return false;
+  }
+
+  unsigned int hash = (unsigned int)value;
+  int index = hash % _bucket_count;
+  u4 bucket_info = _buckets[index];
+  u4 bucket_offset = BUCKET_OFFSET(bucket_info);
+  int bucket_type = BUCKET_TYPE(bucket_info);
+  u4* entry = _entries + bucket_offset;
 
-    Symbol* sym;
-    if (bucket_type == COMPACT_BUCKET_TYPE) {
-      sym = (Symbol*)((void*)(_base_address + bucket[0]));
-      cl->do_symbol(&sym);
+  if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
+    return (entry[0] == value);
+  } else {
+    u4*entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]);
+    while (entry <entry_max) {
+      if (entry[1] == value) {
+        return true;
+      }
+      entry += 2;
+    }
+    return false;
+  }
+}
+
+template <class I>
+inline void SimpleCompactHashtable::iterate(const I& iterator) {
+  assert(!DumpSharedSpaces, "run-time only");
+  for (u4 i = 0; i < _bucket_count; i++) {
+    u4 bucket_info = _buckets[i];
+    u4 bucket_offset = BUCKET_OFFSET(bucket_info);
+    int bucket_type = BUCKET_TYPE(bucket_info);
+    u4* entry = _entries + bucket_offset;
+
+    if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
+      iterator.do_value(_base_address, entry[0]);
     } else {
-      bucket_end += BUCKET_OFFSET(_buckets[i + 1]);
-      while (bucket < bucket_end) {
-        sym = (Symbol*)((void*)(_base_address + bucket[1]));
-        cl->do_symbol(&sym);
-        bucket += 2;
+      u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]);
+      while (entry < entry_max) {
+        iterator.do_value(_base_address, entry[0]);
+        entry += 2;
       }
     }
   }
 }
 
-template <class T, class N> void CompactHashtable<T, N>::oops_do(OopClosure* f) {
-  assert(!DumpSharedSpaces, "run-time only");
-  assert(_type == _string_table || _bucket_count == 0, "sanity");
-  for (juint i = 0; i < _bucket_count; i ++) {
-    juint bucket_info = _buckets[i];
-    juint bucket_offset = BUCKET_OFFSET(bucket_info);
-    int   bucket_type = BUCKET_TYPE(bucket_info);
-    juint* bucket = _buckets + bucket_offset;
-    juint* bucket_end = _buckets;
+template <class T, class N> void CompactHashtable<T, N>::serialize(SerializeClosure* soc) {
+  SimpleCompactHashtable::serialize(soc);
+  soc->do_u4(&_type);
+}
+
+class CompactHashtable_SymbolIterator {
+  SymbolClosure* const _closure;
+public:
+  CompactHashtable_SymbolIterator(SymbolClosure *cl) : _closure(cl) {}
+  inline void do_value(address base_address, u4 offset) const {
+    Symbol* sym = (Symbol*)((void*)(base_address + offset));
+    _closure->do_symbol(&sym);
+  }
+};
 
-    narrowOop o;
-    if (bucket_type == COMPACT_BUCKET_TYPE) {
-      o = (narrowOop)bucket[0];
-      f->do_oop(&o);
-    } else {
-      bucket_end += BUCKET_OFFSET(_buckets[i + 1]);
-      while (bucket < bucket_end) {
-        o = (narrowOop)bucket[1];
-        f->do_oop(&o);
-        bucket += 2;
-      }
-    }
+template <class T, class N> void CompactHashtable<T, N>::symbols_do(SymbolClosure *cl) {
+  CompactHashtable_SymbolIterator iterator(cl);
+  iterate(iterator);
+}
+
+class CompactHashtable_OopIterator {
+  OopClosure* const _closure;
+public:
+  CompactHashtable_OopIterator(OopClosure *cl) : _closure(cl) {}
+  inline void do_value(address base_address, u4 offset) const {
+    narrowOop o = (narrowOop)offset;
+    _closure->do_oop(&o);
   }
+};
+
+template <class T, class N> void CompactHashtable<T, N>::oops_do(OopClosure* cl) {
+  assert(_type == _string_table || _bucket_count == 0, "sanity");
+  CompactHashtable_OopIterator iterator(cl);
+  iterate(iterator);
 }
 
 // Explicitly instantiate these types
@@ -360,7 +350,7 @@
   } else {
     corrupted(_p, "Unexpected character");
   }
-  _line_no ++;
+  _line_no++;
   return true;
 }
 
@@ -390,7 +380,7 @@
 }
 
 void HashtableTextDump::scan_prefix_type() {
-  _p ++;
+  _p++;
   if (strncmp(_p, "SECTION: String", 15) == 0) {
     _p += 15;
     _prefix_type = StringPrefix;
--- a/hotspot/src/share/vm/classfile/compactHashtable.hpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp	Sun Apr 17 19:15:52 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -31,7 +31,10 @@
 #include "services/diagnosticCommand.hpp"
 #include "utilities/hashtable.hpp"
 
+template <class T, class N> class CompactHashtable;
 class NumberSeq;
+class SimpleCompactHashtable;
+class SerializeClosure;
 
 // Stats for symbol tables in the CDS archive
 class CompactHashtableStats VALUE_OBJ_CLASS_SPEC {
@@ -70,66 +73,74 @@
 //
 class CompactHashtableWriter: public StackObj {
 public:
-  class Entry: public CHeapObj<mtSymbol> {
-    Entry* _next;
+  class Entry VALUE_OBJ_CLASS_SPEC {
     unsigned int _hash;
-    void* _literal;
+    u4 _value;
 
   public:
-    Entry(unsigned int hash, Symbol *symbol) : _next(NULL), _hash(hash), _literal(symbol) {}
-    Entry(unsigned int hash, oop string)     : _next(NULL), _hash(hash), _literal(string) {}
+    Entry() {}
+    Entry(unsigned int hash, u4 val) : _hash(hash), _value(val) {}
 
-    void *value() {
-      return _literal;
-    }
-    Symbol *symbol() {
-      return (Symbol*)_literal;
-    }
-    oop string() {
-      return (oop)_literal;
+    u4 value() {
+      return _value;
     }
     unsigned int hash() {
       return _hash;
     }
-    Entry *next()           {return _next;}
-    void set_next(Entry *p) {_next = p;}
+
+    bool operator==(const CompactHashtableWriter::Entry& other) {
+      return (_value == other._value && _hash == other._hash);
+    }
   }; // class CompactHashtableWriter::Entry
 
 private:
-  static int number_of_buckets(int num_entries);
-
-  int _type;
   int _num_entries;
   int _num_buckets;
-  juint* _bucket_sizes;
-  Entry** _buckets;
-  int _required_bytes;
+  int _num_empty_buckets;
+  int _num_value_only_buckets;
+  int _num_other_buckets;
+  GrowableArray<Entry>** _buckets;
   CompactHashtableStats* _stats;
+  Array<u4>* _compact_buckets;
+  Array<u4>* _compact_entries;
 
 public:
   // This is called at dump-time only
-  CompactHashtableWriter(int table_type, int num_entries, CompactHashtableStats* stats);
+  CompactHashtableWriter(int num_buckets, CompactHashtableStats* stats);
   ~CompactHashtableWriter();
 
-  int get_required_bytes() {
-    return _required_bytes;
+  void add(unsigned int hash, u4 value);
+  void add(u4 value) {
+    add((unsigned int)value, value);
   }
 
-  inline void add(unsigned int hash, Symbol* symbol);
-  inline void add(unsigned int hash, oop string);
-
 private:
-  void add(unsigned int hash, Entry* entry);
-  juint* dump_table(juint* p, juint** first_bucket, NumberSeq* summary);
-  juint* dump_buckets(juint* table, juint* p, NumberSeq* summary);
+  void allocate_table();
+  void dump_table(NumberSeq* summary);
 
 public:
-  void dump(char** top, char* end);
+  void dump(SimpleCompactHashtable *cht, const char* table_name);
   const char* table_name();
 };
 
+class CompactSymbolTableWriter: public CompactHashtableWriter {
+public:
+  CompactSymbolTableWriter(int num_buckets, CompactHashtableStats* stats) :
+    CompactHashtableWriter(num_buckets, stats) {}
+  void add(unsigned int hash, Symbol *symbol);
+  void dump(CompactHashtable<Symbol*, char> *cht);
+};
+
+class CompactStringTableWriter: public CompactHashtableWriter {
+public:
+  CompactStringTableWriter(int num_entries, CompactHashtableStats* stats) :
+    CompactHashtableWriter(num_entries, stats) {}
+  void add(unsigned int hash, oop string);
+  void dump(CompactHashtable<oop, char> *cht);
+};
+
 #define REGULAR_BUCKET_TYPE       0
-#define COMPACT_BUCKET_TYPE       1
+#define VALUE_ONLY_BUCKET_TYPE    1
 #define TABLEEND_BUCKET_TYPE      3
 #define BUCKET_OFFSET_MASK        0x3FFFFFFF
 #define BUCKET_OFFSET(info)       ((info) & BUCKET_OFFSET_MASK)
@@ -146,90 +157,106 @@
 // and tend to have large number of entries, we try to minimize the footprint
 // cost per entry.
 //
-// Layout of compact table in the shared archive:
+// The CompactHashtable is split into two arrays
 //
-//   uintx base_address;
-//   juint num_entries;
-//   juint num_buckets;
-//   juint bucket_infos[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset
-//   juint table[]
+//   u4 buckets[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset
+//   u4 entries[<variable size>]
 //
-// -----------------------------------
-// | base_address  | num_entries     |
-// |---------------------------------|
-// | num_buckets   | bucket_info0    |
-// |---------------------------------|
-// | bucket_info1  | bucket_info2    |
-// | bucket_info3    ...             |
-// | ....          | table_end_info  |
-// |---------------------------------|
-// | entry0                          |
-// | entry1                          |
-// | entry2                          |
-// |                                 |
-// | ...                             |
-// -----------------------------------
+// The size of buckets[] is 'num_buckets + 1'. Each entry of
+// buckets[] is a 32-bit encoding of the bucket type and bucket offset,
+// with the type in the left-most 2-bit and offset in the remaining 30-bit.
+// The last entry is a special type. It contains the end of the last
+// bucket.
 //
-// The size of the bucket_info table is 'num_buckets + 1'. Each entry of the
-// bucket_info table is a 32-bit encoding of the bucket type and bucket offset,
-// with the type in the left-most 2-bit and offset in the remaining 30-bit.
-// The last entry is a special type. It contains the offset of the last
-// bucket end. We use that information when traversing the compact table.
-//
-// There are two types of buckets, regular buckets and compact buckets. The
-// compact buckets have '01' in their highest 2-bit, and regular buckets have
+// There are two types of buckets, regular buckets and value_only buckets. The
+// value_only buckets have '01' in their highest 2-bit, and regular buckets have
 // '00' in their highest 2-bit.
 //
-// For normal buckets, each entry is 8 bytes in the table[]:
-//   juint hash;    /* symbol/string hash */
+// For normal buckets, each entry is 8 bytes in the entries[]:
+//   u4 hash;    /* symbol/string hash */
 //   union {
-//     juint offset;  /* Symbol* sym = (Symbol*)(base_address + offset) */
+//     u4 offset;  /* Symbol* sym = (Symbol*)(base_address + offset) */
 //     narrowOop str; /* String narrowOop encoding */
 //   }
 //
 //
-// For compact buckets, each entry has only the 4-byte 'offset' in the table[].
+// For value_only buckets, each entry has only the 4-byte 'offset' in the entries[].
+//
+// Example -- note that the second bucket is a VALUE_ONLY_BUCKET_TYPE so the hash code
+//            is skipped.
+// buckets[0, 4, 5, ....]
+//         |  |  |
+//         |  |  +---+
+//         |  |      |
+//         |  +----+ |
+//         v       v v
+// entries[H,O,H,O,O,H,O,H,O.....]
 //
 // See CompactHashtable::lookup() for how the table is searched at runtime.
 // See CompactHashtableWriter::dump() for how the table is written at CDS
 // dump time.
 //
-template <class T, class N> class CompactHashtable VALUE_OBJ_CLASS_SPEC {
+class SimpleCompactHashtable VALUE_OBJ_CLASS_SPEC {
+protected:
+  address  _base_address;
+  u4  _bucket_count;
+  u4  _entry_count;
+  u4* _buckets;
+  u4* _entries;
+
+public:
+  SimpleCompactHashtable() {
+    _entry_count = 0;
+    _bucket_count = 0;
+    _buckets = 0;
+    _entries = 0;
+  }
+
+  void reset() {
+    _bucket_count = 0;
+    _entry_count = 0;
+    _buckets = 0;
+    _entries = 0;
+  }
+
+  void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) {
+    _base_address = base_address;
+    _bucket_count = bucket_count;
+    _entry_count = entry_count;
+    _buckets = buckets;
+    _entries = entries;
+  }
+
+  template <class I> inline void iterate(const I& iterator);
+
+  bool exists(u4 value);
+
+  // For reading from/writing to the CDS archive
+  void serialize(SerializeClosure* soc);
+};
+
+template <class T, class N> class CompactHashtable : public SimpleCompactHashtable {
   friend class VMStructs;
 
- public:
+public:
   enum CompactHashtableType {
     _symbol_table = 0,
     _string_table = 1
   };
 
 private:
-  CompactHashtableType _type;
-  uintx  _base_address;
-  juint  _entry_count;
-  juint  _bucket_count;
-  juint  _table_end_offset;
-  juint* _buckets;
+  u4 _type;
 
-  inline Symbol* lookup_entry(CompactHashtable<Symbol*, char>* const t,
-                              juint* addr, const char* name, int len);
+  inline Symbol* decode_entry(CompactHashtable<Symbol*, char>* const t,
+                              u4 offset, const char* name, int len);
 
-  inline oop lookup_entry(CompactHashtable<oop, char>* const t,
-                          juint* addr, const char* name, int len);
+  inline oop decode_entry(CompactHashtable<oop, char>* const t,
+                          u4 offset, const char* name, int len);
 public:
-  CompactHashtable() {
-    _entry_count = 0;
-    _bucket_count = 0;
-    _table_end_offset = 0;
-    _buckets = 0;
-  }
-  const char* init(CompactHashtableType type, const char *buffer);
+  CompactHashtable() : SimpleCompactHashtable() {}
 
-  void reset() {
-    _entry_count = 0;
-    _bucket_count = 0;
-    _table_end_offset = 0;
-    _buckets = 0;
+  void set_type(CompactHashtableType type) {
+    _type = (u4)type;
   }
 
   // Lookup an entry from the compact table
@@ -240,6 +267,9 @@
 
   // iterate over strings
   void oops_do(OopClosure* f);
+
+  // For reading from/writing to the CDS archive
+  void serialize(SerializeClosure* soc);
 };
 
 ////////////////////////////////////////////////////////////////////////
@@ -293,7 +323,7 @@
     u8 n = 0;
 
     while (p < end) {
-      char c = *p ++;
+      char c = *p++;
       if ('0' <= c && c <= '9') {
         n = n * 10 + (c - '0');
         if (n > (u8)INT_MAX) {
--- a/hotspot/src/share/vm/classfile/compactHashtable.inline.hpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/classfile/compactHashtable.inline.hpp	Sun Apr 17 19:15:52 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, 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
@@ -30,9 +30,9 @@
 #include "oops/oop.inline.hpp"
 
 template <class T, class N>
-inline Symbol* CompactHashtable<T, N>::lookup_entry(CompactHashtable<Symbol*, char>* const t,
-                                             juint* addr, const char* name, int len) {
-  Symbol* sym = (Symbol*)((void*)(_base_address + *addr));
+inline Symbol* CompactHashtable<T, N>::decode_entry(CompactHashtable<Symbol*, char>* const t,
+                                                    u4 offset, const char* name, int len) {
+  Symbol* sym = (Symbol*)(_base_address + offset);
   if (sym->equals(name, len)) {
     assert(sym->refcount() == -1, "must be shared");
     return sym;
@@ -42,9 +42,9 @@
 }
 
 template <class T, class N>
-inline oop CompactHashtable<T, N>::lookup_entry(CompactHashtable<oop, char>* const t,
-                                                juint* addr, const char* name, int len) {
-  narrowOop obj = (narrowOop)(*addr);
+inline oop CompactHashtable<T, N>::decode_entry(CompactHashtable<oop, char>* const t,
+                                                u4 offset, const char* name, int len) {
+  narrowOop obj = (narrowOop)offset;
   oop string = oopDesc::decode_heap_oop(obj);
   if (java_lang_String::equals(string, (jchar*)name, len)) {
     return string;
@@ -56,17 +56,14 @@
 template <class T, class N>
 inline T CompactHashtable<T,N>::lookup(const N* name, unsigned int hash, int len) {
   if (_entry_count > 0) {
-    assert(!DumpSharedSpaces, "run-time only");
     int index = hash % _bucket_count;
-    juint bucket_info = _buckets[index];
-    juint bucket_offset = BUCKET_OFFSET(bucket_info);
-    int   bucket_type = BUCKET_TYPE(bucket_info);
-    juint* bucket = _buckets + bucket_offset;
-    juint* bucket_end = _buckets;
+    u4 bucket_info = _buckets[index];
+    u4 bucket_offset = BUCKET_OFFSET(bucket_info);
+    int bucket_type = BUCKET_TYPE(bucket_info);
+    u4* entry = _entries + bucket_offset;
 
-    if (bucket_type == COMPACT_BUCKET_TYPE) {
-      // the compact bucket has one entry with entry offset only
-      T res = lookup_entry(this, &bucket[0], name, len);
+    if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
+      T res = decode_entry(this, entry[0], name, len);
       if (res != NULL) {
         return res;
       }
@@ -74,29 +71,20 @@
       // This is a regular bucket, which has more than one
       // entries. Each entry is a pair of entry (hash, offset).
       // Seek until the end of the bucket.
-      bucket_end += BUCKET_OFFSET(_buckets[index + 1]);
-      while (bucket < bucket_end) {
-        unsigned int h = (unsigned int)(bucket[0]);
+      u4* entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]);
+      while (entry < entry_max) {
+        unsigned int h = (unsigned int)(entry[0]);
         if (h == hash) {
-          T res = lookup_entry(this, &bucket[1], name, len);
+          T res = decode_entry(this, entry[1], name, len);
           if (res != NULL) {
             return res;
           }
         }
-        bucket += 2;
+        entry += 2;
       }
     }
   }
   return NULL;
 }
 
-inline void CompactHashtableWriter::add(unsigned int hash, Symbol* symbol) {
-  add(hash, new Entry(hash, symbol));
-}
-
-inline void CompactHashtableWriter::add(unsigned int hash, oop string) {
-  add(hash, new Entry(hash, string));
-}
-
-
 #endif // SHARE_VM_CLASSFILE_COMPACTHASHTABLE_INLINE_HPP
--- a/hotspot/src/share/vm/classfile/stringTable.cpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/classfile/stringTable.cpp	Sun Apr 17 19:15:52 2016 -0700
@@ -662,7 +662,7 @@
 
 // Sharing
 bool StringTable::copy_shared_string(GrowableArray<MemRegion> *string_space,
-                                     CompactHashtableWriter* ch_table) {
+                                     CompactStringTableWriter* writer) {
 #if INCLUDE_CDS && INCLUDE_ALL_GCS && defined(_LP64) && !defined(_WINDOWS)
   assert(UseG1GC, "Only support G1 GC");
   assert(UseCompressedOops && UseCompressedClassPointers,
@@ -713,7 +713,7 @@
       }
 
       // add to the compact table
-      ch_table->add(hash, new_s);
+      writer->add(hash, new_s);
     }
   }
 
@@ -723,40 +723,41 @@
   return true;
 }
 
-bool StringTable::copy_compact_table(char** top, char *end, GrowableArray<MemRegion> *string_space,
-                                     size_t* space_size) {
+void StringTable::serialize(SerializeClosure* soc, GrowableArray<MemRegion> *string_space,
+                            size_t* space_size) {
 #if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS)
-  if (!(UseG1GC && UseCompressedOops && UseCompressedClassPointers)) {
-    if (PrintSharedSpaces) {
-      tty->print_cr("Shared strings are excluded from the archive as UseG1GC, "
-                    "UseCompressedOops and UseCompressedClassPointers are required.");
+  _shared_table.reset();
+  if (soc->writing()) {
+    if (!(UseG1GC && UseCompressedOops && UseCompressedClassPointers)) {
+      if (PrintSharedSpaces) {
+        tty->print_cr("Shared strings are excluded from the archive as UseG1GC, "
+                      "UseCompressedOops and UseCompressedClassPointers are required.");
+      }
+    } else {
+      int num_buckets = the_table()->number_of_entries() /
+                             SharedSymbolTableBucketSize;
+      CompactStringTableWriter writer(num_buckets,
+                                      &MetaspaceShared::stats()->string);
+
+      // Copy the interned strings into the "string space" within the java heap
+      if (copy_shared_string(string_space, &writer)) {
+        for (int i = 0; i < string_space->length(); i++) {
+          *space_size += string_space->at(i).byte_size();
+        }
+        writer.dump(&_shared_table);
+      }
     }
-    return true;
   }
 
-  CompactHashtableWriter ch_table(CompactHashtable<oop, char>::_string_table,
-                                  the_table()->number_of_entries(),
-                                  &MetaspaceShared::stats()->string);
+  _shared_table.set_type(CompactHashtable<oop, char>::_string_table);
+  _shared_table.serialize(soc);
 
-  // Copy the interned strings into the "string space" within the java heap
-  if (!copy_shared_string(string_space, &ch_table)) {
-    return false;
-  }
-
-  for (int i = 0; i < string_space->length(); i++) {
-    *space_size += string_space->at(i).byte_size();
+  if (soc->writing()) {
+    _shared_table.reset(); // Sanity. Make sure we don't use the shared table at dump time
+  } else if (_ignore_shared_strings) {
+    _shared_table.reset();
   }
-
-  // Now dump the compact table
-  if (*top + ch_table.get_required_bytes() > end) {
-    // not enough space left
-    return false;
-  }
-  ch_table.dump(top, end);
-  *top = (char*)align_ptr_up(*top, sizeof(void*));
-
 #endif
-  return true;
 }
 
 void StringTable::shared_oops_do(OopClosure* f) {
@@ -765,25 +766,3 @@
 #endif
 }
 
-const char* StringTable::init_shared_table(FileMapInfo *mapinfo, char *buffer) {
-#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS)
-  if (mapinfo->space_capacity(MetaspaceShared::first_string) == 0) {
-    // no shared string data
-    return buffer;
-  }
-
-  // initialize the shared table
-  juint *p = (juint*)buffer;
-  const char* end = _shared_table.init(
-          CompactHashtable<oop, char>::_string_table, (char*)p);
-  const char* aligned_end = (const char*)align_ptr_up(end, sizeof(void*));
-
-  if (_ignore_shared_strings) {
-    _shared_table.reset();
-  }
-
-  return aligned_end;
-#endif
-
-  return buffer;
-}
--- a/hotspot/src/share/vm/classfile/stringTable.hpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/classfile/stringTable.hpp	Sun Apr 17 19:15:52 2016 -0700
@@ -29,8 +29,9 @@
 #include "utilities/hashtable.hpp"
 
 template <class T, class N> class CompactHashtable;
-class CompactHashtableWriter;
+class CompactStringTableWriter;
 class FileMapInfo;
+class SerializeClosure;
 
 class StringTable : public RehashableHashtable<oop, mtSymbol> {
   friend class VMStructs;
@@ -155,10 +156,9 @@
   static bool shared_string_ignored()       { return _ignore_shared_strings; }
   static void shared_oops_do(OopClosure* f);
   static bool copy_shared_string(GrowableArray<MemRegion> *string_space,
-                                 CompactHashtableWriter* ch_table);
-  static bool copy_compact_table(char** top, char* end, GrowableArray<MemRegion> *string_space,
-                                 size_t* space_size);
-  static const char* init_shared_table(FileMapInfo *mapinfo, char* buffer);
+                                 CompactStringTableWriter* ch_table);
+  static void serialize(SerializeClosure* soc, GrowableArray<MemRegion> *string_space,
+                        size_t* space_size);
   static void reverse() {
     the_table()->Hashtable<oop, mtSymbol>::reverse();
   }
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp	Sun Apr 17 19:15:52 2016 -0700
@@ -537,37 +537,42 @@
   }
 }
 
-bool SymbolTable::copy_compact_table(char** top, char*end) {
+void SymbolTable::serialize(SerializeClosure* soc) {
 #if INCLUDE_CDS
-  CompactHashtableWriter ch_table(CompactHashtable<Symbol*, char>::_symbol_table,
-                                  the_table()->number_of_entries(),
-                                  &MetaspaceShared::stats()->symbol);
-  if (*top + ch_table.get_required_bytes() > end) {
-    // not enough space left
-    return false;
+  _shared_table.reset();
+  if (soc->writing()) {
+    int num_buckets = the_table()->number_of_entries() /
+                            SharedSymbolTableBucketSize;
+    CompactSymbolTableWriter writer(num_buckets,
+                                    &MetaspaceShared::stats()->symbol);
+    for (int i = 0; i < the_table()->table_size(); ++i) {
+      HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
+      for ( ; p != NULL; p = p->next()) {
+        Symbol* s = (Symbol*)(p->literal());
+      unsigned int fixed_hash =  hash_shared_symbol((char*)s->bytes(), s->utf8_length());
+        assert(fixed_hash == p->hash(), "must not rehash during dumping");
+        writer.add(fixed_hash, s);
+      }
+    }
+
+    writer.dump(&_shared_table);
   }
 
-  for (int i = 0; i < the_table()->table_size(); ++i) {
-    HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
-    for ( ; p != NULL; p = p->next()) {
-      Symbol* s = (Symbol*)(p->literal());
-      unsigned int fixed_hash =  hash_shared_symbol((char*)s->bytes(), s->utf8_length());
-      assert(fixed_hash == p->hash(), "must not rehash during dumping");
-      ch_table.add(fixed_hash, s);
-    }
-  }
+  _shared_table.set_type(CompactHashtable<Symbol*, char>::_symbol_table);
+  _shared_table.serialize(soc);
 
-  ch_table.dump(top, end);
+  if (soc->writing()) {
+    // Verify table is correct
+    Symbol* sym = vmSymbols::java_lang_Object();
+    const char* name = (const char*)sym->bytes();
+    int len = sym->utf8_length();
+    unsigned int hash = hash_symbol(name, len);
+    assert(sym == _shared_table.lookup(name, hash, len), "sanity");
 
-  *top = (char*)align_ptr_up(*top, sizeof(void*));
+    // Sanity. Make sure we don't use the shared table at dump time
+    _shared_table.reset();
+  }
 #endif
-  return true;
-}
-
-const char* SymbolTable::init_shared_table(const char* buffer) {
-  const char* end = _shared_table.init(
-          CompactHashtable<Symbol*, char>::_symbol_table, buffer);
-  return (const char*)align_ptr_up(end, sizeof(void*));
 }
 
 //---------------------------------------------------------------------------
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp	Sun Apr 17 19:15:52 2016 -0700
@@ -41,6 +41,7 @@
 
 class BoolObjectClosure;
 class outputStream;
+class SerializeClosure;
 
 // TempNewSymbol acts as a handle class in a handle/body idiom and is
 // responsible for proper resource management of the body (which is a Symbol*).
@@ -251,8 +252,7 @@
   static void read(const char* filename, TRAPS);
 
   // Sharing
-  static bool copy_compact_table(char** top, char* end);
-  static const char* init_shared_table(const char* buffer);
+  static void serialize(SerializeClosure* soc);
 
   // Rehash the symbol table if it gets out of balance
   static void rehash_table();
--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp	Sun Apr 17 19:15:52 2016 -0700
@@ -29,6 +29,7 @@
 #include "classfile/dictionary.hpp"
 
 class ClassFileStream;
+class SerializeClosure;
 
 class SystemDictionaryShared: public SystemDictionary {
 public:
@@ -77,6 +78,7 @@
                                            TRAPS) {
     return NULL;
   }
+  static void serialize(SerializeClosure* soc) {}
 };
 
 #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
--- a/hotspot/src/share/vm/memory/iterator.hpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/memory/iterator.hpp	Sun Apr 17 19:15:52 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2016, 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
@@ -355,6 +355,9 @@
   // Read/write the void pointer pointed to by p.
   virtual void do_ptr(void** p) = 0;
 
+  // Read/write the 32-bit unsigned integer pointed to by p.
+  virtual void do_u4(u4* p) = 0;
+
   // Read/write the region specified.
   virtual void do_region(u_char* start, size_t size) = 0;
 
@@ -363,6 +366,10 @@
   // for verification that sections of the serialized data are of the
   // correct length.
   virtual void do_tag(int tag) = 0;
+
+  bool writing() {
+    return !reading();
+  }
 };
 
 class SymbolClosure : public StackObj {
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp	Sun Apr 17 19:15:52 2016 -0700
@@ -31,6 +31,7 @@
 #include "classfile/sharedClassUtil.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
 #include "code/codeCache.hpp"
 #include "gc/shared/gcLocker.hpp"
 #include "interpreter/bytecodeStream.hpp"
@@ -106,7 +107,8 @@
 // Read/write a data stream for restoring/preserving metadata pointers and
 // miscellaneous data from/to the shared archive file.
 
-void MetaspaceShared::serialize(SerializeClosure* soc) {
+void MetaspaceShared::serialize(SerializeClosure* soc, GrowableArray<MemRegion> *string_space,
+                                size_t* space_size) {
   int tag = 0;
   soc->do_tag(--tag);
 
@@ -128,6 +130,15 @@
   vmSymbols::serialize(soc);
   soc->do_tag(--tag);
 
+  // Dump/restore the symbol and string tables
+  SymbolTable::serialize(soc);
+  StringTable::serialize(soc, string_space, space_size);
+  soc->do_tag(--tag);
+
+  // Dump/restore the misc information for system dictionary
+  SystemDictionaryShared::serialize(soc);
+  soc->do_tag(--tag);
+
   soc->do_tag(666);
 }
 
@@ -314,6 +325,11 @@
     ++top;
   }
 
+  void do_u4(u4* p) {
+    void* ptr = (void*)(uintx(*p));
+    do_ptr(&ptr);
+  }
+
   void do_tag(int tag) {
     check_space();
     *top = (intptr_t)tag;
@@ -348,6 +364,8 @@
   METASPACE_OBJ_TYPES_DO(f) \
   f(SymbolHashentry) \
   f(SymbolBucket) \
+  f(StringHashentry) \
+  f(StringBucket) \
   f(Other)
 
 #define SHAREDSPACE_OBJ_TYPE_DECLARE(name) name ## Type,
@@ -406,13 +424,22 @@
   MetaspaceSharedStats *stats = MetaspaceShared::stats();
 
   // symbols
-  _counts[RW][SymbolHashentryType] = stats->symbol.hashentry_count;
-  _bytes [RW][SymbolHashentryType] = stats->symbol.hashentry_bytes;
-  other_bytes -= stats->symbol.hashentry_bytes;
+  _counts[RO][SymbolHashentryType] = stats->symbol.hashentry_count;
+  _bytes [RO][SymbolHashentryType] = stats->symbol.hashentry_bytes;
+  _bytes [RO][TypeArrayU4Type]    -= stats->symbol.hashentry_bytes;
+
+  _counts[RO][SymbolBucketType] = stats->symbol.bucket_count;
+  _bytes [RO][SymbolBucketType] = stats->symbol.bucket_bytes;
+  _bytes [RO][TypeArrayU4Type] -= stats->symbol.bucket_bytes;
 
-  _counts[RW][SymbolBucketType] = stats->symbol.bucket_count;
-  _bytes [RW][SymbolBucketType] = stats->symbol.bucket_bytes;
-  other_bytes -= stats->symbol.bucket_bytes;
+  // strings
+  _counts[RO][StringHashentryType] = stats->string.hashentry_count;
+  _bytes [RO][StringHashentryType] = stats->string.hashentry_bytes;
+  _bytes [RO][TypeArrayU4Type]    -= stats->string.hashentry_bytes;
+
+  _counts[RO][StringBucketType] = stats->string.bucket_count;
+  _bytes [RO][StringBucketType] = stats->string.bucket_bytes;
+  _bytes [RO][TypeArrayU4Type] -= stats->string.bucket_bytes;
 
   // TODO: count things like dictionary, vtable, etc
   _bytes[RW][OtherType] =  other_bytes;
@@ -488,7 +515,6 @@
   GrowableArray<Klass*> *_class_promote_order;
   VirtualSpace _md_vs;
   VirtualSpace _mc_vs;
-  CompactHashtableWriter* _string_cht;
   GrowableArray<MemRegion> *_string_regions;
 
 public:
@@ -600,39 +626,27 @@
   // Not doing this either.
 
   SystemDictionary::reorder_dictionary();
-
   NOT_PRODUCT(SystemDictionary::verify();)
-
-  // Copy the symbol table, string table, and the system dictionary to the shared
-  // space in usable form.  Copy the hashtable
-  // buckets first [read-write], then copy the linked lists of entries
-  // [read-only].
-
-  NOT_PRODUCT(SymbolTable::verify());
-  handle_misc_data_space_failure(SymbolTable::copy_compact_table(&md_top, md_end));
-
-  size_t ss_bytes = 0;
-  char* ss_low;
-  // The string space has maximum two regions. See FileMapInfo::write_string_regions() for details.
-  _string_regions = new GrowableArray<MemRegion>(2);
-  NOT_PRODUCT(StringTable::verify());
-  handle_misc_data_space_failure(StringTable::copy_compact_table(&md_top, md_end, _string_regions,
-                                                                 &ss_bytes));
-  ss_low = _string_regions->is_empty() ? NULL : (char*)_string_regions->first().start();
-
   SystemDictionary::reverse();
   SystemDictionary::copy_buckets(&md_top, md_end);
 
   SystemDictionary::copy_table(&md_top, md_end);
 
   // Write the other data to the output array.
+  // SymbolTable, StringTable and extra information for system dictionary
+  NOT_PRODUCT(SymbolTable::verify());
+  NOT_PRODUCT(StringTable::verify());
+  size_t ss_bytes = 0;
+  char* ss_low;
+  // The string space has maximum two regions. See FileMapInfo::write_string_regions() for details.
+  _string_regions = new GrowableArray<MemRegion>(2);
+
   WriteClosure wc(md_top, md_end);
-  MetaspaceShared::serialize(&wc);
+  MetaspaceShared::serialize(&wc, _string_regions, &ss_bytes);
   md_top = wc.get_top();
+  ss_low = _string_regions->is_empty() ? NULL : (char*)_string_regions->first().start();
 
   // Print shared spaces all the time
-// To make fmt_space be a syntactic constant (for format warnings), use #define.
-#define fmt_space "%s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%4.1f%% used] at " INTPTR_FORMAT
   Metaspace* ro_space = _loader_data->ro_metaspace();
   Metaspace* rw_space = _loader_data->rw_metaspace();
 
@@ -665,12 +679,13 @@
   const double mc_u_perc = mc_bytes / double(mc_alloced) * 100.0;
   const double total_u_perc = total_bytes / double(total_alloced) * 100.0;
 
+#define fmt_space "%s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used] at " INTPTR_FORMAT
   tty->print_cr(fmt_space, "ro", ro_bytes, ro_t_perc, ro_alloced, ro_u_perc, p2i(ro_space->bottom()));
   tty->print_cr(fmt_space, "rw", rw_bytes, rw_t_perc, rw_alloced, rw_u_perc, p2i(rw_space->bottom()));
   tty->print_cr(fmt_space, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, p2i(md_low));
   tty->print_cr(fmt_space, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, p2i(mc_low));
   tty->print_cr(fmt_space, "st", ss_bytes, ss_t_perc, ss_bytes,   100.0,     p2i(ss_low));
-  tty->print_cr("total   : " SIZE_FORMAT_W(9) " [100.0%% of total] out of " SIZE_FORMAT_W(9) " bytes [%4.1f%% used]",
+  tty->print_cr("total   : " SIZE_FORMAT_W(9) " [100.0%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used]",
                  total_bytes, total_alloced, total_u_perc);
 
   // Update the vtable pointers in all of the Klass objects in the
@@ -974,6 +989,11 @@
     *p = (void*)obj;
   }
 
+  void do_u4(u4* p) {
+    intptr_t obj = nextPtr();
+    *p = (u4)(uintx(obj));
+  }
+
   void do_tag(int tag) {
     int old_tag;
     old_tag = (int)(intptr_t)nextPtr();
@@ -1097,21 +1117,6 @@
   buffer += sizeof(intptr_t);
   buffer += vtable_size;
 
-  // Create the shared symbol table using the compact table at this spot in the
-  // misc data space. (Todo: move this to read-only space. Currently
-  // this is mapped copy-on-write but will never be written into).
-
-  buffer = (char*)SymbolTable::init_shared_table(buffer);
-  SymbolTable::create_table();
-
-  // Create the shared string table using the compact table
-  buffer = (char*)StringTable::init_shared_table(mapinfo, buffer);
-
-  // Create the shared dictionary using the bucket array at this spot in
-  // the misc data space.  Since the shared dictionary table is never
-  // modified, this region (of mapped pages) will be (effectively, if
-  // not explicitly) read-only.
-
   int sharedDictionaryLen = *(intptr_t*)buffer;
   buffer += sizeof(intptr_t);
   int number_of_entries = *(intptr_t*)buffer;
@@ -1129,9 +1134,14 @@
   buffer += sizeof(intptr_t);
   buffer += len;
 
+  // Verify various attributes of the archive, plus initialize the
+  // shared string/symbol tables
   intptr_t* array = (intptr_t*)buffer;
   ReadClosure rc(&array);
-  serialize(&rc);
+  serialize(&rc, NULL, NULL);
+
+  // Initialize the run-time symbol table.
+  SymbolTable::create_table();
 
   // Close the mapinfo file
   mapinfo->close();
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp	Sun Apr 17 19:15:52 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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
@@ -47,7 +47,7 @@
 #define MIN_SHARED_READ_WRITE_SIZE      (NOT_LP64(7*M) LP64_ONLY(12*M))
 
 #define DEFAULT_SHARED_READ_ONLY_SIZE   (NOT_LP64(12*M) LP64_ONLY(16*M))
-#define MIN_SHARED_READ_ONLY_SIZE       (NOT_LP64(8*M) LP64_ONLY(9*M))
+#define MIN_SHARED_READ_ONLY_SIZE       (NOT_LP64(9*M) LP64_ONLY(10*M))
 
 // the MIN_SHARED_MISC_DATA_SIZE and MIN_SHARED_MISC_CODE_SIZE estimates are based on
 // the sizes required for dumping the archive using the default classlist. The sizes
@@ -193,7 +193,8 @@
                                       void** vtable,
                                       char** md_top, char* md_end,
                                       char** mc_top, char* mc_end);
-  static void serialize(SerializeClosure* sc);
+  static void serialize(SerializeClosure* sc, GrowableArray<MemRegion> *string_space,
+                        size_t* space_size);
 
   static MetaspaceSharedStats* stats() {
     return &_stats;
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp	Sun Apr 17 19:15:52 2016 -0700
@@ -662,11 +662,11 @@
   /* CompactHashTable */                                                                                                             \
   /********************/                                                                                                             \
                                                                                                                                      \
-  nonstatic_field(SymbolCompactHashTable,      _base_address,                                 uintx)                                 \
-  nonstatic_field(SymbolCompactHashTable,      _entry_count,                                  juint)                                 \
-  nonstatic_field(SymbolCompactHashTable,      _bucket_count,                                 juint)                                 \
-  nonstatic_field(SymbolCompactHashTable,      _table_end_offset,                             juint)                                 \
-  nonstatic_field(SymbolCompactHashTable,      _buckets,                                      juint*)                                \
+  nonstatic_field(SymbolCompactHashTable,      _base_address,                                 address)                               \
+  nonstatic_field(SymbolCompactHashTable,      _entry_count,                                  u4)                                    \
+  nonstatic_field(SymbolCompactHashTable,      _bucket_count,                                 u4)                                    \
+  nonstatic_field(SymbolCompactHashTable,      _buckets,                                      u4*)                                   \
+  nonstatic_field(SymbolCompactHashTable,      _entries,                                      u4*)                                   \
                                                                                                                                      \
   /********************/                                                                                                             \
   /* SystemDictionary */                                                                                                             \
--- a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java	Tue Apr 19 11:03:37 2016 -0400
+++ b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java	Sun Apr 17 19:15:52 2016 -0700
@@ -125,7 +125,7 @@
 
         // test with sizes which just meet the minimum required sizes
         // the following tests also attempt to use the shared archive
-        new SharedSizeTestData(Region.RO, Platform.is64bit() ? "9M":"8M", Result.VALID_ARCHIVE),
+        new SharedSizeTestData(Region.RO, Platform.is64bit() ? "10M":"9M", Result.VALID_ARCHIVE),
         new SharedSizeTestData(Region.RW, Platform.is64bit() ? "12M":"7M", Result.VALID_ARCHIVE),
         new SharedSizeTestData(Region.MD, Platform.is64bit() ? "4M":"2M", Result.VALID_ARCHIVE),
         new SharedSizeTestData(Region.MC, "120k", Result.VALID_ARCHIVE),
@@ -176,7 +176,7 @@
                                output.getOutput().contains("Unable to reserve shared space at required address")) &&
                                output.getExitValue() == 1) {
                                System.out.println("Unable to use shared archive: test not executed; assumed passed");
-                               return;
+                               continue;
                           }
                       }
                       output.shouldHaveExitValue(0);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/SASymbolTableTest.java	Sun Apr 17 19:15:52 2016 -0700
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2016, 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 SASymbolTableTest
+ * @summary Walk symbol table using SA, with and without CDS.
+ * @library /testlibrary
+ * @modules java.base/jdk.internal.misc
+ *          jdk.hotspot.agent/sun.jvm.hotspot.oops
+ *          jdk.hotspot.agent/sun.jvm.hotspot.memory
+ *          jdk.hotspot.agent/sun.jvm.hotspot.runtime
+ *          jdk.hotspot.agent/sun.jvm.hotspot.tools
+ *          java.management
+ * @build SASymbolTableTestAgent SASymbolTableTestAttachee jdk.test.lib.*
+ * @run main SASymbolTableTest
+ */
+
+import jdk.test.lib.*;
+
+/*
+ * The purpose of this test is to validate that we can use SA to
+ * attach a process and walk its SymbolTable, regardless whether
+ * the attachee process runs in CDS mode or not.
+ *
+ * SASymbolTableTest Just sets up the agent and attachee processes.
+ * The SymbolTable walking is done in the SASymbolTableTestAgent class.
+ */
+public class SASymbolTableTest {
+    static String jsaName = "./SASymbolTableTest.jsa";
+
+    public static void main(String[] args) throws Exception {
+        if (!Platform.shouldSAAttach()) {
+            System.out.println("SA attach not expected to work - test skipped.");
+            return;
+        }
+        createArchive();
+        run(true);
+        run(false);
+    }
+
+    private static void createArchive()  throws Exception {
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "-XX:SharedArchiveFile=" + jsaName,
+            "-Xshare:dump");
+
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        output.shouldContain("Loading classes to share");
+        output.shouldHaveExitValue(0);
+    }
+
+    private static void run(boolean useArchive) throws Exception {
+        String flag = useArchive ? "auto" : "off";
+
+        // (1) Launch the attachee process
+        ProcessBuilder attachee = ProcessTools.createJavaProcessBuilder(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "-XX:SharedArchiveFile=" + jsaName,
+            "-Xshare:" + flag,
+            "-showversion",                // so we can see "sharing" in the output
+            "SASymbolTableTestAttachee");
+
+        final Process p = attachee.start();
+
+        // (2) Launch the agent process
+        long pid = p.getPid();
+        System.out.println("Attaching agent " + pid);
+        ProcessBuilder tool = ProcessTools.createJavaProcessBuilder(
+            "-XaddExports:jdk.hotspot.agent/sun.jvm.hotspot.oops=ALL-UNNAMED",
+            "-XaddExports:jdk.hotspot.agent/sun.jvm.hotspot.memory=ALL-UNNAMED",
+            "-XaddExports:jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED",
+            "-XaddExports:jdk.hotspot.agent/sun.jvm.hotspot.tools=ALL-UNNAMED",
+            "SASymbolTableTestAgent",
+            Long.toString(pid));
+        OutputAnalyzer output = ProcessTools.executeProcess(tool);
+        System.out.println(output.getOutput());
+        output.shouldHaveExitValue(0);
+
+        Thread t = new Thread() {
+                public void run() {
+                    try {
+                        OutputAnalyzer output = new OutputAnalyzer(p);
+                        System.out.println("STDOUT[");
+                        System.out.print(output.getStdout());
+                        System.out.println("]");
+                        System.out.println("STDERR[");
+                        System.out.print(output.getStderr());
+                        System.out.println("]");
+                    } catch (Throwable t) {
+                        t.printStackTrace();
+                    }
+                }
+            };
+        t.start();
+
+        Thread.sleep(2 * 1000);
+        p.destroy();
+        t.join();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/SASymbolTableTestAgent.java	Sun Apr 17 19:15:52 2016 -0700
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+import sun.jvm.hotspot.memory.SymbolTable;
+import sun.jvm.hotspot.oops.Symbol;
+import sun.jvm.hotspot.runtime.VM;
+import sun.jvm.hotspot.tools.Tool;
+
+/**
+ * This class is launched in a sub-process by the main test,
+ * SASymbolTableTest.java.
+ *
+ * It uses SA to connect to another JVM process, whose PID is specified in args[].
+ * The purpose of the test is to validate that we can walk the SymbolTable
+ * and CompactHashTable of the other process. Everything should work regardless
+ * of whether the other process runs in CDS mode or not.
+ *
+ * Note: CompactHashTable is used only when CDS is enabled.
+ */
+public class SASymbolTableTestAgent extends Tool {
+    public SASymbolTableTestAgent() {
+        super();
+    }
+    public static void main(String args[]) {
+        SASymbolTableTestAgent tool = new SASymbolTableTestAgent();
+        tool.execute(args);
+    }
+
+    static String[] commonNames = {
+        "java/lang/Object",
+        "java/lang/String",
+        "java/lang/Class",
+        "java/lang/Cloneable",
+        "java/lang/ClassLoader",
+        "java/io/Serializable",
+        "java/lang/System",
+        "java/lang/Throwable",
+        "java/lang/Error",
+        "java/lang/ThreadDeath",
+        "java/lang/Exception",
+        "java/lang/RuntimeException",
+        "java/lang/SecurityManager",
+        "java/security/ProtectionDomain",
+        "java/security/AccessControlContext",
+        "java/security/SecureClassLoader",
+        "java/lang/ClassNotFoundException",
+        "java/lang/NoClassDefFoundError",
+        "java/lang/LinkageError",
+        "java/lang/ClassCastException",
+        "java/lang/ArrayStoreException",
+        "java/lang/VirtualMachineError",
+        "java/lang/OutOfMemoryError",
+        "java/lang/StackOverflowError",
+        "java/lang/IllegalMonitorStateException",
+        "java/lang/ref/Reference",
+        "java/lang/ref/SoftReference",
+        "java/lang/ref/WeakReference",
+        "java/lang/ref/FinalReference",
+        "java/lang/ref/PhantomReference",
+        "java/lang/ref/Finalizer",
+        "java/lang/Thread",
+        "java/lang/ThreadGroup",
+        "java/util/Properties",
+        "java/lang/reflect/AccessibleObject",
+        "java/lang/reflect/Field",
+        "java/lang/reflect/Method",
+        "java/lang/reflect/Constructor",
+        "java/lang/invoke/MethodHandle",
+        "java/lang/invoke/MemberName",
+        "java/lang/invoke/MethodHandleNatives",
+        "java/lang/invoke/MethodType",
+        "java/lang/BootstrapMethodError",
+        "java/lang/invoke/CallSite",
+        "java/lang/invoke/ConstantCallSite",
+        "java/lang/invoke/MutableCallSite",
+        "java/lang/invoke/VolatileCallSite",
+        "java/lang/StringBuffer",
+        "java/lang/StringBuilder",
+        "java/io/ByteArrayInputStream",
+        "java/io/File",
+        "java/net/URLClassLoader",
+        "java/net/URL",
+        "java/util/jar/Manifest",
+        "java/security/CodeSource",
+    };
+
+    static String[] badNames = {
+        "java/lang/badbadbad",
+        "java/io/badbadbadbad",
+        "this*symbol*must*not*exist"
+    };
+
+    public void run() {
+        System.out.println("SASymbolTableTestAgent: starting");
+        VM vm = VM.getVM();
+        SymbolTable table = vm.getSymbolTable();
+
+        // (a) These are names that are likely to exist in the symbol table
+        //     of a JVM after start-up. They were taken from vmSymbols.hpp
+        //     during the middle of JDK9 development.
+        //
+        //     The purpose is not to check that each name must exist (a future
+        //     version of JDK may not preload some of the classes).
+        //
+        //     The purpose of this loops is to ensure that we check a lot of symbols,
+        //     so we will (most likely) hit on both VALUE_ONLY_BUCKET_TYPE and normal bucket type
+        //     in CompactHashTable.probe().
+        for (String n : commonNames) {
+            Symbol s = table.probe(n);
+            System.out.format("%-40s = %s\n", n, s);
+        }
+
+        System.out.println("======================================================================");
+
+        // (b) Also test a few strings that are known to not exist in the table. This will
+        //     both the compact table (if it exists) and the regular table to be walked.
+        for (String n : badNames) {
+            Symbol s = table.probe(n);
+            System.out.format("%-40s = %s\n", n, s);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/SASymbolTableTestAttachee.java	Sun Apr 17 19:15:52 2016 -0700
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+/**
+ * This class is launched in a sub-process by the main test,
+ * SASymbolTableTest.java.
+ *
+ * This class does nothing in particular. It just sleeps for 120
+ * seconds so SASymbolTableTestAgent can have a chance to examine its
+ * SymbolTable. This process should be killed by the parent process
+ * after SASymbolTableTestAgent has completed testing.
+ */
+public class SASymbolTableTestAttachee {
+    public static void main(String args[]) throws Throwable {
+        System.out.println("SASymbolTableTestAttachee: sleeping to wait for SA tool to attach ...");
+        Thread.sleep(120 * 1000);
+    }
+}