changeset 51405:8b23aa7cef47

8195100: Use a low latency hashtable for SymbolTable Summary: Used concurrentHashTable, similar to stringTable Reviewed-by: coleenp, kbarrett, iklam, pliden
author gziemski
date Tue, 14 Aug 2018 18:42:14 -0500
parents c5461fe16efb
children f4b4dfac45b1
files src/hotspot/share/classfile/classLoaderData.cpp src/hotspot/share/classfile/compactHashtable.hpp src/hotspot/share/classfile/stringTable.cpp src/hotspot/share/classfile/stringTable.hpp src/hotspot/share/classfile/symbolTable.cpp src/hotspot/share/classfile/symbolTable.hpp src/hotspot/share/gc/g1/g1CollectedHeap.cpp src/hotspot/share/gc/g1/g1CollectedHeap.hpp src/hotspot/share/gc/g1/g1ConcurrentMark.cpp src/hotspot/share/gc/g1/g1FullCollector.cpp src/hotspot/share/gc/z/zRootsIterator.cpp src/hotspot/share/gc/z/zRootsIterator.hpp src/hotspot/share/gc/z/z_globals.hpp src/hotspot/share/logging/logPrefix.hpp src/hotspot/share/logging/logTag.hpp src/hotspot/share/memory/arena.hpp src/hotspot/share/oops/symbol.cpp src/hotspot/share/oops/symbol.hpp src/hotspot/share/runtime/serviceThread.cpp src/hotspot/share/runtime/vmStructs.cpp src/hotspot/share/utilities/concurrentHashTable.hpp src/hotspot/share/utilities/concurrentHashTable.inline.hpp src/hotspot/share/utilities/globalCounter.cpp src/hotspot/share/utilities/globalDefinitions.hpp test/hotspot/jtreg/gc/g1/TestStringSymbolTableStats.java test/hotspot/jtreg/gc/g1/TestStringTableStats.java test/hotspot/jtreg/runtime/symboltable/ShortLivedSymbolCleanup.java
diffstat 27 files changed, 1158 insertions(+), 862 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/classfile/classLoaderData.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/classfile/classLoaderData.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -1418,6 +1418,28 @@
   }
 
   if (seen_dead_loader) {
+    data = _head;
+    while (data != NULL) {
+      // Remove entries in the dictionary of live class loader that have
+      // initiated loading classes in a dead class loader.
+      if (data->dictionary() != NULL) {
+        data->dictionary()->do_unloading();
+      }
+      // Walk a ModuleEntry's reads, and a PackageEntry's exports
+      // lists to determine if there are modules on those lists that are now
+      // dead and should be removed.  A module's life cycle is equivalent
+      // to its defining class loader's life cycle.  Since a module is
+      // considered dead if its class loader is dead, these walks must
+      // occur after each class loader's aliveness is determined.
+      if (data->packages() != NULL) {
+        data->packages()->purge_all_package_exports();
+      }
+      if (data->modules_defined()) {
+        data->modules()->purge_all_module_reads();
+      }
+      data = data->next();
+    }
+    SymbolTable::do_check_concurrent_work();
     JFR_ONLY(post_class_unload_events();)
   }
 
--- a/src/hotspot/share/classfile/compactHashtable.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/classfile/compactHashtable.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -231,6 +231,10 @@
 
   // For reading from/writing to the CDS archive
   void serialize(SerializeClosure* soc);
+
+  inline bool empty() {
+    return (_entry_count == 0);
+  }
 };
 
 template <class T, class N> class CompactHashtable : public SimpleCompactHashtable {
--- a/src/hotspot/share/classfile/stringTable.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/classfile/stringTable.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -64,9 +64,9 @@
 
 // --------------------------------------------------------------------------
 StringTable* StringTable::_the_table = NULL;
-bool StringTable::_shared_string_mapped = false;
 CompactHashtable<oop, char> StringTable::_shared_table;
-bool StringTable::_alt_hash = false;
+volatile bool StringTable::_shared_string_mapped = false;
+volatile bool StringTable::_alt_hash = false;
 
 static juint murmur_seed = 0;
 
@@ -176,18 +176,18 @@
   }
 };
 
-static size_t ceil_pow_2(uintx val) {
+static size_t ceil_log2(size_t val) {
   size_t ret;
   for (ret = 1; ((size_t)1 << ret) < val; ++ret);
   return ret;
 }
 
 StringTable::StringTable() : _local_table(NULL), _current_size(0), _has_work(0),
-  _needs_rehashing(false), _weak_handles(NULL), _items(0), _uncleaned_items(0) {
+  _needs_rehashing(false), _weak_handles(NULL), _items_count(0), _uncleaned_items_count(0) {
   _weak_handles = new OopStorage("StringTable weak",
                                  StringTableWeakAlloc_lock,
                                  StringTableWeakActive_lock);
-  size_t start_size_log_2 = ceil_pow_2(StringTableSize);
+  size_t start_size_log_2 = ceil_log2(StringTableSize);
   _current_size = ((size_t)1) << start_size_log_2;
   log_trace(stringtable)("Start size: " SIZE_FORMAT " (" SIZE_FORMAT ")",
                          _current_size, start_size_log_2);
@@ -195,32 +195,31 @@
 }
 
 size_t StringTable::item_added() {
-  return Atomic::add((size_t)1, &(the_table()->_items));
+  return Atomic::add((size_t)1, &(the_table()->_items_count));
 }
 
-size_t StringTable::add_items_to_clean(size_t ndead) {
-  size_t total = Atomic::add((size_t)ndead, &(the_table()->_uncleaned_items));
+size_t StringTable::add_items_count_to_clean(size_t ndead) {
+  size_t total = Atomic::add((size_t)ndead, &(the_table()->_uncleaned_items_count));
   log_trace(stringtable)(
      "Uncleaned items:" SIZE_FORMAT " added: " SIZE_FORMAT " total:" SIZE_FORMAT,
-     the_table()->_uncleaned_items, ndead, total);
+     the_table()->_uncleaned_items_count, ndead, total);
   return total;
 }
 
 void StringTable::item_removed() {
-  Atomic::add((size_t)-1, &(the_table()->_items));
+  Atomic::add((size_t)-1, &(the_table()->_items_count));
 }
 
 double StringTable::get_load_factor() {
-  return (_items*1.0)/_current_size;
+  return (double)_items_count/_current_size;
 }
 
 double StringTable::get_dead_factor() {
-  return (_uncleaned_items*1.0)/_current_size;
+  return (double)_uncleaned_items_count/_current_size;
 }
 
-size_t StringTable::table_size(Thread* thread) {
-  return ((size_t)(1)) << _local_table->get_size_log2(thread != NULL ? thread
-                                                      : Thread::current());
+size_t StringTable::table_size() {
+  return ((size_t)1) << _local_table->get_size_log2(Thread::current());
 }
 
 void StringTable::trigger_concurrent_work() {
@@ -406,7 +405,7 @@
 
   // This is the serial case without ParState.
   // Just set the correct number and check for a cleaning phase.
-  the_table()->_uncleaned_items = stiac._count;
+  the_table()->_uncleaned_items_count = stiac._count;
   StringTable::the_table()->check_concurrent_work();
 
   if (processed != NULL) {
@@ -433,7 +432,7 @@
   _par_state_string->weak_oops_do(&stiac, &dnc);
 
   // Accumulate the dead strings.
-  the_table()->add_items_to_clean(stiac._count);
+  the_table()->add_items_count_to_clean(stiac._count);
 
   *processed = (int) stiac._count_total;
   *removed = (int) stiac._count;
@@ -465,7 +464,7 @@
     }
   }
   gt.done(jt);
-  _current_size = table_size(jt);
+  _current_size = table_size();
   log_debug(stringtable)("Grown to size:" SIZE_FORMAT, _current_size);
 }
 
@@ -843,7 +842,7 @@
   assert(MetaspaceShared::is_heap_object_archiving_allowed(), "must be");
 
   _shared_table.reset();
-  int num_buckets = the_table()->_items / SharedSymbolTableBucketSize;
+  int num_buckets = the_table()->_items_count / SharedSymbolTableBucketSize;
   // calculation of num_buckets can result in zero buckets, we need at least one
   CompactStringTableWriter writer(num_buckets > 1 ? num_buckets : 1,
                                   &MetaspaceShared::stats()->string);
--- a/src/hotspot/share/classfile/stringTable.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/classfile/stringTable.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -58,21 +58,22 @@
   static StringTable* _the_table;
   // Shared string table
   static CompactHashtable<oop, char> _shared_table;
-  static bool _shared_string_mapped;
-  static bool _alt_hash;
+  static volatile bool _shared_string_mapped;
+  static volatile bool _alt_hash;
+
 private:
 
-   // Set if one bucket is out of balance due to hash algorithm deficiency
   StringTableHash* _local_table;
   size_t _current_size;
   volatile bool _has_work;
+  // Set if one bucket is out of balance due to hash algorithm deficiency
   volatile bool _needs_rehashing;
 
   OopStorage* _weak_handles;
 
-  volatile size_t _items;
+  volatile size_t _items_count;
   DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(volatile size_t));
-  volatile size_t _uncleaned_items;
+  volatile size_t _uncleaned_items_count;
   DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(volatile size_t));
 
   double get_load_factor();
@@ -83,7 +84,7 @@
 
   static size_t item_added();
   static void item_removed();
-  size_t add_items_to_clean(size_t ndead);
+  size_t add_items_count_to_clean(size_t ndead);
 
   StringTable();
 
@@ -100,7 +101,7 @@
  public:
   // The string table
   static StringTable* the_table() { return _the_table; }
-  size_t table_size(Thread* thread = NULL);
+  size_t table_size();
 
   static OopStorage* weak_storage() { return the_table()->_weak_handles; }
 
@@ -116,7 +117,7 @@
 
   // Must be called before a parallel walk where strings might die.
   static void reset_dead_counter() {
-    the_table()->_uncleaned_items = 0;
+    the_table()->_uncleaned_items_count = 0;
   }
   // After the parallel walk this method must be called to trigger
   // cleaning. Note it might trigger a resize instead.
@@ -127,7 +128,7 @@
   // If GC uses ParState directly it should add the number of cleared
   // strings to this method.
   static void inc_dead_counter(size_t ndead) {
-    the_table()->add_items_to_clean(ndead);
+    the_table()->add_items_count_to_clean(ndead);
   }
 
   //   Delete pointers to otherwise-unreachable objects.
--- a/src/hotspot/share/classfile/symbolTable.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/classfile/symbolTable.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -27,46 +27,178 @@
 #include "classfile/compactHashtable.inline.hpp"
 #include "classfile/javaClasses.hpp"
 #include "classfile/symbolTable.hpp"
-#include "classfile/systemDictionary.hpp"
-#include "gc/shared/collectedHeap.inline.hpp"
 #include "memory/allocation.inline.hpp"
-#include "memory/filemap.hpp"
 #include "memory/metaspaceClosure.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/atomic.hpp"
-#include "runtime/mutexLocker.hpp"
-#include "runtime/safepointVerifiers.hpp"
+#include "runtime/interfaceSupport.inline.hpp"
+#include "runtime/timerTrace.hpp"
 #include "services/diagnosticCommand.hpp"
-#include "utilities/hashtable.inline.hpp"
+#include "utilities/concurrentHashTable.inline.hpp"
+#include "utilities/concurrentHashTableTasks.inline.hpp"
+
+// We used to not resize at all, so let's be conservative
+// and not set it too short before we decide to resize,
+// to match previous startup behavior
+#define PREF_AVG_LIST_LEN           8
+// 2^17 (131,072) is max size, which is about 6.5 times as large
+// as the previous table size (used to be 20,011),
+// which never resized
+#define END_SIZE                    17
+// If a chain gets to 100 something might be wrong
+#define REHASH_LEN                  100
+// We only get a chance to check whether we need
+// to clean infrequently (on class unloading),
+// so if we have even one dead entry then mark table for cleaning
+#define CLEAN_DEAD_HIGH_WATER_MARK  0.0
+
+#define ON_STACK_BUFFER_LENGTH 128
 
 // --------------------------------------------------------------------------
-// the number of buckets a thread claims
-const int ClaimChunkSize = 32;
-
 SymbolTable* SymbolTable::_the_table = NULL;
+CompactHashtable<Symbol*, char> SymbolTable::_shared_table;
+volatile bool SymbolTable::_alt_hash = false;
+volatile bool SymbolTable::_lookup_shared_first = false;
 // Static arena for symbols that are not deallocated
 Arena* SymbolTable::_arena = NULL;
-bool SymbolTable::_needs_rehashing = false;
-bool SymbolTable::_lookup_shared_first = false;
 
-CompactHashtable<Symbol*, char> SymbolTable::_shared_table;
+static juint murmur_seed = 0;
 
-Symbol* SymbolTable::allocate_symbol(const u1* name, int len, bool c_heap, TRAPS) {
+static inline void log_trace_symboltable_helper(Symbol* sym, const char* msg) {
+#ifndef PRODUCT
+  ResourceMark rm;
+  log_trace(symboltable)("%s [%s]", msg, sym->as_quoted_ascii());
+#endif // PRODUCT
+}
+
+// Pick hashing algorithm.
+static uintx hash_symbol(const char* s, int len, bool useAlt) {
+  return useAlt ?
+  AltHashing::murmur3_32(murmur_seed, (const jbyte*)s, len) :
+  java_lang_String::hash_code((const jbyte*)s, len);
+}
+
+static uintx hash_shared_symbol(const char* s, int len) {
+  return java_lang_String::hash_code((const jbyte*)s, len);
+}
+
+class SymbolTableConfig : public SymbolTableHash::BaseConfig {
+private:
+public:
+  static uintx get_hash(Symbol* const& value, bool* is_dead) {
+    *is_dead = (value->refcount() == 0);
+    if (*is_dead) {
+      return 0;
+    } else {
+      return hash_symbol((const char*)value->bytes(), value->utf8_length(), SymbolTable::_alt_hash);
+    }
+  }
+  // We use default allocation/deallocation but counted
+  static void* allocate_node(size_t size, Symbol* const& value) {
+    SymbolTable::item_added();
+    return SymbolTableHash::BaseConfig::allocate_node(size, value);
+  }
+  static void free_node(void* memory, Symbol* const& value) {
+    // We get here either because #1 some threads lost a race
+    // to insert a newly created Symbol, or #2 we are freeing
+    // a symbol during normal cleanup deletion.
+    // If #1, then the symbol can be a permanent (refcount==PERM_REFCOUNT),
+    // or regular newly created one but with refcount==0 (see SymbolTableCreateEntry)
+    // If #2, then the symbol must have refcount==0
+    assert((value->refcount() == PERM_REFCOUNT) || (value->refcount() == 0),
+           "refcount %d", value->refcount());
+    SymbolTable::delete_symbol(value);
+    SymbolTableHash::BaseConfig::free_node(memory, value);
+    SymbolTable::item_removed();
+  }
+};
+
+static size_t ceil_log2(size_t value) {
+  size_t ret;
+  for (ret = 1; ((size_t)1 << ret) < value; ++ret);
+  return ret;
+}
+
+SymbolTable::SymbolTable() :
+  _symbols_removed(0), _symbols_counted(0), _local_table(NULL),
+  _current_size(0), _has_work(0), _needs_rehashing(false),
+  _items_count(0), _uncleaned_items_count(0) {
+
+  size_t start_size_log_2 = ceil_log2(SymbolTableSize);
+  _current_size = ((size_t)1) << start_size_log_2;
+  log_trace(symboltable)("Start size: " SIZE_FORMAT " (" SIZE_FORMAT ")",
+                         _current_size, start_size_log_2);
+  _local_table = new SymbolTableHash(start_size_log_2, END_SIZE, REHASH_LEN);
+}
+
+void SymbolTable::delete_symbol(Symbol* sym) {
+  if (sym->refcount() == PERM_REFCOUNT) {
+    MutexLocker ml(SymbolTable_lock); // Protect arena
+    // Deleting permanent symbol should not occur very often (insert race condition),
+    // so log it.
+    log_trace_symboltable_helper(sym, "Freeing permanent symbol");
+    if (!arena()->Afree(sym, sym->size())) {
+      log_trace_symboltable_helper(sym, "Leaked permanent symbol");
+    }
+  } else {
+    delete sym;
+  }
+}
+
+void SymbolTable::item_added() {
+  Atomic::inc(&(SymbolTable::the_table()->_items_count));
+}
+
+void SymbolTable::set_item_clean_count(size_t ncl) {
+  Atomic::store(ncl, &(SymbolTable::the_table()->_uncleaned_items_count));
+  log_trace(symboltable)("Set uncleaned items:" SIZE_FORMAT, SymbolTable::the_table()->_uncleaned_items_count);
+}
+
+void SymbolTable::mark_item_clean_count() {
+  if (Atomic::cmpxchg((size_t)1, &(SymbolTable::the_table()->_uncleaned_items_count), (size_t)0) == 0) { // only mark if unset
+    log_trace(symboltable)("Marked uncleaned items:" SIZE_FORMAT, SymbolTable::the_table()->_uncleaned_items_count);
+  }
+}
+
+void SymbolTable::item_removed() {
+  Atomic::inc(&(SymbolTable::the_table()->_symbols_removed));
+  Atomic::dec(&(SymbolTable::the_table()->_items_count));
+}
+
+double SymbolTable::get_load_factor() {
+  return (double)_items_count/_current_size;
+}
+
+double SymbolTable::get_dead_factor() {
+  return (double)_uncleaned_items_count/_current_size;
+}
+
+size_t SymbolTable::table_size() {
+  return ((size_t)1) << _local_table->get_size_log2(Thread::current());
+}
+
+void SymbolTable::trigger_concurrent_work() {
+  MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
+  SymbolTable::the_table()->_has_work = true;
+  Service_lock->notify_all();
+}
+
+Symbol* SymbolTable::allocate_symbol(const char* name, int len, bool c_heap, TRAPS) {
   assert (len <= Symbol::max_length(), "should be checked by caller");
 
   Symbol* sym;
-
   if (DumpSharedSpaces) {
     c_heap = false;
   }
   if (c_heap) {
     // refcount starts as 1
-    sym = new (len, THREAD) Symbol(name, len, 1);
+    sym = new (len, THREAD) Symbol((const u1*)name, len, 1);
     assert(sym != NULL, "new should call vm_exit_out_of_memory if C_HEAP is exhausted");
   } else {
     // Allocate to global arena
-    sym = new (len, arena(), THREAD) Symbol(name, len, PERM_REFCOUNT);
+    MutexLocker ml(SymbolTable_lock); // Protect arena
+    sym = new (len, arena(), THREAD) Symbol((const u1*)name, len, PERM_REFCOUNT);
   }
   return sym;
 }
@@ -80,314 +212,176 @@
   }
 }
 
+class SymbolsDo : StackObj {
+  SymbolClosure *_cl;
+public:
+  SymbolsDo(SymbolClosure *cl) : _cl(cl) {}
+  bool operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    _cl->do_symbol(value);
+    return true;
+  };
+};
+
 // Call function for all symbols in the symbol table.
 void SymbolTable::symbols_do(SymbolClosure *cl) {
   // all symbols from shared table
   _shared_table.symbols_do(cl);
 
   // all symbols from the dynamic table
-  const int n = the_table()->table_size();
-  for (int i = 0; i < n; i++) {
-    for (HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
-         p != NULL;
-         p = p->next()) {
-      cl->do_symbol(p->literal_addr());
-    }
+  SymbolsDo sd(cl);
+  if (!SymbolTable::the_table()->_local_table->try_scan(Thread::current(), sd)) {
+    log_info(stringtable)("symbols_do unavailable at this moment");
   }
 }
 
+class MetaspacePointersDo : StackObj {
+  MetaspaceClosure *_it;
+public:
+  MetaspacePointersDo(MetaspaceClosure *it) : _it(it) {}
+  bool operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    _it->push(value);
+    return true;
+  };
+};
+
 void SymbolTable::metaspace_pointers_do(MetaspaceClosure* it) {
   assert(DumpSharedSpaces, "called only during dump time");
-  const int n = the_table()->table_size();
-  for (int i = 0; i < n; i++) {
-    for (HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
-         p != NULL;
-         p = p->next()) {
-      it->push(p->literal_addr());
-    }
-  }
+  MetaspacePointersDo mpd(it);
+  SymbolTable::the_table()->_local_table->do_scan(Thread::current(), mpd);
 }
 
-int SymbolTable::_symbols_removed = 0;
-int SymbolTable::_symbols_counted = 0;
-volatile int SymbolTable::_parallel_claimed_idx = 0;
-
-void SymbolTable::buckets_unlink(int start_idx, int end_idx, BucketUnlinkContext* context) {
-  for (int i = start_idx; i < end_idx; ++i) {
-    HashtableEntry<Symbol*, mtSymbol>** p = the_table()->bucket_addr(i);
-    HashtableEntry<Symbol*, mtSymbol>* entry = the_table()->bucket(i);
-    while (entry != NULL) {
-      // Shared entries are normally at the end of the bucket and if we run into
-      // a shared entry, then there is nothing more to remove. However, if we
-      // have rehashed the table, then the shared entries are no longer at the
-      // end of the bucket.
-      if (entry->is_shared() && !use_alternate_hashcode()) {
-        break;
-      }
-      Symbol* s = entry->literal();
-      context->_num_processed++;
-      assert(s != NULL, "just checking");
-      // If reference count is zero, remove.
-      if (s->refcount() == 0) {
-        assert(!entry->is_shared(), "shared entries should be kept live");
-        delete s;
-        *p = entry->next();
-        context->free_entry(entry);
-      } else {
-        p = entry->next_addr();
-      }
-      // get next entry
-      entry = (HashtableEntry<Symbol*, mtSymbol>*)HashtableEntry<Symbol*, mtSymbol>::make_ptr(*p);
-    }
-  }
-}
-
-// Remove unreferenced symbols from the symbol table
-// This is done late during GC.
-void SymbolTable::unlink(int* processed, int* removed) {
-  BucketUnlinkContext context;
-  buckets_unlink(0, the_table()->table_size(), &context);
-  _the_table->bulk_free_entries(&context);
-  *processed = context._num_processed;
-  *removed = context._num_removed;
-
-  _symbols_removed = context._num_removed;
-  _symbols_counted = context._num_processed;
-}
-
-void SymbolTable::possibly_parallel_unlink(int* processed, int* removed) {
-  const int limit = the_table()->table_size();
-
-  BucketUnlinkContext context;
-  for (;;) {
-    // Grab next set of buckets to scan
-    int start_idx = Atomic::add(ClaimChunkSize, &_parallel_claimed_idx) - ClaimChunkSize;
-    if (start_idx >= limit) {
-      // End of table
-      break;
-    }
-
-    int end_idx = MIN2(limit, start_idx + ClaimChunkSize);
-    buckets_unlink(start_idx, end_idx, &context);
-  }
-
-  _the_table->bulk_free_entries(&context);
-  *processed = context._num_processed;
-  *removed = context._num_removed;
-
-  Atomic::add(context._num_processed, &_symbols_counted);
-  Atomic::add(context._num_removed, &_symbols_removed);
-}
-
-// Create a new table and using alternate hash code, populate the new table
-// with the existing strings.   Set flag to use the alternate hash code afterwards.
-void SymbolTable::rehash_table() {
-  if (DumpSharedSpaces) {
-    tty->print_cr("Warning: rehash_table should not be called while dumping archive");
-    return;
-  }
-
-  assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
-  // This should never happen with -Xshare:dump but it might in testing mode.
-  if (DumpSharedSpaces) return;
-
-  // Create a new symbol table
-  SymbolTable* new_table = new SymbolTable();
-
-  the_table()->move_to(new_table);
-
-  // Delete the table and buckets (entries are reused in new table).
-  delete _the_table;
-  // Don't check if we need rehashing until the table gets unbalanced again.
-  // Then rehash with a new global seed.
-  _needs_rehashing = false;
-  _the_table = new_table;
-}
-
-// Lookup a symbol in a bucket.
-
-Symbol* SymbolTable::lookup_dynamic(int index, const char* name,
+Symbol* SymbolTable::lookup_dynamic(const char* name,
                                     int len, unsigned int hash) {
-  int count = 0;
-  for (HashtableEntry<Symbol*, mtSymbol>* e = bucket(index); e != NULL; e = e->next()) {
-    count++;  // count all entries in this bucket, not just ones with same hash
-    if (e->hash() == hash) {
-      Symbol* sym = e->literal();
-      // Skip checking already dead symbols in the bucket.
-      if (sym->refcount() == 0) {
-        count--;   // Don't count this symbol towards rehashing.
-      } else if (sym->equals(name, len)) {
-        if (sym->try_increment_refcount()) {
-          // something is referencing this symbol now.
-          return sym;
-        } else {
-          count--;   // don't count this symbol.
-        }
-      }
-    }
-  }
-  // If the bucket size is too deep check if this hash code is insufficient.
-  if (count >= rehash_count && !needs_rehashing()) {
-    _needs_rehashing = check_rehash_table(count);
-  }
-  return NULL;
+  Symbol* sym = SymbolTable::the_table()->do_lookup(name, len, hash);
+  assert((sym == NULL) || sym->refcount() != 0, "refcount must not be zero");
+  return sym;
 }
 
 Symbol* SymbolTable::lookup_shared(const char* name,
                                    int len, unsigned int hash) {
-  if (use_alternate_hashcode()) {
-    // hash_code parameter may use alternate hashing algorithm but the shared table
-    // always uses the same original hash code.
-    hash = hash_shared_symbol(name, len);
+  if (!_shared_table.empty()) {
+    if (SymbolTable::_alt_hash) {
+      // hash_code parameter may use alternate hashing algorithm but the shared table
+      // always uses the same original hash code.
+      hash = hash_shared_symbol(name, len);
+    }
+    return _shared_table.lookup(name, hash, len);
+  } else {
+    return NULL;
   }
-  return _shared_table.lookup(name, hash, len);
 }
 
-Symbol* SymbolTable::lookup(int index, const char* name,
+Symbol* SymbolTable::lookup_common(const char* name,
                             int len, unsigned int hash) {
   Symbol* sym;
   if (_lookup_shared_first) {
     sym = lookup_shared(name, len, hash);
-    if (sym != NULL) {
-      return sym;
+    if (sym == NULL) {
+      _lookup_shared_first = false;
+      sym = lookup_dynamic(name, len, hash);
     }
-    _lookup_shared_first = false;
-    return lookup_dynamic(index, name, len, hash);
   } else {
-    sym = lookup_dynamic(index, name, len, hash);
-    if (sym != NULL) {
-      return sym;
+    sym = lookup_dynamic(name, len, hash);
+    if (sym == NULL) {
+      sym = lookup_shared(name, len, hash);
+      if (sym != NULL) {
+        _lookup_shared_first = true;
+      }
     }
-    sym = lookup_shared(name, len, hash);
-    if (sym != NULL) {
-      _lookup_shared_first = true;
-    }
-    return sym;
   }
-}
-
-u4 SymbolTable::encode_shared(Symbol* sym) {
-  assert(DumpSharedSpaces, "called only during dump time");
-  uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
-  uintx offset = uintx(sym) - base_address;
-  assert(offset < 0x7fffffff, "sanity");
-  return u4(offset);
-}
-
-Symbol* SymbolTable::decode_shared(u4 offset) {
-  assert(!DumpSharedSpaces, "called only during runtime");
-  uintx base_address = _shared_table.base_address();
-  Symbol* sym = (Symbol*)(base_address + offset);
-
-#ifndef PRODUCT
-  const char* s = (const char*)sym->bytes();
-  int len = sym->utf8_length();
-  unsigned int hash = hash_symbol(s, len);
-  assert(sym == lookup_shared(s, len, hash), "must be shared symbol");
-#endif
-
   return sym;
 }
 
-// Pick hashing algorithm.
-unsigned int SymbolTable::hash_symbol(const char* s, int len) {
-  return use_alternate_hashcode() ?
-           AltHashing::murmur3_32(seed(), (const jbyte*)s, len) :
-           java_lang_String::hash_code((const jbyte*)s, len);
-}
-
-unsigned int SymbolTable::hash_shared_symbol(const char* s, int len) {
-  return java_lang_String::hash_code((const jbyte*)s, len);
-}
-
-
-// We take care not to be blocking while holding the
-// SymbolTable_lock. Otherwise, the system might deadlock, since the
-// symboltable is used during compilation (VM_thread) The lock free
-// synchronization is simplified by the fact that we do not delete
-// entries in the symbol table during normal execution (only during
-// safepoints).
-
 Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) {
-  unsigned int hashValue = hash_symbol(name, len);
-  int index = the_table()->hash_to_index(hashValue);
-
-  Symbol* s = the_table()->lookup(index, name, len, hashValue);
-
-  // Found
-  if (s != NULL) return s;
-
-  // Grab SymbolTable_lock first.
-  MutexLocker ml(SymbolTable_lock, THREAD);
-
-  // Otherwise, add to symbol to table
-  return the_table()->basic_add(index, (u1*)name, len, hashValue, true, THREAD);
+  unsigned int hash = hash_symbol(name, len, SymbolTable::_alt_hash);
+  Symbol* sym = SymbolTable::the_table()->lookup_common(name, len, hash);
+  if (sym == NULL) {
+    sym = SymbolTable::the_table()->do_add_if_needed(name, len, hash, true, CHECK_NULL);
+  }
+  assert(sym->refcount() != 0, "lookup should have incremented the count");
+  assert(sym->equals(name, len), "symbol must be properly initialized");
+  return sym;
 }
 
 Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) {
-  char* buffer;
-  int index, len;
-  unsigned int hashValue;
-  char* name;
-  {
-    debug_only(NoSafepointVerifier nsv;)
-
-    name = (char*)sym->base() + begin;
-    len = end - begin;
-    hashValue = hash_symbol(name, len);
-    index = the_table()->hash_to_index(hashValue);
-    Symbol* s = the_table()->lookup(index, name, len, hashValue);
-
-    // Found
-    if (s != NULL) return s;
+  assert(sym->refcount() != 0, "require a valid symbol");
+  const char* name = (const char*)sym->base() + begin;
+  int len = end - begin;
+  unsigned int hash = hash_symbol(name, len, SymbolTable::_alt_hash);
+  Symbol* found = SymbolTable::the_table()->lookup_common(name, len, hash);
+  if (found == NULL) {
+    found = SymbolTable::the_table()->do_add_if_needed(name, len, hash, true, THREAD);
   }
-
-  // Otherwise, add to symbol to table. Copy to a C string first.
-  char stack_buf[128];
-  ResourceMark rm(THREAD);
-  if (len <= 128) {
-    buffer = stack_buf;
-  } else {
-    buffer = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, char, len);
-  }
-  for (int i=0; i<len; i++) {
-    buffer[i] = name[i];
-  }
-  // Make sure there is no safepoint in the code above since name can't move.
-  // We can't include the code in NoSafepointVerifier because of the
-  // ResourceMark.
-
-  // Grab SymbolTable_lock first.
-  MutexLocker ml(SymbolTable_lock, THREAD);
-
-  return the_table()->basic_add(index, (u1*)buffer, len, hashValue, true, THREAD);
+  return found;
 }
 
-Symbol* SymbolTable::lookup_only(const char* name, int len,
-                                   unsigned int& hash) {
-  hash = hash_symbol(name, len);
-  int index = the_table()->hash_to_index(hash);
+class SymbolTableLookup : StackObj {
+private:
+  Thread* _thread;
+  uintx _hash;
+  int _len;
+  const char* _str;
+public:
+  SymbolTableLookup(Thread* thread, const char* key, int len, uintx hash)
+  : _thread(thread), _hash(hash), _len(len), _str(key) {}
+  uintx get_hash() const {
+    return _hash;
+  }
+  bool equals(Symbol** value, bool* is_dead) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    Symbol *sym = *value;
+    if (sym->equals(_str, _len)) {
+      if (sym->try_increment_refcount()) {
+        // something is referencing this symbol now.
+        return true;
+      } else {
+        assert(sym->refcount() == 0, "expected dead symbol");
+        *is_dead = true;
+        return false;
+      }
+    } else {
+      *is_dead = (sym->refcount() == 0);
+      return false;
+    }
+  }
+};
 
-  Symbol* s = the_table()->lookup(index, name, len, hash);
-  return s;
+class SymbolTableGet : public StackObj {
+  Symbol* _return;
+public:
+  SymbolTableGet() : _return(NULL) {}
+  void operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    _return = *value;
+  }
+  Symbol* get_res_sym() {
+    return _return;
+  }
+};
+
+Symbol* SymbolTable::do_lookup(const char* name, int len, uintx hash) {
+  Thread* thread = Thread::current();
+  SymbolTableLookup lookup(thread, name, len, hash);
+  SymbolTableGet stg;
+  bool rehash_warning = false;
+  _local_table->get(thread, lookup, stg, &rehash_warning);
+  if (rehash_warning) {
+    _needs_rehashing = true;
+  }
+  Symbol* sym = stg.get_res_sym();
+  assert((sym == NULL) || sym->refcount() != 0, "found dead symbol");
+  return sym;
 }
 
-// Look up the address of the literal in the SymbolTable for this Symbol*
-// Do not create any new symbols
-// Do not increment the reference count to keep this alive
-Symbol** SymbolTable::lookup_symbol_addr(Symbol* sym){
-  unsigned int hash = hash_symbol((char*)sym->bytes(), sym->utf8_length());
-  int index = the_table()->hash_to_index(hash);
-
-  for (HashtableEntry<Symbol*, mtSymbol>* e = the_table()->bucket(index); e != NULL; e = e->next()) {
-    if (e->hash() == hash) {
-      Symbol* literal_sym = e->literal();
-      if (sym == literal_sym) {
-        return e->literal_addr();
-      }
-    }
-  }
-  return NULL;
+Symbol* SymbolTable::lookup_only(const char* name, int len, unsigned int& hash) {
+  hash = hash_symbol(name, len, SymbolTable::_alt_hash);
+  return SymbolTable::the_table()->lookup_common(name, len, hash);
 }
 
 // Suggestion: Push unicode-based lookup all the way into the hashing
@@ -395,14 +389,14 @@
 // an actual new Symbol* is created.
 Symbol* SymbolTable::lookup_unicode(const jchar* name, int utf16_length, TRAPS) {
   int utf8_length = UNICODE::utf8_length((jchar*) name, utf16_length);
-  char stack_buf[128];
+  char stack_buf[ON_STACK_BUFFER_LENGTH];
   if (utf8_length < (int) sizeof(stack_buf)) {
     char* chars = stack_buf;
     UNICODE::convert_to_utf8(name, utf16_length, chars);
     return lookup(chars, utf8_length, THREAD);
   } else {
     ResourceMark rm(THREAD);
-    char* chars = NEW_RESOURCE_ARRAY(char, utf8_length + 1);;
+    char* chars = NEW_RESOURCE_ARRAY(char, utf8_length + 1);
     UNICODE::convert_to_utf8(name, utf16_length, chars);
     return lookup(chars, utf8_length, THREAD);
   }
@@ -411,214 +405,243 @@
 Symbol* SymbolTable::lookup_only_unicode(const jchar* name, int utf16_length,
                                            unsigned int& hash) {
   int utf8_length = UNICODE::utf8_length((jchar*) name, utf16_length);
-  char stack_buf[128];
+  char stack_buf[ON_STACK_BUFFER_LENGTH];
   if (utf8_length < (int) sizeof(stack_buf)) {
     char* chars = stack_buf;
     UNICODE::convert_to_utf8(name, utf16_length, chars);
     return lookup_only(chars, utf8_length, hash);
   } else {
     ResourceMark rm;
-    char* chars = NEW_RESOURCE_ARRAY(char, utf8_length + 1);;
+    char* chars = NEW_RESOURCE_ARRAY(char, utf8_length + 1);
     UNICODE::convert_to_utf8(name, utf16_length, chars);
     return lookup_only(chars, utf8_length, hash);
   }
 }
 
 void SymbolTable::add(ClassLoaderData* loader_data, const constantPoolHandle& cp,
-                      int names_count,
-                      const char** names, int* lengths, int* cp_indices,
-                      unsigned int* hashValues, TRAPS) {
-  // Grab SymbolTable_lock first.
-  MutexLocker ml(SymbolTable_lock, THREAD);
+                      int names_count, const char** names, int* lengths,
+                      int* cp_indices, unsigned int* hashValues, TRAPS) {
+  bool c_heap = !loader_data->is_the_null_class_loader_data();
+  for (int i = 0; i < names_count; i++) {
+    const char *name = names[i];
+    int len = lengths[i];
+    unsigned int hash = hashValues[i];
+    Symbol* sym = SymbolTable::the_table()->lookup_common(name, len, hash);
+    if (sym == NULL) {
+      sym = SymbolTable::the_table()->do_add_if_needed(name, len, hash, c_heap, CHECK);
+    }
+    assert(sym->refcount() != 0, "lookup should have incremented the count");
+    cp->symbol_at_put(cp_indices[i], sym);
+  }
+}
 
-  SymbolTable* table = the_table();
-  bool added = table->basic_add(loader_data, cp, names_count, names, lengths,
-                                cp_indices, hashValues, CHECK);
-  if (!added) {
-    // do it the hard way
-    for (int i=0; i<names_count; i++) {
-      int index = table->hash_to_index(hashValues[i]);
-      bool c_heap = !loader_data->is_the_null_class_loader_data();
-      Symbol* sym = table->basic_add(index, (u1*)names[i], lengths[i], hashValues[i], c_heap, CHECK);
-      cp->symbol_at_put(cp_indices[i], sym);
+class SymbolTableCreateEntry : public StackObj {
+private:
+  Thread*     _thread;
+  const char* _name;
+  int         _len;
+  bool        _heap;
+  Symbol*     _return;
+  Symbol*     _created;
+
+  void assert_for_name(Symbol* sym, const char* where) const {
+#ifdef ASSERT
+    assert(sym->utf8_length() == _len, "%s [%d,%d]", where, sym->utf8_length(), _len);
+    for (int i = 0; i < _len; i++) {
+      assert(sym->byte_at(i) == _name[i],
+             "%s [%d,%d,%d]", where, i, sym->byte_at(i), _name[i]);
+    }
+#endif
+  }
+
+public:
+  SymbolTableCreateEntry(Thread* thread, const char* name, int len, bool heap)
+  : _thread(thread), _name(name) , _len(len), _heap(heap), _return(NULL) , _created(NULL) {
+    assert(_name != NULL, "expected valid name");
+  }
+  Symbol* operator()() {
+    _created = SymbolTable::the_table()->allocate_symbol(_name, _len, _heap, _thread);
+    assert(_created != NULL, "expected created symbol");
+    assert_for_name(_created, "operator()()");
+    assert(_created->equals(_name, _len),
+           "symbol must be properly initialized [%p,%d,%d]", _name, _len, (int)_heap);
+    return _created;
+  }
+  void operator()(bool inserted, Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    if (!inserted && (_created != NULL)) {
+      // We created our symbol, but someone else inserted
+      // theirs first, so ours will be destroyed.
+      // Since symbols are created with refcount of 1,
+      // we must decrement it here to 0 to delete,
+      // unless it's a permanent one.
+      if (_created->refcount() != PERM_REFCOUNT) {
+        assert(_created->refcount() == 1, "expected newly created symbol");
+        _created->decrement_refcount();
+        assert(_created->refcount() == 0, "expected dead symbol");
+      }
+    }
+    _return = *value;
+    assert_for_name(_return, "operator()");
+  }
+  Symbol* get_new_sym() const {
+    assert_for_name(_return, "get_new_sym");
+    return _return;
+  }
+};
+
+Symbol* SymbolTable::do_add_if_needed(const char* name, int len, uintx hash, bool heap, TRAPS) {
+  SymbolTableLookup lookup(THREAD, name, len, hash);
+  SymbolTableCreateEntry stce(THREAD, name, len, heap);
+  bool rehash_warning = false;
+  bool clean_hint = false;
+  _local_table->get_insert_lazy(THREAD, lookup, stce, stce, &rehash_warning, &clean_hint);
+  if (rehash_warning) {
+    _needs_rehashing = true;
+  }
+  if (clean_hint) {
+    // we just found out that there is a dead item,
+    // which we were unable to clean right now,
+    // but we have no way of telling whether it's
+    // been previously counted or not, so mark
+    // it only if no other items were found yet
+    mark_item_clean_count();
+    check_concurrent_work();
+  }
+  Symbol* sym = stce.get_new_sym();
+  assert(sym->refcount() != 0, "zero is invalid");
+  return sym;
+}
+
+Symbol* SymbolTable::new_permanent_symbol(const char* name, TRAPS) {
+  unsigned int hash = 0;
+  int len = (int)strlen(name);
+  Symbol* sym = SymbolTable::lookup_only(name, len, hash);
+  if (sym == NULL) {
+    sym = SymbolTable::the_table()->do_add_if_needed(name, len, hash, false, CHECK_NULL);
+  }
+  if (sym->refcount() != PERM_REFCOUNT) {
+    sym->increment_refcount();
+    log_trace_symboltable_helper(sym, "Asked for a permanent symbol, but got a regular one");
+  }
+  return sym;
+}
+
+struct SizeFunc : StackObj {
+  size_t operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    return (*value)->size() * HeapWordSize;
+  };
+};
+
+void SymbolTable::print_table_statistics(outputStream* st,
+                                         const char* table_name) {
+  SizeFunc sz;
+  _local_table->statistics_to(Thread::current(), sz, st, table_name);
+}
+
+// Verification
+class VerifySymbols : StackObj {
+public:
+  bool operator()(Symbol** value) {
+    guarantee(value != NULL, "expected valid value");
+    guarantee(*value != NULL, "value should point to a symbol");
+    Symbol* sym = *value;
+    guarantee(sym->equals((const char*)sym->bytes(), sym->utf8_length()),
+              "symbol must be internally consistent");
+    return true;
+  };
+};
+
+void SymbolTable::verify() {
+  Thread* thr = Thread::current();
+  VerifySymbols vs;
+  if (!SymbolTable::the_table()->_local_table->try_scan(thr, vs)) {
+    log_info(stringtable)("verify unavailable at this moment");
+  }
+}
+
+// Dumping
+class DumpSymbol : StackObj {
+  Thread* _thr;
+  outputStream* _st;
+public:
+  DumpSymbol(Thread* thr, outputStream* st) : _thr(thr), _st(st) {}
+  bool operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    Symbol* sym = *value;
+    const char* utf8_string = (const char*)sym->bytes();
+    int utf8_length = sym->utf8_length();
+    _st->print("%d %d: ", utf8_length, sym->refcount());
+    HashtableTextDump::put_utf8(_st, utf8_string, utf8_length);
+    _st->cr();
+    return true;
+  };
+};
+
+void SymbolTable::dump(outputStream* st, bool verbose) {
+  if (!verbose) {
+    SymbolTable::the_table()->print_table_statistics(st, "SymbolTable");
+  } else {
+    Thread* thr = Thread::current();
+    ResourceMark rm(thr);
+    st->print_cr("VERSION: 1.1");
+    DumpSymbol ds(thr, st);
+    if (!SymbolTable::the_table()->_local_table->try_scan(thr, ds)) {
+      log_info(symboltable)("dump unavailable at this moment");
     }
   }
 }
 
-Symbol* SymbolTable::new_permanent_symbol(const char* name, TRAPS) {
-  unsigned int hash;
-  Symbol* result = SymbolTable::lookup_only((char*)name, (int)strlen(name), hash);
-  if (result != NULL) {
-    return result;
+#if INCLUDE_CDS
+struct CopyToArchive : StackObj {
+  CompactSymbolTableWriter* _writer;
+  CopyToArchive(CompactSymbolTableWriter* writer) : _writer(writer) {}
+  bool operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    Symbol* sym = *value;
+    unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length());
+    if (fixed_hash == 0) {
+      return true;
+    }
+    assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length(), false),
+           "must not rehash during dumping");
+
+    // add to the compact table
+    _writer->add(fixed_hash, sym);
+
+    return true;
   }
-  // Grab SymbolTable_lock first.
-  MutexLocker ml(SymbolTable_lock, THREAD);
+};
 
-  SymbolTable* table = the_table();
-  int index = table->hash_to_index(hash);
-  return table->basic_add(index, (u1*)name, (int)strlen(name), hash, false, THREAD);
-}
-
-Symbol* SymbolTable::basic_add(int index_arg, u1 *name, int len,
-                               unsigned int hashValue_arg, bool c_heap, TRAPS) {
-  assert(!Universe::heap()->is_in_reserved(name),
-         "proposed name of symbol must be stable");
-
-  // Don't allow symbols to be created which cannot fit in a Symbol*.
-  if (len > Symbol::max_length()) {
-    THROW_MSG_0(vmSymbols::java_lang_InternalError(),
-                "name is too long to represent");
-  }
-
-  // Cannot hit a safepoint in this function because the "this" pointer can move.
-  NoSafepointVerifier nsv;
-
-  // Check if the symbol table has been rehashed, if so, need to recalculate
-  // the hash value and index.
-  unsigned int hashValue;
-  int index;
-  if (use_alternate_hashcode()) {
-    hashValue = hash_symbol((const char*)name, len);
-    index = hash_to_index(hashValue);
-  } else {
-    hashValue = hashValue_arg;
-    index = index_arg;
-  }
-
-  // Since look-up was done lock-free, we need to check if another
-  // thread beat us in the race to insert the symbol.
-  Symbol* test = lookup(index, (char*)name, len, hashValue);
-  if (test != NULL) {
-    // A race occurred and another thread introduced the symbol.
-    assert(test->refcount() != 0, "lookup should have incremented the count");
-    return test;
-  }
-
-  // Create a new symbol.
-  Symbol* sym = allocate_symbol(name, len, c_heap, CHECK_NULL);
-  assert(sym->equals((char*)name, len), "symbol must be properly initialized");
-
-  HashtableEntry<Symbol*, mtSymbol>* entry = new_entry(hashValue, sym);
-  add_entry(index, entry);
-  return sym;
-}
-
-// This version of basic_add adds symbols in batch from the constant pool
-// parsing.
-bool SymbolTable::basic_add(ClassLoaderData* loader_data, const constantPoolHandle& cp,
-                            int names_count,
-                            const char** names, int* lengths,
-                            int* cp_indices, unsigned int* hashValues,
-                            TRAPS) {
-
-  // Check symbol names are not too long.  If any are too long, don't add any.
-  for (int i = 0; i< names_count; i++) {
-    if (lengths[i] > Symbol::max_length()) {
-      THROW_MSG_0(vmSymbols::java_lang_InternalError(),
-                  "name is too long to represent");
-    }
-  }
-
-  // Cannot hit a safepoint in this function because the "this" pointer can move.
-  NoSafepointVerifier nsv;
-
-  for (int i=0; i<names_count; i++) {
-    // Check if the symbol table has been rehashed, if so, need to recalculate
-    // the hash value.
-    unsigned int hashValue;
-    if (use_alternate_hashcode()) {
-      hashValue = hash_symbol(names[i], lengths[i]);
-    } else {
-      hashValue = hashValues[i];
-    }
-    // Since look-up was done lock-free, we need to check if another
-    // thread beat us in the race to insert the symbol.
-    int index = hash_to_index(hashValue);
-    Symbol* test = lookup(index, names[i], lengths[i], hashValue);
-    if (test != NULL) {
-      // A race occurred and another thread introduced the symbol, this one
-      // will be dropped and collected. Use test instead.
-      cp->symbol_at_put(cp_indices[i], test);
-      assert(test->refcount() != 0, "lookup should have incremented the count");
-    } else {
-      // Create a new symbol.  The null class loader is never unloaded so these
-      // are allocated specially in a permanent arena.
-      bool c_heap = !loader_data->is_the_null_class_loader_data();
-      Symbol* sym = allocate_symbol((const u1*)names[i], lengths[i], c_heap, CHECK_(false));
-      assert(sym->equals(names[i], lengths[i]), "symbol must be properly initialized");  // why wouldn't it be???
-      HashtableEntry<Symbol*, mtSymbol>* entry = new_entry(hashValue, sym);
-      add_entry(index, entry);
-      cp->symbol_at_put(cp_indices[i], sym);
-    }
-  }
-  return true;
-}
-
-
-void SymbolTable::verify() {
-  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());
-      guarantee(s != NULL, "symbol is NULL");
-      unsigned int h = hash_symbol((char*)s->bytes(), s->utf8_length());
-      guarantee(p->hash() == h, "broken hash in symbol table entry");
-      guarantee(the_table()->hash_to_index(h) == i,
-                "wrong index in symbol table");
-    }
-  }
-}
-
-void SymbolTable::dump(outputStream* st, bool verbose) {
-  if (!verbose) {
-    the_table()->print_table_statistics(st, "SymbolTable");
-  } else {
-    st->print_cr("VERSION: 1.0");
-    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());
-        const char* utf8_string = (const char*)s->bytes();
-        int utf8_length = s->utf8_length();
-        st->print("%d %d: ", utf8_length, s->refcount());
-        HashtableTextDump::put_utf8(st, utf8_string, utf8_length);
-        st->cr();
-      }
-    }
-  }
+void SymbolTable::copy_shared_symbol_table(CompactSymbolTableWriter* writer) {
+  CopyToArchive copy(writer);
+  SymbolTable::the_table()->_local_table->do_scan(Thread::current(), copy);
 }
 
 void SymbolTable::write_to_archive() {
-#if INCLUDE_CDS
-    _shared_table.reset();
+  _shared_table.reset();
 
-    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);
-      }
-    }
+  int num_buckets = (int)(SymbolTable::the_table()->_items_count / SharedSymbolTableBucketSize);
+  // calculation of num_buckets can result in zero buckets, we need at least one
+  CompactSymbolTableWriter writer(num_buckets > 1 ? num_buckets : 1,
+                                  &MetaspaceShared::stats()->symbol);
+  copy_shared_symbol_table(&writer);
+  writer.dump(&_shared_table);
 
-    writer.dump(&_shared_table);
-
-    // 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");
-#endif
+  // 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, SymbolTable::_alt_hash);
+  assert(sym == _shared_table.lookup(name, hash, len), "sanity");
 }
 
 void SymbolTable::serialize(SerializeClosure* soc) {
-#if INCLUDE_CDS
   _shared_table.set_type(CompactHashtable<Symbol*, char>::_symbol_table);
   _shared_table.serialize(soc);
 
@@ -626,7 +649,201 @@
     // Sanity. Make sure we don't use the shared table at dump time
     _shared_table.reset();
   }
-#endif
+}
+#endif //INCLUDE_CDS
+
+// Concurrent work
+void SymbolTable::grow(JavaThread* jt) {
+  SymbolTableHash::GrowTask gt(_local_table);
+  if (!gt.prepare(jt)) {
+    return;
+  }
+  log_trace(symboltable)("Started to grow");
+  {
+    TraceTime timer("Grow", TRACETIME_LOG(Debug, symboltable, perf));
+    while (gt.do_task(jt)) {
+      gt.pause(jt);
+      {
+        ThreadBlockInVM tbivm(jt);
+      }
+      gt.cont(jt);
+    }
+  }
+  gt.done(jt);
+  _current_size = table_size();
+  log_debug(symboltable)("Grown to size:" SIZE_FORMAT, _current_size);
+}
+
+struct SymbolTableDoDelete : StackObj {
+  int _deleted;
+  SymbolTableDoDelete() : _deleted(0) {}
+  void operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    Symbol *sym = *value;
+    assert(sym->refcount() == 0, "refcount");
+    _deleted++;
+  }
+};
+
+struct SymbolTableDeleteCheck : StackObj {
+  int _processed;
+  SymbolTableDeleteCheck() : _processed(0) {}
+  bool operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    _processed++;
+    Symbol *sym = *value;
+    return (sym->refcount() == 0);
+  }
+};
+
+void SymbolTable::clean_dead_entries(JavaThread* jt) {
+  SymbolTableHash::BulkDeleteTask bdt(_local_table);
+  if (!bdt.prepare(jt)) {
+    return;
+  }
+
+  SymbolTableDeleteCheck stdc;
+  SymbolTableDoDelete stdd;
+  {
+    TraceTime timer("Clean", TRACETIME_LOG(Debug, symboltable, perf));
+    while (bdt.do_task(jt, stdc, stdd)) {
+      bdt.pause(jt);
+      {
+        ThreadBlockInVM tbivm(jt);
+      }
+      bdt.cont(jt);
+    }
+    SymbolTable::the_table()->set_item_clean_count(0);
+    bdt.done(jt);
+  }
+
+  Atomic::add((size_t)stdc._processed, &_symbols_counted);
+
+  log_debug(symboltable)("Cleaned " INT32_FORMAT " of " INT32_FORMAT,
+                         stdd._deleted, stdc._processed);
+}
+
+void SymbolTable::check_concurrent_work() {
+  if (_has_work) {
+    return;
+  }
+  double load_factor = SymbolTable::get_load_factor();
+  double dead_factor = SymbolTable::get_dead_factor();
+  // We should clean/resize if we have more dead than alive,
+  // more items than preferred load factor or
+  // more dead items than water mark.
+  if ((dead_factor > load_factor) ||
+      (load_factor > PREF_AVG_LIST_LEN) ||
+      (dead_factor > CLEAN_DEAD_HIGH_WATER_MARK)) {
+    log_debug(symboltable)("Concurrent work triggered, live factor:%f dead factor:%f",
+                           load_factor, dead_factor);
+    trigger_concurrent_work();
+  }
+}
+
+void SymbolTable::concurrent_work(JavaThread* jt) {
+  double load_factor = get_load_factor();
+  log_debug(symboltable, perf)("Concurrent work, live factor: %g", load_factor);
+  // We prefer growing, since that also removes dead items
+  if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) {
+    grow(jt);
+  } else {
+    clean_dead_entries(jt);
+  }
+  _has_work = false;
+}
+
+class CountDead : StackObj {
+  int _count;
+public:
+  CountDead() : _count(0) {}
+  bool operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    Symbol* sym = *value;
+    if (sym->refcount() == 0) {
+      _count++;
+    }
+    return true;
+  };
+  int get_dead_count() {
+    return _count;
+  }
+};
+
+void SymbolTable::do_check_concurrent_work() {
+  CountDead counter;
+  if (!SymbolTable::the_table()->_local_table->try_scan(Thread::current(), counter)) {
+    log_info(symboltable)("count dead unavailable at this moment");
+  } else {
+    SymbolTable::the_table()->set_item_clean_count(counter.get_dead_count());
+    SymbolTable::the_table()->check_concurrent_work();
+  }
+}
+
+void SymbolTable::do_concurrent_work(JavaThread* jt) {
+  SymbolTable::the_table()->concurrent_work(jt);
+}
+
+// Rehash
+bool SymbolTable::do_rehash() {
+  if (!_local_table->is_safepoint_safe()) {
+    return false;
+  }
+
+  // We use max size
+  SymbolTableHash* new_table = new SymbolTableHash(END_SIZE, END_SIZE, REHASH_LEN);
+  // Use alt hash from now on
+  _alt_hash = true;
+  if (!_local_table->try_move_nodes_to(Thread::current(), new_table)) {
+    _alt_hash = false;
+    delete new_table;
+    return false;
+  }
+
+  // free old table
+  delete _local_table;
+  _local_table = new_table;
+
+  return true;
+}
+
+void SymbolTable::try_rehash_table() {
+  static bool rehashed = false;
+  log_debug(symboltable)("Table imbalanced, rehashing called.");
+
+  // Grow instead of rehash.
+  if (get_load_factor() > PREF_AVG_LIST_LEN &&
+      !_local_table->is_max_size_reached()) {
+    log_debug(symboltable)("Choosing growing over rehashing.");
+    trigger_concurrent_work();
+    _needs_rehashing = false;
+    return;
+  }
+
+  // Already rehashed.
+  if (rehashed) {
+    log_warning(symboltable)("Rehashing already done, still long lists.");
+    trigger_concurrent_work();
+    _needs_rehashing = false;
+    return;
+  }
+
+  murmur_seed = AltHashing::compute_seed();
+
+  if (do_rehash()) {
+    rehashed = true;
+  } else {
+    log_info(symboltable)("Resizes in progress rehashing skipped.");
+  }
+
+  _needs_rehashing = false;
+}
+
+void SymbolTable::rehash_table() {
+  SymbolTable::the_table()->try_rehash_table();
 }
 
 //---------------------------------------------------------------------------
@@ -634,89 +851,80 @@
 
 #ifndef PRODUCT
 
-void SymbolTable::print_histogram() {
-  MutexLocker ml(SymbolTable_lock);
-  const int results_length = 100;
-  int counts[results_length];
-  int sizes[results_length];
-  int i,j;
-
-  // initialize results to zero
-  for (j = 0; j < results_length; j++) {
-    counts[j] = 0;
-    sizes[j] = 0;
-  }
-
-  int total_size = 0;
-  int total_count = 0;
-  int total_length = 0;
-  int max_length = 0;
-  int out_of_range_count = 0;
-  int out_of_range_size = 0;
-  for (i = 0; i < the_table()->table_size(); i++) {
-    HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
-    for ( ; p != NULL; p = p->next()) {
-      int size = p->literal()->size();
-      int len = p->literal()->utf8_length();
-      if (len < results_length) {
-        counts[len]++;
-        sizes[len] += size;
-      } else {
-        out_of_range_count++;
-        out_of_range_size += size;
-      }
-      total_count++;
-      total_size += size;
-      total_length += len;
-      max_length = MAX2(max_length, len);
+class HistogramIterator : StackObj {
+public:
+  static const size_t results_length = 100;
+  size_t counts[results_length];
+  size_t sizes[results_length];
+  size_t total_size;
+  size_t total_count;
+  size_t total_length;
+  size_t max_length;
+  size_t out_of_range_count;
+  size_t out_of_range_size;
+  HistogramIterator() : total_size(0), total_count(0), total_length(0),
+                        max_length(0), out_of_range_count(0), out_of_range_size(0) {
+    // initialize results to zero
+    for (size_t i = 0; i < results_length; i++) {
+      counts[i] = 0;
+      sizes[i] = 0;
     }
   }
+  bool operator()(Symbol** value) {
+    assert(value != NULL, "expected valid value");
+    assert(*value != NULL, "value should point to a symbol");
+    Symbol* sym = *value;
+    size_t size = sym->size();
+    size_t len = sym->utf8_length();
+    if (len < results_length) {
+      counts[len]++;
+      sizes[len] += size;
+    } else {
+      out_of_range_count++;
+      out_of_range_size += size;
+    }
+    total_count++;
+    total_size += size;
+    total_length += len;
+    max_length = MAX2(max_length, len);
+
+    return true;
+  };
+};
+
+void SymbolTable::print_histogram() {
+  SymbolTable* st = SymbolTable::the_table();
+  HistogramIterator hi;
+  st->_local_table->do_scan(Thread::current(), hi);
   tty->print_cr("Symbol Table Histogram:");
-  tty->print_cr("  Total number of symbols  %7d", total_count);
-  tty->print_cr("  Total size in memory     %7dK",
-          (total_size*wordSize)/1024);
-  tty->print_cr("  Total counted            %7d", _symbols_counted);
-  tty->print_cr("  Total removed            %7d", _symbols_removed);
-  if (_symbols_counted > 0) {
+  tty->print_cr("  Total number of symbols  " SIZE_FORMAT_W(7), hi.total_count);
+  tty->print_cr("  Total size in memory     " SIZE_FORMAT_W(7) "K",
+          (hi.total_size * wordSize) / 1024);
+  tty->print_cr("  Total counted            " SIZE_FORMAT_W(7), st->_symbols_counted);
+  tty->print_cr("  Total removed            " SIZE_FORMAT_W(7), st->_symbols_removed);
+  if (SymbolTable::the_table()->_symbols_counted > 0) {
     tty->print_cr("  Percent removed          %3.2f",
-          ((float)_symbols_removed/(float)_symbols_counted)* 100);
+          ((float)st->_symbols_removed / st->_symbols_counted) * 100);
   }
-  tty->print_cr("  Reference counts         %7d", Symbol::_total_count);
-  tty->print_cr("  Symbol arena used        " SIZE_FORMAT_W(7) "K", arena()->used()/1024);
-  tty->print_cr("  Symbol arena size        " SIZE_FORMAT_W(7) "K", arena()->size_in_bytes()/1024);
-  tty->print_cr("  Total symbol length      %7d", total_length);
-  tty->print_cr("  Maximum symbol length    %7d", max_length);
-  tty->print_cr("  Average symbol length    %7.2f", ((float) total_length / (float) total_count));
+  tty->print_cr("  Reference counts         " SIZE_FORMAT_W(7), Symbol::_total_count);
+  tty->print_cr("  Symbol arena used        " SIZE_FORMAT_W(7) "K", arena()->used() / 1024);
+  tty->print_cr("  Symbol arena size        " SIZE_FORMAT_W(7) "K", arena()->size_in_bytes() / 1024);
+  tty->print_cr("  Total symbol length      " SIZE_FORMAT_W(7), hi.total_length);
+  tty->print_cr("  Maximum symbol length    " SIZE_FORMAT_W(7), hi.max_length);
+  tty->print_cr("  Average symbol length    %7.2f", ((float)hi.total_length / hi.total_count));
   tty->print_cr("  Symbol length histogram:");
   tty->print_cr("    %6s %10s %10s", "Length", "#Symbols", "Size");
-  for (i = 0; i < results_length; i++) {
-    if (counts[i] > 0) {
-      tty->print_cr("    %6d %10d %10dK", i, counts[i], (sizes[i]*wordSize)/1024);
+  for (size_t i = 0; i < hi.results_length; i++) {
+    if (hi.counts[i] > 0) {
+      tty->print_cr("    " SIZE_FORMAT_W(6) " " SIZE_FORMAT_W(10) " " SIZE_FORMAT_W(10) "K",
+                    i, hi.counts[i], (hi.sizes[i] * wordSize) / 1024);
     }
   }
-  tty->print_cr("  >=%6d %10d %10dK\n", results_length,
-          out_of_range_count, (out_of_range_size*wordSize)/1024);
-}
-
-void SymbolTable::print() {
-  for (int i = 0; i < the_table()->table_size(); ++i) {
-    HashtableEntry<Symbol*, mtSymbol>** p = the_table()->bucket_addr(i);
-    HashtableEntry<Symbol*, mtSymbol>* entry = the_table()->bucket(i);
-    if (entry != NULL) {
-      while (entry != NULL) {
-        tty->print(PTR_FORMAT " ", p2i(entry->literal()));
-        entry->literal()->print();
-        tty->print(" %d", entry->literal()->refcount());
-        p = entry->next_addr();
-        entry = (HashtableEntry<Symbol*, mtSymbol>*)HashtableEntry<Symbol*, mtSymbol>::make_ptr(*p);
-      }
-      tty->cr();
-    }
-  }
+  tty->print_cr("  >=" SIZE_FORMAT_W(6) " " SIZE_FORMAT_W(10) " " SIZE_FORMAT_W(10) "K\n",
+                hi.results_length, hi.out_of_range_count, (hi.out_of_range_size*wordSize) / 1024);
 }
 #endif // PRODUCT
 
-
 // Utility for dumping symbols
 SymboltableDCmd::SymboltableDCmd(outputStream* output, bool heap) :
                                  DCmdWithParser(output, heap),
--- a/src/hotspot/share/classfile/symbolTable.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/classfile/symbolTable.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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
@@ -26,23 +26,11 @@
 #define SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
 
 #include "memory/allocation.hpp"
+#include "memory/padded.hpp"
 #include "oops/symbol.hpp"
+#include "utilities/concurrentHashTable.hpp"
 #include "utilities/hashtable.hpp"
 
-// The symbol table holds all Symbol*s and corresponding interned strings.
-// Symbol*s and literal strings should be canonicalized.
-//
-// The interned strings are created lazily.
-//
-// It is implemented as an open hash table with a fixed number of buckets.
-//
-// %note:
-//  - symbolTableEntrys are allocated in blocks to reduce the space overhead.
-
-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*).
 // The body is resource managed by a reference counting scheme.
@@ -59,7 +47,7 @@
 class TempNewSymbol : public StackObj {
   Symbol* _temp;
 
- public:
+public:
   TempNewSymbol() : _temp(NULL) {}
 
   // Conversion from a Symbol* to a TempNewSymbol.
@@ -97,35 +85,69 @@
 };
 
 template <class T, class N> class CompactHashtable;
+class CompactSymbolTableWriter;
+class SerializeClosure;
 
-class SymbolTable : public RehashableHashtable<Symbol*, mtSymbol> {
+class SymbolTableConfig;
+typedef ConcurrentHashTable<Symbol*,
+                              SymbolTableConfig, mtSymbol> SymbolTableHash;
+
+class SymbolTableCreateEntry;
+
+class SymbolTable : public CHeapObj<mtSymbol> {
   friend class VMStructs;
+  friend class Symbol;
   friend class ClassFileParser;
+  friend class SymbolTableConfig;
+  friend class SymbolTableCreateEntry;
 
 private:
+  static void delete_symbol(Symbol* sym);
+  void grow(JavaThread* jt);
+  void clean_dead_entries(JavaThread* jt);
+
   // The symbol table
   static SymbolTable* _the_table;
-
-  // Set if one bucket is out of balance due to hash algorithm deficiency
-  static bool _needs_rehashing;
-  static bool _lookup_shared_first;
+  // Shared symbol table.
+  static CompactHashtable<Symbol*, char> _shared_table;
+  static volatile bool _lookup_shared_first;
+  static volatile bool _alt_hash;
 
   // For statistics
-  static int _symbols_removed;
-  static int _symbols_counted;
+  volatile size_t _symbols_removed;
+  volatile size_t _symbols_counted;
 
-  // shared symbol table.
-  static CompactHashtable<Symbol*, char> _shared_table;
+  SymbolTableHash* _local_table;
+  size_t _current_size;
+  volatile bool _has_work;
+  // Set if one bucket is out of balance due to hash algorithm deficiency
+  volatile bool _needs_rehashing;
 
-  Symbol* allocate_symbol(const u1* name, int len, bool c_heap, TRAPS); // Assumes no characters larger than 0x7F
+  volatile size_t _items_count;
+  volatile size_t _uncleaned_items_count;
+
+  double get_load_factor();
+  double get_dead_factor();
+
+  void check_concurrent_work();
+  void trigger_concurrent_work();
+
+  static void item_added();
+  static void item_removed();
+  static void set_item_clean_count(size_t ncl);
+  static void mark_item_clean_count();
+
+  SymbolTable();
+
+  Symbol* allocate_symbol(const char* name, int len, bool c_heap, TRAPS); // Assumes no characters larger than 0x7F
+  Symbol* do_lookup(const char* name, int len, uintx hash);
+  Symbol* do_add_if_needed(const char* name, int len, uintx hash, bool heap, TRAPS);
 
   // Adding elements
-  Symbol* basic_add(int index, u1* name, int len, unsigned int hashValue,
-                    bool c_heap, TRAPS);
-  bool basic_add(ClassLoaderData* loader_data,
-                 const constantPoolHandle& cp, int names_count,
-                 const char** names, int* lengths, int* cp_indices,
-                 unsigned int* hashValues, TRAPS);
+  static void add(ClassLoaderData* loader_data,
+                  const constantPoolHandle& cp, int names_count,
+                  const char** names, int* lengths, int* cp_indices,
+                  unsigned int* hashValues, TRAPS);
 
   static void new_symbols(ClassLoaderData* loader_data,
                           const constantPoolHandle& cp, int names_count,
@@ -136,15 +158,8 @@
   }
 
   static Symbol* lookup_shared(const char* name, int len, unsigned int hash);
-  Symbol* lookup_dynamic(int index, const char* name, int len, unsigned int hash);
-  Symbol* lookup(int index, const char* name, int len, unsigned int hash);
-
-  SymbolTable()
-    : RehashableHashtable<Symbol*, mtSymbol>(SymbolTableSize, sizeof (HashtableEntry<Symbol*, mtSymbol>)) {}
-
-  SymbolTable(HashtableBucket<mtSymbol>* t, int number_of_entries)
-    : RehashableHashtable<Symbol*, mtSymbol>(SymbolTableSize, sizeof (HashtableEntry<Symbol*, mtSymbol>), t,
-                number_of_entries) {}
+  Symbol* lookup_dynamic(const char* name, int len, unsigned int hash);
+  Symbol* lookup_common(const char* name, int len, unsigned int hash);
 
   // Arena for permanent symbols (null class loader) that are never unloaded
   static Arena*  _arena;
@@ -152,88 +167,45 @@
 
   static void initialize_symbols(int arena_alloc_size = 0);
 
-  static volatile int _parallel_claimed_idx;
+  void concurrent_work(JavaThread* jt);
+  void print_table_statistics(outputStream* st, const char* table_name);
 
-  typedef SymbolTable::BucketUnlinkContext BucketUnlinkContext;
-  // Release any dead symbols. Unlinked bucket entries are collected in the given
-  // context to be freed later.
-  // This allows multiple threads to work on the table at once.
-  static void buckets_unlink(int start_idx, int end_idx, BucketUnlinkContext* context);
+  void try_rehash_table();
+  bool do_rehash();
+
 public:
+  // The symbol table
+  static SymbolTable* the_table() { return _the_table; }
+  size_t table_size();
+
   enum {
     symbol_alloc_batch_size = 8,
     // Pick initial size based on java -version size measurements
-    symbol_alloc_arena_size = 360*K
+    symbol_alloc_arena_size = 360*K // TODO (revisit)
   };
 
-  // The symbol table
-  static SymbolTable* the_table() { return _the_table; }
-
-  // Size of one bucket in the string table.  Used when checking for rollover.
-  static uint bucket_size() { return sizeof(HashtableBucket<mtSymbol>); }
-
   static void create_table() {
     assert(_the_table == NULL, "One symbol table allowed.");
     _the_table = new SymbolTable();
     initialize_symbols(symbol_alloc_arena_size);
   }
 
-  static unsigned int hash_symbol(const char* s, int len);
-  static unsigned int hash_shared_symbol(const char* s, int len);
+  static void unlink() {
+    do_check_concurrent_work();
+  }
+  static void do_check_concurrent_work();
+  static void do_concurrent_work(JavaThread* jt);
+  static bool has_work() { return the_table()->_has_work; }
 
+  // Probing
   static Symbol* lookup(const char* name, int len, TRAPS);
   // lookup only, won't add. Also calculate hash.
   static Symbol* lookup_only(const char* name, int len, unsigned int& hash);
-  // Only copy to C string to be added if lookup failed.
+  // adds new symbol if not found
   static Symbol* lookup(const Symbol* sym, int begin, int end, TRAPS);
-
-  static void release(Symbol* sym);
-
-  // Look up the address of the literal in the SymbolTable for this Symbol*
-  static Symbol** lookup_symbol_addr(Symbol* sym);
-
   // jchar (UTF16) version of lookups
   static Symbol* lookup_unicode(const jchar* name, int len, TRAPS);
   static Symbol* lookup_only_unicode(const jchar* name, int len, unsigned int& hash);
-
-  static void add(ClassLoaderData* loader_data,
-                  const constantPoolHandle& cp, int names_count,
-                  const char** names, int* lengths, int* cp_indices,
-                  unsigned int* hashValues, TRAPS);
-
-  // Release any dead symbols
-  static void unlink() {
-    int processed = 0;
-    int removed = 0;
-    unlink(&processed, &removed);
-  }
-  static void unlink(int* processed, int* removed);
-  // Release any dead symbols, possibly parallel version
-  static void possibly_parallel_unlink(int* processed, int* removed);
-
-  // iterate over symbols
-  static void symbols_do(SymbolClosure *cl);
-  static void metaspace_pointers_do(MetaspaceClosure* it);
-
-  // Symbol creation
-  static Symbol* new_symbol(const char* utf8_buffer, int length, TRAPS) {
-    assert(utf8_buffer != NULL, "just checking");
-    return lookup(utf8_buffer, length, THREAD);
-  }
-  static Symbol*       new_symbol(const char* name, TRAPS) {
-    return new_symbol(name, (int)strlen(name), THREAD);
-  }
-  static Symbol*       new_symbol(const Symbol* sym, int begin, int end, TRAPS) {
-    assert(begin <= end && end <= sym->utf8_length(), "just checking");
-    return lookup(sym, begin, end, THREAD);
-  }
-
-  // Create a symbol in the arena for symbols that are not deleted
-  static Symbol* new_permanent_symbol(const char* name, TRAPS);
-
-  // Symbol lookup
-  static Symbol* lookup(int index, const char* name, int len, TRAPS);
-
   // Needed for preloading classes in signatures when compiling.
   // Returns the symbol is already present in symbol table, otherwise
   // NULL.  NO ALLOCATION IS GUARANTEED!
@@ -246,27 +218,45 @@
     return lookup_only_unicode(name, len, ignore_hash);
   }
 
-  // Histogram
-  static void print_histogram()     PRODUCT_RETURN;
-  static void print()     PRODUCT_RETURN;
+  // Symbol creation
+  static Symbol* new_symbol(const char* utf8_buffer, int length, TRAPS) {
+    assert(utf8_buffer != NULL, "just checking");
+    return lookup(utf8_buffer, length, THREAD);
+  }
+  static Symbol* new_symbol(const char* name, TRAPS) {
+    return new_symbol(name, (int)strlen(name), THREAD);
+  }
+  static Symbol* new_symbol(const Symbol* sym, int begin, int end, TRAPS) {
+    assert(begin <= end && end <= sym->utf8_length(), "just checking");
+    return lookup(sym, begin, end, THREAD);
+  }
+  // Create a symbol in the arena for symbols that are not deleted
+  static Symbol* new_permanent_symbol(const char* name, TRAPS);
 
+  // Rehash the string table if it gets out of balance
+  static void rehash_table();
+  static bool needs_rehashing()
+    { return SymbolTable::the_table()->_needs_rehashing; }
+
+  // Heap dumper and CDS
+  static void symbols_do(SymbolClosure *cl);
+
+  // Sharing
+private:
+  static void copy_shared_symbol_table(CompactSymbolTableWriter* ch_table);
+public:
+  static void write_to_archive() NOT_CDS_RETURN;
+  static void serialize(SerializeClosure* soc) NOT_CDS_RETURN;
+  static void metaspace_pointers_do(MetaspaceClosure* it);
+
+  // Jcmd
+  static void dump(outputStream* st, bool verbose=false);
   // Debugging
   static void verify();
-  static void dump(outputStream* st, bool verbose=false);
   static void read(const char* filename, TRAPS);
 
-  // Sharing
-  static void write_to_archive();
-  static void serialize(SerializeClosure* soc);
-  static u4 encode_shared(Symbol* sym);
-  static Symbol* decode_shared(u4 offset);
-
-  // Rehash the symbol table if it gets out of balance
-  static void rehash_table();
-  static bool needs_rehashing()         { return _needs_rehashing; }
-  // Parallel chunked scanning
-  static void clear_parallel_claimed_index() { _parallel_claimed_idx = 0; }
-  static int parallel_claimed_index()        { return _parallel_claimed_idx; }
+  // Histogram
+  static void print_histogram() PRODUCT_RETURN;
 };
 
 #endif // SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -25,7 +25,6 @@
 #include "precompiled.hpp"
 #include "classfile/metadataOnStackMark.hpp"
 #include "classfile/stringTable.hpp"
-#include "classfile/symbolTable.hpp"
 #include "code/codeCache.hpp"
 #include "code/icBuffer.hpp"
 #include "gc/g1/g1Allocator.inline.hpp"
@@ -3256,56 +3255,40 @@
                undo_waste * HeapWordSize / K);
 }
 
-class G1StringAndSymbolCleaningTask : public AbstractGangTask {
+class G1StringCleaningTask : public AbstractGangTask {
 private:
   BoolObjectClosure* _is_alive;
   G1StringDedupUnlinkOrOopsDoClosure _dedup_closure;
   OopStorage::ParState<false /* concurrent */, false /* const */> _par_state_string;
 
   int _initial_string_table_size;
-  int _initial_symbol_table_size;
 
   bool  _process_strings;
   int _strings_processed;
   int _strings_removed;
 
-  bool  _process_symbols;
-  int _symbols_processed;
-  int _symbols_removed;
-
   bool _process_string_dedup;
 
 public:
-  G1StringAndSymbolCleaningTask(BoolObjectClosure* is_alive, bool process_strings, bool process_symbols, bool process_string_dedup) :
-    AbstractGangTask("String/Symbol Unlinking"),
+  G1StringCleaningTask(BoolObjectClosure* is_alive, bool process_strings, bool process_string_dedup) :
+    AbstractGangTask("String Unlinking"),
     _is_alive(is_alive),
     _dedup_closure(is_alive, NULL, false),
     _par_state_string(StringTable::weak_storage()),
     _process_strings(process_strings), _strings_processed(0), _strings_removed(0),
-    _process_symbols(process_symbols), _symbols_processed(0), _symbols_removed(0),
     _process_string_dedup(process_string_dedup) {
 
     _initial_string_table_size = (int) StringTable::the_table()->table_size();
-    _initial_symbol_table_size = SymbolTable::the_table()->table_size();
-    if (process_symbols) {
-      SymbolTable::clear_parallel_claimed_index();
-    }
     if (process_strings) {
       StringTable::reset_dead_counter();
     }
   }
 
-  ~G1StringAndSymbolCleaningTask() {
-    guarantee(!_process_symbols || SymbolTable::parallel_claimed_index() >= _initial_symbol_table_size,
-              "claim value %d after unlink less than initial symbol table size %d",
-              SymbolTable::parallel_claimed_index(), _initial_symbol_table_size);
-
+  ~G1StringCleaningTask() {
     log_info(gc, stringtable)(
-        "Cleaned string and symbol table, "
-        "strings: " SIZE_FORMAT " processed, " SIZE_FORMAT " removed, "
-        "symbols: " SIZE_FORMAT " processed, " SIZE_FORMAT " removed",
-        strings_processed(), strings_removed(),
-        symbols_processed(), symbols_removed());
+        "Cleaned string table, "
+        "strings: " SIZE_FORMAT " processed, " SIZE_FORMAT " removed",
+        strings_processed(), strings_removed());
     if (_process_strings) {
       StringTable::finish_dead_counter();
     }
@@ -3314,18 +3297,11 @@
   void work(uint worker_id) {
     int strings_processed = 0;
     int strings_removed = 0;
-    int symbols_processed = 0;
-    int symbols_removed = 0;
     if (_process_strings) {
       StringTable::possibly_parallel_unlink(&_par_state_string, _is_alive, &strings_processed, &strings_removed);
       Atomic::add(strings_processed, &_strings_processed);
       Atomic::add(strings_removed, &_strings_removed);
     }
-    if (_process_symbols) {
-      SymbolTable::possibly_parallel_unlink(&symbols_processed, &symbols_removed);
-      Atomic::add(symbols_processed, &_symbols_processed);
-      Atomic::add(symbols_removed, &_symbols_removed);
-    }
     if (_process_string_dedup) {
       G1StringDedup::parallel_unlink(&_dedup_closure, worker_id);
     }
@@ -3333,9 +3309,6 @@
 
   size_t strings_processed() const { return (size_t)_strings_processed; }
   size_t strings_removed()   const { return (size_t)_strings_removed; }
-
-  size_t symbols_processed() const { return (size_t)_symbols_processed; }
-  size_t symbols_removed()   const { return (size_t)_symbols_removed; }
 };
 
 class G1CodeCacheUnloadingTask {
@@ -3585,7 +3558,7 @@
 class G1ParallelCleaningTask : public AbstractGangTask {
 private:
   bool                          _unloading_occurred;
-  G1StringAndSymbolCleaningTask _string_symbol_task;
+  G1StringCleaningTask          _string_task;
   G1CodeCacheUnloadingTask      _code_cache_task;
   G1KlassCleaningTask           _klass_cleaning_task;
   G1ResolvedMethodCleaningTask  _resolved_method_cleaning_task;
@@ -3595,7 +3568,7 @@
   G1ParallelCleaningTask(BoolObjectClosure* is_alive, uint num_workers, bool unloading_occurred) :
       AbstractGangTask("Parallel Cleaning"),
       _unloading_occurred(unloading_occurred),
-      _string_symbol_task(is_alive, true, true, G1StringDedup::is_enabled()),
+      _string_task(is_alive, true, G1StringDedup::is_enabled()),
       _code_cache_task(num_workers, is_alive, unloading_occurred),
       _klass_cleaning_task(),
       _resolved_method_cleaning_task() {
@@ -3609,8 +3582,8 @@
     // Let the threads mark that the first pass is done.
     _code_cache_task.barrier_mark(worker_id);
 
-    // Clean the Strings and Symbols.
-    _string_symbol_task.work(worker_id);
+    // Clean the Strings.
+    _string_task.work(worker_id);
 
     // Clean unreferenced things in the ResolvedMethodTable
     _resolved_method_cleaning_task.work();
@@ -3642,16 +3615,14 @@
 
 void G1CollectedHeap::partial_cleaning(BoolObjectClosure* is_alive,
                                        bool process_strings,
-                                       bool process_symbols,
                                        bool process_string_dedup) {
-  if (!process_strings && !process_symbols && !process_string_dedup) {
+  if (!process_strings && !process_string_dedup) {
     // Nothing to clean.
     return;
   }
 
-  G1StringAndSymbolCleaningTask g1_unlink_task(is_alive, process_strings, process_symbols, process_string_dedup);
+  G1StringCleaningTask g1_unlink_task(is_alive, process_strings, process_string_dedup);
   workers()->run_task(&g1_unlink_task);
-
 }
 
 class G1RedirtyLoggedCardsTask : public AbstractGangTask {
@@ -4045,7 +4016,7 @@
   process_discovered_references(per_thread_states);
 
   // FIXME
-  // CM's reference processing also cleans up the string and symbol tables.
+  // CM's reference processing also cleans up the string table.
   // Should we do that here also? We could, but it is a serial operation
   // and could significantly increase the pause time.
 
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -1324,9 +1324,8 @@
   // Partial cleaning used when class unloading is disabled.
   // Let the caller choose what structures to clean out:
   // - StringTable
-  // - SymbolTable
   // - StringDeduplication structures
-  void partial_cleaning(BoolObjectClosure* is_alive, bool unlink_strings, bool unlink_symbols, bool unlink_string_dedup);
+  void partial_cleaning(BoolObjectClosure* is_alive, bool unlink_strings, bool unlink_string_dedup);
 
   // Complete cleaning used when class unloading is enabled.
   // Cleans out all structures handled by partial_cleaning and also the CodeCache.
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -24,7 +24,6 @@
 
 #include "precompiled.hpp"
 #include "classfile/metadataOnStackMark.hpp"
-#include "classfile/symbolTable.hpp"
 #include "code/codeCache.hpp"
 #include "gc/g1/g1BarrierSet.hpp"
 #include "gc/g1/g1CollectedHeap.inline.hpp"
@@ -1578,8 +1577,8 @@
   // Is alive closure.
   G1CMIsAliveClosure g1_is_alive(_g1h);
 
-  // Inner scope to exclude the cleaning of the string and symbol
-  // tables from the displayed time.
+  // Inner scope to exclude the cleaning of the string table
+  // from the displayed time.
   {
     GCTraceTime(Debug, gc, phases) debug("Reference Processing", _gc_timer_cm);
 
@@ -1673,16 +1672,16 @@
     WeakProcessor::weak_oops_do(&g1_is_alive, &do_nothing_cl);
   }
 
-  // Unload Klasses, String, Symbols, Code Cache, etc.
+  // Unload Klasses, String, Code Cache, etc.
   if (ClassUnloadingWithConcurrentMark) {
     GCTraceTime(Debug, gc, phases) debug("Class Unloading", _gc_timer_cm);
     bool purged_classes = SystemDictionary::do_unloading(_gc_timer_cm, false /* Defer cleaning */);
     _g1h->complete_cleaning(&g1_is_alive, purged_classes);
   } else {
     GCTraceTime(Debug, gc, phases) debug("Cleanup", _gc_timer_cm);
-    // No need to clean string table and symbol table as they are treated as strong roots when
+    // No need to clean string table as it is treated as strong roots when
     // class unloading is disabled.
-    _g1h->partial_cleaning(&g1_is_alive, false, false, G1StringDedup::is_enabled());
+    _g1h->partial_cleaning(&g1_is_alive, false, G1StringDedup::is_enabled());
   }
 }
 
--- a/src/hotspot/share/gc/g1/g1FullCollector.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -226,8 +226,8 @@
     _heap->complete_cleaning(&_is_alive, purged_class);
   } else {
     GCTraceTime(Debug, gc, phases) debug("Phase 1: String and Symbol Tables Cleanup", scope()->timer());
-    // If no class unloading just clean out strings and symbols.
-    _heap->partial_cleaning(&_is_alive, true, true, G1StringDedup::is_enabled());
+    // If no class unloading just clean out strings.
+    _heap->partial_cleaning(&_is_alive, true, G1StringDedup::is_enabled());
   }
 
   scope()->tracer()->report_object_count_after_gc(&_is_alive);
--- a/src/hotspot/share/gc/z/zRootsIterator.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/gc/z/zRootsIterator.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -24,7 +24,6 @@
 #include "precompiled.hpp"
 #include "classfile/classLoaderData.hpp"
 #include "classfile/stringTable.hpp"
-#include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "code/codeCache.hpp"
 #include "compiler/oopMap.hpp"
@@ -74,7 +73,6 @@
 static const ZStatSubPhase ZSubPhasePauseWeakRootsJNIWeakHandles("Pause Weak Roots JNIWeakHandles");
 static const ZStatSubPhase ZSubPhasePauseWeakRootsJVMTIWeakExport("Pause Weak Roots JVMTIWeakExport");
 static const ZStatSubPhase ZSubPhasePauseWeakRootsJFRWeak("Pause Weak Roots JFRWeak");
-static const ZStatSubPhase ZSubPhasePauseWeakRootsSymbolTable("Pause Weak Roots SymbolTable");
 static const ZStatSubPhase ZSubPhasePauseWeakRootsStringTable("Pause Weak Roots StringTable");
 
 static const ZStatSubPhase ZSubPhaseConcurrentWeakRoots("Concurrent Weak Roots");
@@ -302,11 +300,9 @@
     _jfr_weak(this),
     _vm_weak_handles(this),
     _jni_weak_handles(this),
-    _symbol_table(this),
     _string_table(this) {
   assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
   ZStatTimer timer(ZSubPhasePauseWeakRootsSetup);
-  SymbolTable::clear_parallel_claimed_index();
   StringTable::reset_dead_counter();
 }
 
@@ -337,12 +333,6 @@
 #endif
 }
 
-void ZWeakRootsIterator::do_symbol_table(BoolObjectClosure* is_alive, OopClosure* cl) {
-  ZStatTimer timer(ZSubPhasePauseWeakRootsSymbolTable);
-  int dummy;
-  SymbolTable::possibly_parallel_unlink(&dummy, &dummy);
-}
-
 class ZStringTableDeadCounterBoolObjectClosure : public BoolObjectClosure  {
 private:
   BoolObjectClosure* const _cl;
@@ -375,9 +365,6 @@
 
 void ZWeakRootsIterator::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* cl) {
   ZStatTimer timer(ZSubPhasePauseWeakRoots);
-  if (ZSymbolTableUnloading) {
-    _symbol_table.weak_oops_do(is_alive, cl);
-  }
   if (ZWeakRoots) {
     _jvmti_weak_export.weak_oops_do(is_alive, cl);
     _jfr_weak.weak_oops_do(is_alive, cl);
--- a/src/hotspot/share/gc/z/zRootsIterator.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/gc/z/zRootsIterator.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -130,14 +130,12 @@
   void do_jni_weak_handles(BoolObjectClosure* is_alive, OopClosure* cl);
   void do_jvmti_weak_export(BoolObjectClosure* is_alive, OopClosure* cl);
   void do_jfr_weak(BoolObjectClosure* is_alive, OopClosure* cl);
-  void do_symbol_table(BoolObjectClosure* is_alive, OopClosure* cl);
   void do_string_table(BoolObjectClosure* is_alive, OopClosure* cl);
 
   ZSerialWeakOopsDo<ZWeakRootsIterator, &ZWeakRootsIterator::do_jvmti_weak_export>  _jvmti_weak_export;
   ZSerialWeakOopsDo<ZWeakRootsIterator, &ZWeakRootsIterator::do_jfr_weak>           _jfr_weak;
   ZParallelWeakOopsDo<ZWeakRootsIterator, &ZWeakRootsIterator::do_vm_weak_handles>  _vm_weak_handles;
   ZParallelWeakOopsDo<ZWeakRootsIterator, &ZWeakRootsIterator::do_jni_weak_handles> _jni_weak_handles;
-  ZParallelWeakOopsDo<ZWeakRootsIterator, &ZWeakRootsIterator::do_symbol_table>     _symbol_table;
   ZParallelWeakOopsDo<ZWeakRootsIterator, &ZWeakRootsIterator::do_string_table>     _string_table;
 
 public:
--- a/src/hotspot/share/gc/z/z_globals.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/gc/z/z_globals.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -79,9 +79,6 @@
   diagnostic(bool, ZVerifyForwarding, false,                                \
           "Verify forwarding tables")                                       \
                                                                             \
-  diagnostic(bool, ZSymbolTableUnloading, false,                            \
-          "Unload unused VM symbols")                                       \
-                                                                            \
   diagnostic(bool, ZWeakRoots, true,                                        \
           "Treat JNI WeakGlobalRefs and StringTable as weak roots")         \
                                                                             \
--- a/src/hotspot/share/logging/logPrefix.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/logging/logPrefix.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -80,6 +80,7 @@
   LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, reloc)) \
   LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, start)) \
   LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, stringtable)) \
+  LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, symboltable)) \
   LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, sweep)) \
   LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, task)) \
   LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, task, start)) \
--- a/src/hotspot/share/logging/logTag.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/logging/logTag.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -147,6 +147,7 @@
   LOG_TAG(stats) \
   LOG_TAG(stringdedup) \
   LOG_TAG(stringtable) \
+  LOG_TAG(symboltable) \
   LOG_TAG(stackmap) \
   LOG_TAG(subclass) \
   LOG_TAG(survivor) \
--- a/src/hotspot/share/memory/arena.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/memory/arena.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018, 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
@@ -199,12 +199,18 @@
   }
 
   // Fast delete in area.  Common case is: NOP (except for storage reclaimed)
-  void Afree(void *ptr, size_t size) {
+  bool Afree(void *ptr, size_t size) {
 #ifdef ASSERT
     if (ZapResourceArea) memset(ptr, badResourceValue, size); // zap freed memory
-    if (UseMallocOnly) return;
+    if (UseMallocOnly) return true;
 #endif
-    if (((char*)ptr) + size == _hwm) _hwm = (char*)ptr;
+    if (((char*)ptr) + size == _hwm) {
+      _hwm = (char*)ptr;
+      return true;
+    } else {
+      // Unable to fast free, so we just drop it.
+      return false;
+    }
   }
 
   void *Arealloc( void *old_ptr, size_t old_size, size_t new_size,
--- a/src/hotspot/share/oops/symbol.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/oops/symbol.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -318,4 +318,4 @@
 }
 
 // SymbolTable prints this in its statistics
-NOT_PRODUCT(int Symbol::_total_count = 0;)
+NOT_PRODUCT(size_t Symbol::_total_count = 0;)
--- a/src/hotspot/share/oops/symbol.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/oops/symbol.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -256,7 +256,7 @@
   // only for getting its vtable pointer.
   Symbol() { }
 
-  static int _total_count;
+  static size_t _total_count;
 #endif
 };
 
--- a/src/hotspot/share/runtime/serviceThread.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/runtime/serviceThread.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -24,6 +24,7 @@
 
 #include "precompiled.hpp"
 #include "classfile/stringTable.hpp"
+#include "classfile/symbolTable.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/serviceThread.hpp"
@@ -84,6 +85,7 @@
     bool has_dcmd_notification_event = false;
     bool acs_notify = false;
     bool stringtable_work = false;
+    bool symboltable_work = false;
     JvmtiDeferredEvent jvmti_event;
     {
       // Need state transition ThreadBlockInVM so that this thread
@@ -101,7 +103,8 @@
              !(has_jvmti_events = JvmtiDeferredEventQueue::has_events()) &&
               !(has_gc_notification_event = GCNotifier::has_event()) &&
               !(has_dcmd_notification_event = DCmdFactory::has_pending_jmx_notification()) &&
-              !(stringtable_work = StringTable::has_work())) {
+              !(stringtable_work = StringTable::has_work()) &&
+              !(symboltable_work = SymbolTable::has_work())) {
         // wait until one of the sensors has pending requests, or there is a
         // pending JVMTI event or JMX GC notification to post
         Service_lock->wait(Mutex::_no_safepoint_check_flag);
@@ -116,6 +119,10 @@
       StringTable::do_concurrent_work(jt);
     }
 
+    if (symboltable_work) {
+      SymbolTable::do_concurrent_work(jt);
+    }
+
     if (has_jvmti_events) {
       jvmti_event.post();
     }
--- a/src/hotspot/share/runtime/vmStructs.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/runtime/vmStructs.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -162,12 +162,8 @@
 
 typedef HashtableEntry<intptr_t, mtInternal>  IntptrHashtableEntry;
 typedef Hashtable<intptr_t, mtInternal>       IntptrHashtable;
-typedef Hashtable<Symbol*, mtSymbol>          SymbolHashtable;
-typedef HashtableEntry<Symbol*, mtClass>      SymbolHashtableEntry;
 typedef Hashtable<InstanceKlass*, mtClass>       KlassHashtable;
 typedef HashtableEntry<InstanceKlass*, mtClass>  KlassHashtableEntry;
-typedef CompactHashtable<Symbol*, char>       SymbolCompactHashTable;
-typedef RehashableHashtable<Symbol*, mtSymbol>   RehashableSymbolHashtable;
 
 typedef PaddedEnd<ObjectMonitor>              PaddedObjectMonitor;
 
@@ -467,24 +463,6 @@
      static_field(PerfMemory,                  _prologue,                                     PerfDataPrologue*)                     \
      static_field(PerfMemory,                  _initialized,                                  int)                                   \
                                                                                                                                      \
-  /***************/                                                                                                                  \
-  /* SymbolTable */                                                                                                                  \
-  /***************/                                                                                                                  \
-                                                                                                                                     \
-     static_field(SymbolTable,                 _the_table,                                    SymbolTable*)                          \
-     static_field(SymbolTable,                 _shared_table,                                 SymbolCompactHashTable)                \
-     static_field(RehashableSymbolHashtable,   _seed,                                         juint)                                 \
-                                                                                                                                     \
-  /********************/                                                                                                             \
-  /* CompactHashTable */                                                                                                             \
-  /********************/                                                                                                             \
-                                                                                                                                     \
-  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 */                                                                                                             \
   /********************/                                                                                                             \
@@ -1351,15 +1329,13 @@
   declare_toplevel_type(PerfMemory)                                       \
   declare_type(PerfData, CHeapObj<mtInternal>)                            \
                                                                           \
-  /*********************************/                                     \
-  /* SymbolTable, SystemDictionary */                                     \
-  /*********************************/                                     \
+  /********************/                                                  \
+  /* SystemDictionary */                                                  \
+  /********************/                                                  \
                                                                           \
   declare_toplevel_type(BasicHashtable<mtInternal>)                       \
     declare_type(IntptrHashtable, BasicHashtable<mtInternal>)             \
   declare_toplevel_type(BasicHashtable<mtSymbol>)                         \
-    declare_type(RehashableSymbolHashtable, BasicHashtable<mtSymbol>)     \
-  declare_type(SymbolTable, SymbolHashtable)                              \
     declare_type(Dictionary, KlassHashtable)                              \
   declare_toplevel_type(BasicHashtableEntry<mtInternal>)                  \
   declare_type(IntptrHashtableEntry, BasicHashtableEntry<mtInternal>)     \
@@ -1373,8 +1349,6 @@
   declare_toplevel_type(Arena)                                            \
     declare_type(ResourceArea, Arena)                                     \
                                                                           \
-  declare_toplevel_type(SymbolCompactHashTable)                           \
-                                                                          \
   /***********************************************************/           \
   /* Thread hierarchy (needed for run-time type information) */           \
   /***********************************************************/           \
--- a/src/hotspot/share/utilities/concurrentHashTable.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/utilities/concurrentHashTable.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -309,7 +309,7 @@
   // Insert which handles a number of cases.
   template <typename LOOKUP_FUNC, typename VALUE_FUNC, typename CALLBACK_FUNC>
   bool internal_insert(Thread* thread, LOOKUP_FUNC& lookup_f, VALUE_FUNC& value_f,
-                       CALLBACK_FUNC& callback, bool* grow_hint = NULL);
+                       CALLBACK_FUNC& callback, bool* grow_hint = NULL, bool* clean_hint = NULL);
 
   // Returns true if an item matching LOOKUP_FUNC is removed.
   // Calls DELETE_FUNC before destroying the node.
@@ -396,8 +396,8 @@
   // value already exists.
   template <typename LOOKUP_FUNC, typename VALUE_FUNC, typename CALLBACK_FUNC>
   bool get_insert_lazy(Thread* thread, LOOKUP_FUNC& lookup_f, VALUE_FUNC& val_f,
-                       CALLBACK_FUNC& callback_f, bool* grow_hint = NULL) {
-    return !internal_insert(thread, lookup_f, val_f, callback_f, grow_hint);
+                       CALLBACK_FUNC& callback_f, bool* grow_hint = NULL, bool* clean_hint = NULL) {
+    return !internal_insert(thread, lookup_f, val_f, callback_f, grow_hint, clean_hint);
   }
 
   // Same without CALLBACK_FUNC.
@@ -436,9 +436,9 @@
   // LOOKUP_FUNC.
   template <typename LOOKUP_FUNC>
   bool insert(Thread* thread, LOOKUP_FUNC& lookup_f, const VALUE& value,
-              bool* grow_hint = NULL) {
+              bool* grow_hint = NULL, bool* clean_hint = NULL) {
     LazyValueRetrieve vp(value);
-    return internal_insert(thread, lookup_f, vp, noOp, grow_hint);
+    return internal_insert(thread, lookup_f, vp, noOp, grow_hint, clean_hint);
   }
 
   // This does a fast unsafe insert and can thus only be used when there is no
--- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -540,6 +540,8 @@
 inline void ConcurrentHashTable<VALUE, CONFIG, F>::
   delete_in_bucket(Thread* thread, Bucket* bucket, LOOKUP_FUNC& lookup_f)
 {
+  assert(bucket->is_locked(), "Must be locked.");
+
   size_t dels = 0;
   Node* ndel[BULK_DELETE_LIMIT];
   Node* const volatile * rem_n_prev = bucket->first_ptr();
@@ -874,7 +876,7 @@
 template <typename LOOKUP_FUNC, typename VALUE_FUNC, typename CALLBACK_FUNC>
 inline bool ConcurrentHashTable<VALUE, CONFIG, F>::
   internal_insert(Thread* thread, LOOKUP_FUNC& lookup_f, VALUE_FUNC& value_f,
-                  CALLBACK_FUNC& callback, bool* grow_hint)
+                  CALLBACK_FUNC& callback, bool* grow_hint, bool* clean_hint)
 {
   bool ret = false;
   bool clean = false;
@@ -925,15 +927,20 @@
   } else if (i == 0 && clean) {
     // We only do cleaning on fast inserts.
     Bucket* bucket = get_bucket_locked(thread, lookup_f.get_hash());
-    assert(bucket->is_locked(), "Must be locked.");
     delete_in_bucket(thread, bucket, lookup_f);
     bucket->unlock();
+
+    clean = false;
   }
 
   if (grow_hint != NULL) {
     *grow_hint = loops > _grow_hint;
   }
 
+  if (clean_hint != NULL) {
+    *clean_hint = clean;
+  }
+
   return ret;
 }
 
--- a/src/hotspot/share/utilities/globalCounter.cpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/utilities/globalCounter.cpp	Tue Aug 14 18:42:14 2018 -0500
@@ -61,6 +61,11 @@
   // Atomic::add must provide fence since we have storeload dependency.
   volatile uintx gbl_cnt = Atomic::add((uintx)COUNTER_INCREMENT, &_global_counter._counter,
                                        memory_order_conservative);
+  // Handle bootstrap
+  if (Threads::number_of_threads() == 0) {
+    return;
+  }
+
   // Do all RCU threads.
   CounterThreadCheck ctc(gbl_cnt);
   for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
--- a/src/hotspot/share/utilities/globalDefinitions.hpp	Tue Aug 14 14:08:04 2018 -0700
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp	Tue Aug 14 18:42:14 2018 -0500
@@ -422,13 +422,14 @@
 const int max_method_code_size = 64*K - 1;  // JVM spec, 2nd ed. section 4.8.1 (p.134)
 
 //----------------------------------------------------------------------------------------------------
-// Default and minimum StringTableSize values
+// Default and minimum StringTable and SymbolTable size values
+// Must be a power of 2
 
-const int defaultStringTableSize = NOT_LP64(1024) LP64_ONLY(65536);
-const int minimumStringTableSize = 128;
+const size_t defaultStringTableSize = NOT_LP64(1024) LP64_ONLY(65536);
+const size_t minimumStringTableSize = 128;
 
-const int defaultSymbolTableSize = 20011;
-const int minimumSymbolTableSize = 1009;
+const size_t defaultSymbolTableSize = 32768; // 2^15
+const size_t minimumSymbolTableSize = 1024;
 
 
 //----------------------------------------------------------------------------------------------------
--- a/test/hotspot/jtreg/gc/g1/TestStringSymbolTableStats.java	Tue Aug 14 14:08:04 2018 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2013, 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 TestStringSymbolTableStats.java
- * @bug 8027476 8027455
- * @summary Ensure that the G1TraceStringSymbolTableScrubbing prints the expected message.
- * @key gc
- * @requires vm.gc.G1
- * @library /test/lib
- * @modules java.base/jdk.internal.misc
- *          java.management
- */
-
-import jdk.test.lib.process.OutputAnalyzer;
-import jdk.test.lib.process.ProcessTools;
-
-public class TestStringSymbolTableStats {
-  public static void main(String[] args) throws Exception {
-
-    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
-                                                              "-XX:+UnlockExperimentalVMOptions",
-                                                              "-Xlog:gc+stringtable=trace",
-                                                              SystemGCTest.class.getName());
-
-    OutputAnalyzer output = new OutputAnalyzer(pb.start());
-
-    System.out.println("Output:\n" + output.getOutput());
-
-    output.shouldMatch("GC\\(\\d+\\) Cleaned string and symbol table");
-    output.shouldHaveExitValue(0);
-  }
-
-  static class SystemGCTest {
-    public static void main(String [] args) {
-      System.out.println("Calling System.gc()");
-      System.gc();
-    }
-  }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/g1/TestStringTableStats.java	Tue Aug 14 18:42:14 2018 -0500
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, 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 TestStringTableStats.java
+ * @bug 8027476 8027455
+ * @summary Ensure that the G1TraceStringTableScrubbing prints the expected message.
+ * @key gc
+ * @requires vm.gc.G1
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class TestStringTableStats {
+  public static void main(String[] args) throws Exception {
+
+    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
+                                                              "-XX:+UnlockExperimentalVMOptions",
+                                                              "-Xlog:gc+stringtable=trace",
+                                                              SystemGCTest.class.getName());
+
+    OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+    System.out.println("Output:\n" + output.getOutput());
+
+    output.shouldMatch("GC\\(\\d+\\) Cleaned string table");
+    output.shouldHaveExitValue(0);
+  }
+
+  static class SystemGCTest {
+    public static void main(String [] args) {
+      System.out.println("Calling System.gc()");
+      System.gc();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/symboltable/ShortLivedSymbolCleanup.java	Tue Aug 14 18:42:14 2018 -0500
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2018, 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 8195100
+ * @summary a short lived Symbol should be cleaned up
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ * @requires (vm.debug == true)
+ */
+
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import java.util.Scanner;
+
+public class ShortLivedSymbolCleanup {
+
+  static int getSymbolTableSize(ProcessBuilder pb) throws Exception {
+    int size = 0;
+
+    OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
+    String output = analyzer.getStdout();
+    analyzer.shouldHaveExitValue(0);
+
+    // Split string into lines using platform independent end of line marker.
+    String[] lines = output.split("\\R");
+    for (String line : lines) {
+      if (line.contains("Start size")) {
+        // ex. "[0.023s][trace][symboltable] Start size: 32768 (15)"
+        Scanner scanner = new Scanner(line);
+        scanner.next(); // skip "[0.023s][trace][symboltable]"
+        scanner.next(); // skip "Start"
+        scanner.next(); // skip "size:"
+        size = Integer.parseInt(scanner.next()); // process "32768"
+        scanner.close();
+      }
+    }
+
+    return size;
+  }
+
+  static void analyzeOutputOn(int size, ProcessBuilder pb) throws Exception {
+    OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
+    String output = analyzer.getStdout();
+    analyzer.shouldHaveExitValue(0);
+
+    // Split string into lines using platform independent end of line marker.
+    String[] lines = output.split("\\R");
+    for (String line : lines) {
+      if (line.startsWith("  Total removed")) {
+        // ex. "Total removed              13309"
+        Scanner scanner = new Scanner(line);
+        scanner.next(); // skip "Total"
+        scanner.next(); // skip "removed"
+        int removed = Integer.parseInt(scanner.next()); // process "13309"
+        scanner.close();
+
+        if (removed < (size/2)) {
+          System.out.println(output);
+          // We should have removed at least half of the temporary Symbols
+          throw new RuntimeException("Did not clean dead temporary Symbols [removed:"+removed+", size:"+size+"]");
+        }
+      }
+    }
+  }
+
+  public static void main(String[] args) throws Exception {
+
+      if (Platform.isDebugBuild()) {
+        {
+          ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:symboltable=trace",
+                                                                    "-version");
+          int size = getSymbolTableSize(pb);
+
+          pb = ProcessTools.createJavaProcessBuilder("-XX:+PrintSymbolTableSizeHistogram",
+                                                                    LotsOfTempSymbols.class.getName(),
+                                                                    Integer.toString(size));
+          analyzeOutputOn(size, pb);
+        }
+     }
+  }
+
+  static class LotsOfTempSymbols {
+    public static void main(String [] args) {
+      int size = 2*Integer.parseInt(args[0]);
+      // Create enough temporary Symbols, that we are
+      // guranteed to insert into every bucket twice,
+      // and therefore have the table check for dead entries
+      for (int i=0; i<size; i++) {
+        try {
+          Class.forName(String.format("%05d", i), false, null);
+        } catch (java.lang.ClassNotFoundException e) {}
+      }
+    }
+  }
+}
\ No newline at end of file