8213229: Investigate treating StringTable as weak in young collections
authortschatzl
Tue, 29 Jan 2019 11:30:17 +0100
changeset 53536 482109fae02b
parent 53535 ce77e4d928f3
child 53537 b5c92b95fe45
8213229: Investigate treating StringTable as weak in young collections Reviewed-by: zgu, kbarrett
src/hotspot/share/classfile/stringTable.cpp
src/hotspot/share/classfile/stringTable.hpp
src/hotspot/share/gc/cms/cmsHeap.cpp
src/hotspot/share/gc/cms/cmsHeap.hpp
src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp
src/hotspot/share/gc/cms/parNewGeneration.cpp
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/g1/g1FullGCAdjustTask.cpp
src/hotspot/share/gc/g1/g1FullGCAdjustTask.hpp
src/hotspot/share/gc/g1/g1FullGCMarkTask.cpp
src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp
src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp
src/hotspot/share/gc/g1/g1HeapVerifier.cpp
src/hotspot/share/gc/g1/g1RootProcessor.cpp
src/hotspot/share/gc/g1/g1RootProcessor.hpp
src/hotspot/share/gc/g1/g1StringDedup.cpp
src/hotspot/share/gc/g1/g1StringDedup.hpp
src/hotspot/share/gc/parallel/psMarkSweep.cpp
src/hotspot/share/gc/parallel/psParallelCompact.cpp
src/hotspot/share/gc/parallel/psScavenge.cpp
src/hotspot/share/gc/serial/genMarkSweep.cpp
src/hotspot/share/gc/shared/genCollectedHeap.cpp
src/hotspot/share/gc/shared/genCollectedHeap.hpp
src/hotspot/share/gc/shared/parallelCleaning.cpp
src/hotspot/share/gc/shared/parallelCleaning.hpp
src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp
src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp
src/hotspot/share/gc/shared/weakProcessor.cpp
src/hotspot/share/gc/shared/weakProcessor.inline.hpp
src/hotspot/share/gc/shared/weakProcessorPhaseTimes.cpp
src/hotspot/share/gc/shared/weakProcessorPhaseTimes.hpp
src/hotspot/share/gc/shared/weakProcessorPhases.cpp
src/hotspot/share/gc/shared/weakProcessorPhases.hpp
src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp
src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp
src/hotspot/share/logging/logPrefix.hpp
test/hotspot/jtreg/gc/g1/TestGCLogMessages.java
test/hotspot/jtreg/gc/g1/TestStringTableStats.java
test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java
--- a/src/hotspot/share/classfile/stringTable.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/classfile/stringTable.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -376,68 +376,11 @@
   } while(true);
 }
 
-// GC support
-class StringTableIsAliveCounter : public BoolObjectClosure {
-  BoolObjectClosure* _real_boc;
- public:
-  size_t _count;
-  size_t _count_total;
-  StringTableIsAliveCounter(BoolObjectClosure* boc) : _real_boc(boc), _count(0),
-                                                      _count_total(0) {}
-  bool do_object_b(oop obj) {
-    bool ret = _real_boc->do_object_b(obj);
-    if (!ret) {
-      ++_count;
-    }
-    ++_count_total;
-    return ret;
-  }
-};
-
-void StringTable::unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f,
-                                    size_t* processed, size_t* removed) {
-  DoNothingClosure dnc;
-  assert(is_alive != NULL, "No closure");
-  StringTableIsAliveCounter stiac(is_alive);
-  OopClosure* tmp = f != NULL ? f : &dnc;
-
-  StringTable::the_table()->_weak_handles->weak_oops_do(&stiac, tmp);
-
-  // This is the serial case without ParState.
-  // Just set the correct number and check for a cleaning phase.
-  the_table()->_uncleaned_items_count = stiac._count;
-  StringTable::the_table()->check_concurrent_work();
-
-  if (processed != NULL) {
-    *processed = stiac._count_total;
-  }
-  if (removed != NULL) {
-    *removed = stiac._count;
-  }
-}
-
 void StringTable::oops_do(OopClosure* f) {
   assert(f != NULL, "No closure");
   StringTable::the_table()->_weak_handles->oops_do(f);
 }
 
-void StringTable::possibly_parallel_unlink(
-   OopStorage::ParState<false, false>* _par_state_string, BoolObjectClosure* cl,
-   size_t* processed, size_t* removed)
-{
-  DoNothingClosure dnc;
-  assert(cl != NULL, "No closure");
-  StringTableIsAliveCounter stiac(cl);
-
-  _par_state_string->weak_oops_do(&stiac, &dnc);
-
-  // Accumulate the dead strings.
-  the_table()->add_items_to_clean(stiac._count);
-
-  *processed = stiac._count_total;
-  *removed = stiac._count;
-}
-
 void StringTable::possibly_parallel_oops_do(
    OopStorage::ParState<false /* concurrent */, false /* const */>*
    _par_state_string, OopClosure* f)
--- a/src/hotspot/share/classfile/stringTable.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/classfile/stringTable.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -128,20 +128,10 @@
     the_table()->add_items_to_clean(ndead);
   }
 
-  //   Delete pointers to otherwise-unreachable objects.
-  static void unlink(BoolObjectClosure* cl) {
-    unlink_or_oops_do(cl);
-  }
-  static void unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* f = NULL,
-                                size_t* processed = NULL, size_t* removed = NULL);
-
   // Serially invoke "f->do_oop" on the locations of all oops in the table.
   static void oops_do(OopClosure* f);
 
   // Possibly parallel versions of the above
-  static void possibly_parallel_unlink(
-     OopStorage::ParState<false /* concurrent */, false /* const*/>* par_state_string,
-     BoolObjectClosure* cl, size_t* processed, size_t* removed);
   static void possibly_parallel_oops_do(
      OopStorage::ParState<false /* concurrent */, false /* const*/>* par_state_string,
      OopClosure* f);
--- a/src/hotspot/share/gc/cms/cmsHeap.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/cms/cmsHeap.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -225,15 +225,11 @@
                                 ScanningOption so,
                                 bool only_strong_roots,
                                 OopsInGenClosure* root_closure,
-                                CLDClosure* cld_closure,
-                                OopStorage::ParState<false, false>* par_state_string) {
+                                CLDClosure* cld_closure) {
   MarkingCodeBlobClosure mark_code_closure(root_closure, !CodeBlobToOopClosure::FixRelocations);
   CLDClosure* weak_cld_closure = only_strong_roots ? NULL : cld_closure;
 
   process_roots(scope, so, root_closure, cld_closure, weak_cld_closure, &mark_code_closure);
-  if (!only_strong_roots) {
-    process_string_table_roots(scope, root_closure, par_state_string);
-  }
 
   if (young_gen_as_roots &&
       _process_strong_tasks->try_claim_task(GCH_PS_younger_gens)) {
--- a/src/hotspot/share/gc/cms/cmsHeap.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/cms/cmsHeap.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -91,8 +91,7 @@
                          ScanningOption so,
                          bool only_strong_roots,
                          OopsInGenClosure* root_closure,
-                         CLDClosure* cld_closure,
-                         OopStorage::ParState<false, false>* par_state_string = NULL);
+                         CLDClosure* cld_closure);
 
   GCMemoryManager* old_manager() const { return _old_manager; }
 
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -4333,8 +4333,7 @@
                           GenCollectedHeap::ScanningOption(_collector->CMSCollector::roots_scanning_options()),
                           _collector->should_unload_classes(),
                           &par_mri_cl,
-                          &cld_closure,
-                          &_par_state_string);
+                          &cld_closure);
 
   assert(_collector->should_unload_classes()
          || (_collector->CMSCollector::roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache),
@@ -4464,8 +4463,7 @@
                           GenCollectedHeap::ScanningOption(_collector->CMSCollector::roots_scanning_options()),
                           _collector->should_unload_classes(),
                           &par_mrias_cl,
-                          NULL,     // The dirty klasses will be handled below
-                          &_par_state_string);
+                          NULL);     // The dirty klasses will be handled below
 
   assert(_collector->should_unload_classes()
          || (_collector->CMSCollector::roots_scanning_options() & GenCollectedHeap::SO_AllCodeCache),
@@ -5277,12 +5275,6 @@
       // Clean up unreferenced symbols in symbol table.
       SymbolTable::unlink();
     }
-
-    {
-      GCTraceTime(Debug, gc, phases) t("Scrub String Table", _gc_timer_cm);
-      // Delete entries for dead interned strings.
-      StringTable::unlink(&_is_alive_closure);
-    }
   }
 
   // Restore any preserved marks as a result of mark stack or
--- a/src/hotspot/share/gc/cms/parNewGeneration.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/cms/parNewGeneration.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -606,8 +606,7 @@
   heap->young_process_roots(_strong_roots_scope,
                            &par_scan_state.to_space_root_closure(),
                            &par_scan_state.older_gen_closure(),
-                           &cld_scan_closure,
-                           &_par_state_string);
+                           &cld_scan_closure);
 
   par_scan_state.end_strong_roots();
 
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -3362,24 +3362,54 @@
 
 void G1CollectedHeap::complete_cleaning(BoolObjectClosure* is_alive,
                                         bool class_unloading_occurred) {
-  uint n_workers = workers()->active_workers();
-
-  G1StringDedupUnlinkOrOopsDoClosure dedup_closure(is_alive, NULL, false);
-  ParallelCleaningTask g1_unlink_task(is_alive, &dedup_closure, n_workers, class_unloading_occurred);
-  workers()->run_task(&g1_unlink_task);
+  uint num_workers = workers()->active_workers();
+  ParallelCleaningTask unlink_task(is_alive, num_workers, class_unloading_occurred, false);
+  workers()->run_task(&unlink_task);
 }
 
-void G1CollectedHeap::partial_cleaning(BoolObjectClosure* is_alive,
-                                       bool process_strings,
-                                       bool process_string_dedup) {
-  if (!process_strings && !process_string_dedup) {
-    // Nothing to clean.
-    return;
+// Clean string dedup data structures.
+// Ideally we would prefer to use a StringDedupCleaningTask here, but we want to
+// record the durations of the phases. Hence the almost-copy.
+class G1StringDedupCleaningTask : public AbstractGangTask {
+  BoolObjectClosure* _is_alive;
+  OopClosure* _keep_alive;
+  G1GCPhaseTimes* _phase_times;
+
+public:
+  G1StringDedupCleaningTask(BoolObjectClosure* is_alive,
+                            OopClosure* keep_alive,
+                            G1GCPhaseTimes* phase_times) :
+    AbstractGangTask("Partial Cleaning Task"),
+    _is_alive(is_alive),
+    _keep_alive(keep_alive),
+    _phase_times(phase_times)
+  {
+    assert(G1StringDedup::is_enabled(), "String deduplication disabled.");
+    StringDedup::gc_prologue(true);
   }
 
-  G1StringDedupUnlinkOrOopsDoClosure dedup_closure(is_alive, NULL, false);
-  StringCleaningTask g1_unlink_task(is_alive, process_string_dedup ? &dedup_closure : NULL, process_strings);
-  workers()->run_task(&g1_unlink_task);
+  ~G1StringDedupCleaningTask() {
+    StringDedup::gc_epilogue();
+  }
+
+  void work(uint worker_id) {
+    StringDedupUnlinkOrOopsDoClosure cl(_is_alive, _keep_alive);
+    {
+      G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupQueueFixup, worker_id);
+      StringDedupQueue::unlink_or_oops_do(&cl);
+    }
+    {
+      G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupTableFixup, worker_id);
+      StringDedupTable::unlink_or_oops_do(&cl, worker_id);
+    }
+  }
+};
+
+void G1CollectedHeap::string_dedup_cleaning(BoolObjectClosure* is_alive,
+                                            OopClosure* keep_alive,
+                                            G1GCPhaseTimes* phase_times) {
+  G1StringDedupCleaningTask cl(is_alive, keep_alive, phase_times);
+  workers()->run_task(&cl);
 }
 
 class G1RedirtyLoggedCardsTask : public AbstractGangTask {
@@ -3911,11 +3941,6 @@
   // not copied during the pause.
   process_discovered_references(per_thread_states);
 
-  // FIXME
-  // 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.
-
   G1STWIsAliveClosure is_alive(this);
   G1KeepAliveClosure keep_alive(this);
 
@@ -3923,12 +3948,12 @@
                               g1_policy()->phase_times()->weak_phase_times());
 
   if (G1StringDedup::is_enabled()) {
-    double fixup_start = os::elapsedTime();
-
-    G1StringDedup::unlink_or_oops_do(&is_alive, &keep_alive, true, g1_policy()->phase_times());
-
-    double fixup_time_ms = (os::elapsedTime() - fixup_start) * 1000.0;
-    g1_policy()->phase_times()->record_string_dedup_fixup_time(fixup_time_ms);
+    double string_dedup_time_ms = os::elapsedTime();
+
+    string_dedup_cleaning(&is_alive, &keep_alive, g1_policy()->phase_times());
+
+    double string_cleanup_time_ms = (os::elapsedTime() - string_dedup_time_ms) * 1000.0;
+    g1_policy()->phase_times()->record_string_deduplication_time(string_cleanup_time_ms);
   }
 
   if (evacuation_failed()) {
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1332,14 +1332,12 @@
   // after a full GC.
   void rebuild_strong_code_roots();
 
-  // Partial cleaning used when class unloading is disabled.
-  // Let the caller choose what structures to clean out:
-  // - StringTable
-  // - StringDeduplication structures
-  void partial_cleaning(BoolObjectClosure* is_alive, bool unlink_strings, bool unlink_string_dedup);
+  // Partial cleaning of VM internal data structures.
+  void string_dedup_cleaning(BoolObjectClosure* is_alive,
+                             OopClosure* keep_alive,
+                             G1GCPhaseTimes* phase_times = NULL);
 
-  // Complete cleaning used when class unloading is enabled.
-  // Cleans out all structures handled by partial_cleaning and also the CodeCache.
+  // Performs cleaning of data structures after class unloading.
   void complete_cleaning(BoolObjectClosure* is_alive, bool class_unloading_occurred);
 
   // Redirty logged cards in the refinement queue.
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1655,11 +1655,9 @@
     GCTraceTime(Debug, gc, phases) debug("Class Unloading", _gc_timer_cm);
     bool purged_classes = SystemDictionary::do_unloading(_gc_timer_cm);
     _g1h->complete_cleaning(&g1_is_alive, purged_classes);
-  } else {
-    GCTraceTime(Debug, gc, phases) debug("Cleanup", _gc_timer_cm);
-    // 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, G1StringDedup::is_enabled());
+  } else if (StringDedup::is_enabled()) {
+    GCTraceTime(Debug, gc, phases) debug("String Deduplication", _gc_timer_cm);
+    _g1h->string_dedup_cleaning(&g1_is_alive, NULL);
   }
 }
 
--- a/src/hotspot/share/gc/g1/g1FullCollector.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -224,10 +224,10 @@
     // Unload classes and purge the SystemDictionary.
     bool purged_class = SystemDictionary::do_unloading(scope()->timer());
     _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.
-    _heap->partial_cleaning(&_is_alive, true, G1StringDedup::is_enabled());
+  } else if (G1StringDedup::is_enabled()) {
+    GCTraceTime(Debug, gc, phases) debug("Phase 1: String Dedup Cleanup", scope()->timer());
+    // If no class unloading just clean out string deduplication data.
+    _heap->string_dedup_cleaning(&_is_alive, NULL);
   }
 
   scope()->tracer()->report_object_count_after_gc(&_is_alive);
--- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -86,7 +86,7 @@
     _weak_proc_task(collector->workers()),
     _hrclaimer(collector->workers()),
     _adjust(),
-    _adjust_string_dedup(NULL, &_adjust, G1StringDedup::is_enabled()) {
+    _string_dedup_cleaning_task(NULL, &_adjust, false) {
   // Need cleared claim bits for the roots processing
   ClassLoaderDataGraph::clear_claimed_marks();
 }
@@ -110,15 +110,10 @@
 
   CLDToOopClosure adjust_cld(&_adjust, ClassLoaderData::_claim_strong);
   CodeBlobToOopClosure adjust_code(&_adjust, CodeBlobToOopClosure::FixRelocations);
-  _root_processor.process_all_roots(
-      &_adjust,
-      &adjust_cld,
-      &adjust_code);
+  _root_processor.process_all_roots(&_adjust, &adjust_cld, &adjust_code);
 
-  // Adjust string dedup if enabled.
-  if (G1StringDedup::is_enabled()) {
-    G1StringDedup::parallel_unlink(&_adjust_string_dedup, worker_id);
-  }
+  // Adjust string dedup data structures.
+  _string_dedup_cleaning_task.work(worker_id);
 
   // Now adjust pointers region by region
   G1AdjustRegionClosure blk(collector()->mark_bitmap(), worker_id);
--- a/src/hotspot/share/gc/g1/g1FullGCAdjustTask.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1FullGCAdjustTask.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
 #include "gc/g1/g1RootProcessor.hpp"
 #include "gc/g1/g1StringDedup.hpp"
 #include "gc/g1/heapRegionManager.hpp"
+#include "gc/shared/parallelCleaning.hpp"
 #include "gc/shared/weakProcessorPhaseTimes.hpp"
 #include "gc/shared/weakProcessor.hpp"
 #include "utilities/ticks.hpp"
@@ -42,7 +43,7 @@
   WeakProcessor::Task      _weak_proc_task;
   HeapRegionClaimer        _hrclaimer;
   G1AdjustClosure          _adjust;
-  G1StringDedupUnlinkOrOopsDoClosure _adjust_string_dedup;
+  StringDedupCleaningTask  _string_dedup_cleaning_task;
 
 public:
   G1FullGCAdjustTask(G1FullCollector* collector);
--- a/src/hotspot/share/gc/g1/g1FullGCMarkTask.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1FullGCMarkTask.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -49,15 +49,13 @@
   MarkingCodeBlobClosure code_closure(marker->mark_closure(), !CodeBlobToOopClosure::FixRelocations);
 
   if (ClassUnloading) {
-    _root_processor.process_strong_roots(
-        marker->mark_closure(),
-        marker->cld_closure(),
-        &code_closure);
+    _root_processor.process_strong_roots(marker->mark_closure(),
+                                         marker->cld_closure(),
+                                         &code_closure);
   } else {
-    _root_processor.process_all_roots_no_string_table(
-        marker->mark_closure(),
-        marker->cld_closure(),
-        &code_closure);
+    _root_processor.process_all_roots(marker->mark_closure(),
+                                      marker->cld_closure(),
+                                      &code_closure);
   }
 
   // Mark stack is populated, now process and drain it.
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2019, 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
@@ -53,7 +53,6 @@
 
   // Root scanning phases
   _gc_par_phases[ThreadRoots] = new WorkerDataArray<double>(max_gc_threads, "Thread Roots (ms):");
-  _gc_par_phases[StringTableRoots] = new WorkerDataArray<double>(max_gc_threads, "StringTable Roots (ms):");
   _gc_par_phases[UniverseRoots] = new WorkerDataArray<double>(max_gc_threads, "Universe Roots (ms):");
   _gc_par_phases[JNIRoots] = new WorkerDataArray<double>(max_gc_threads, "JNI Handles Roots (ms):");
   _gc_par_phases[ObjectSynchronizerRoots] = new WorkerDataArray<double>(max_gc_threads, "ObjectSynchronizer Roots (ms):");
@@ -136,7 +135,7 @@
   _cur_strong_code_root_purge_time_ms = 0.0;
   _cur_evac_fail_recalc_used = 0.0;
   _cur_evac_fail_remove_self_forwards = 0.0;
-  _cur_string_dedup_fixup_time_ms = 0.0;
+  _cur_string_deduplication_time_ms = 0.0;
   _cur_prepare_tlab_time_ms = 0.0;
   _cur_resize_tlab_time_ms = 0.0;
   _cur_derived_pointer_table_update_time_ms = 0.0;
@@ -290,12 +289,12 @@
   }
 }
 
-void G1GCPhaseTimes::debug_phase(WorkerDataArray<double>* phase) const {
+void G1GCPhaseTimes::debug_phase(WorkerDataArray<double>* phase, uint extra_indent) const {
   LogTarget(Debug, gc, phases) lt;
   if (lt.is_enabled()) {
     ResourceMark rm;
     LogStream ls(lt);
-    log_phase(phase, 2, &ls, true);
+    log_phase(phase, 2 + extra_indent, &ls, true);
   }
 }
 
@@ -417,7 +416,7 @@
                         _recorded_total_free_cset_time_ms +
                         _cur_fast_reclaim_humongous_time_ms +
                         _cur_expand_heap_time_ms +
-                        _cur_string_dedup_fixup_time_ms;
+                        _cur_string_deduplication_time_ms;
 
   info_time("Post Evacuate Collection Set", sum_ms);
 
@@ -430,9 +429,9 @@
   _weak_phase_times.log_print(2);
 
   if (G1StringDedup::is_enabled()) {
-    debug_time("String Dedup Fixup", _cur_string_dedup_fixup_time_ms);
-    debug_phase(_gc_par_phases[StringDedupQueueFixup]);
-    debug_phase(_gc_par_phases[StringDedupTableFixup]);
+    debug_time("String Deduplication", _cur_string_deduplication_time_ms);
+    debug_phase(_gc_par_phases[StringDedupQueueFixup], 1);
+    debug_phase(_gc_par_phases[StringDedupTableFixup], 1);
   }
 
   if (G1CollectedHeap::heap()->evacuation_failed()) {
@@ -497,7 +496,6 @@
       "GCWorkerStart",
       "ExtRootScan",
       "ThreadRoots",
-      "StringTableRoots",
       "UniverseRoots",
       "JNIRoots",
       "ObjectSynchronizerRoots",
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -48,7 +48,6 @@
     GCWorkerStart,
     ExtRootScan,
     ThreadRoots,
-    StringTableRoots,
     UniverseRoots,
     JNIRoots,
     ObjectSynchronizerRoots,
@@ -104,8 +103,6 @@
  private:
   // Markers for grouping the phases in the GCPhases enum above
   static const int GCMainParPhasesLast = GCWorkerEnd;
-  static const int StringDedupPhasesFirst = StringDedupQueueFixup;
-  static const int StringDedupPhasesLast = StringDedupTableFixup;
 
   WorkerDataArray<double>* _gc_par_phases[GCParPhasesSentinel];
 
@@ -134,7 +131,7 @@
   double _cur_evac_fail_recalc_used;
   double _cur_evac_fail_remove_self_forwards;
 
-  double _cur_string_dedup_fixup_time_ms;
+  double _cur_string_deduplication_time_ms;
 
   double _cur_prepare_tlab_time_ms;
   double _cur_resize_tlab_time_ms;
@@ -187,7 +184,7 @@
   void details(T* phase, const char* indent) const;
 
   void log_phase(WorkerDataArray<double>* phase, uint indent, outputStream* out, bool print_sum) const;
-  void debug_phase(WorkerDataArray<double>* phase) const;
+  void debug_phase(WorkerDataArray<double>* phase, uint extra_indent = 0) const;
   void trace_phase(WorkerDataArray<double>* phase, bool print_sum = true) const;
 
   void info_time(const char* name, double value) const;
@@ -272,8 +269,8 @@
     _cur_evac_fail_remove_self_forwards = ms;
   }
 
-  void record_string_dedup_fixup_time(double ms) {
-    _cur_string_dedup_fixup_time_ms = ms;
+  void record_string_deduplication_time(double ms) {
+    _cur_string_deduplication_time_ms = ms;
   }
 
   void record_ref_proc_time(double ms) {
--- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -490,9 +490,7 @@
 
   {
     G1RootProcessor root_processor(_g1h, 1);
-    root_processor.process_all_roots(&rootsCl,
-                                     &cldCl,
-                                     &blobsCl);
+    root_processor.process_all_roots(&rootsCl, &cldCl, &blobsCl);
   }
 
   bool failures = rootsCl.failures() || codeRootsCl.failures();
--- a/src/hotspot/share/gc/g1/g1RootProcessor.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1RootProcessor.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -90,7 +90,6 @@
   }
 
   process_vm_roots(closures, phase_times, worker_i);
-  process_string_table_roots(closures, phase_times, worker_i);
 
   {
     // Now the CM ref_processor roots.
@@ -188,34 +187,17 @@
 
 void G1RootProcessor::process_all_roots(OopClosure* oops,
                                         CLDClosure* clds,
-                                        CodeBlobClosure* blobs,
-                                        bool process_string_table) {
+                                        CodeBlobClosure* blobs) {
   AllRootsClosures closures(oops, clds);
 
   process_java_roots(&closures, NULL, 0);
   process_vm_roots(&closures, NULL, 0);
 
-  if (process_string_table) {
-    process_string_table_roots(&closures, NULL, 0);
-  }
   process_code_cache_roots(blobs, NULL, 0);
 
   _process_strong_tasks.all_tasks_completed(n_workers());
 }
 
-void G1RootProcessor::process_all_roots(OopClosure* oops,
-                                        CLDClosure* clds,
-                                        CodeBlobClosure* blobs) {
-  process_all_roots(oops, clds, blobs, true);
-}
-
-void G1RootProcessor::process_all_roots_no_string_table(OopClosure* oops,
-                                                        CLDClosure* clds,
-                                                        CodeBlobClosure* blobs) {
-  assert(!ClassUnloading, "Should only be used when class unloading is disabled");
-  process_all_roots(oops, clds, blobs, false);
-}
-
 void G1RootProcessor::process_java_roots(G1RootClosures* closures,
                                          G1GCPhaseTimes* phase_times,
                                          uint worker_i) {
@@ -295,16 +277,6 @@
   }
 }
 
-void G1RootProcessor::process_string_table_roots(G1RootClosures* closures,
-                                                 G1GCPhaseTimes* phase_times,
-                                                 uint worker_i) {
-  assert(closures->weak_oops() != NULL, "Should only be called when all roots are processed");
-  G1GCParPhaseTimesTracker x(phase_times, G1GCPhaseTimes::StringTableRoots, worker_i);
-  // All threads execute the following. A specific chunk of buckets
-  // from the StringTable are the individual tasks.
-  StringTable::possibly_parallel_oops_do(&_par_state_string, closures->weak_oops());
-}
-
 void G1RootProcessor::process_code_cache_roots(CodeBlobClosure* code_closure,
                                                G1GCPhaseTimes* phase_times,
                                                uint worker_i) {
--- a/src/hotspot/share/gc/g1/g1RootProcessor.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1RootProcessor.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -75,11 +75,6 @@
   void worker_has_discovered_all_strong_classes();
   void wait_until_all_strong_classes_discovered();
 
-  void process_all_roots(OopClosure* oops,
-                         CLDClosure* clds,
-                         CodeBlobClosure* blobs,
-                         bool process_string_table);
-
   void process_java_roots(G1RootClosures* closures,
                           G1GCPhaseTimes* phase_times,
                           uint worker_i);
@@ -88,10 +83,6 @@
                         G1GCPhaseTimes* phase_times,
                         uint worker_i);
 
-  void process_string_table_roots(G1RootClosures* closures,
-                                  G1GCPhaseTimes* phase_times,
-                                  uint worker_i);
-
   void process_code_cache_roots(CodeBlobClosure* code_closure,
                                 G1GCPhaseTimes* phase_times,
                                 uint worker_i);
@@ -101,7 +92,7 @@
 
   // Apply correct closures from pss to the strongly and weakly reachable roots in the system
   // in a single pass.
-  // Record and report timing measurements for sub phases using the worker_i
+  // Record and report timing measurements for sub phases using worker_id.
   void evacuate_roots(G1ParScanThreadState* pss, uint worker_id);
 
   // Apply oops, clds and blobs to all strongly reachable roots in the system
@@ -114,13 +105,6 @@
                          CLDClosure* clds,
                          CodeBlobClosure* blobs);
 
-  // Apply oops, clds and blobs to strongly and weakly reachable roots in the system,
-  // the only thing different from process_all_roots is that we skip the string table
-  // to avoid keeping every string live when doing class unloading.
-  void process_all_roots_no_string_table(OopClosure* oops,
-                                         CLDClosure* clds,
-                                         CodeBlobClosure* blobs);
-
   // Number of worker threads used by the root processor.
   uint n_workers() const;
 };
--- a/src/hotspot/share/gc/g1/g1StringDedup.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1StringDedup.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -89,53 +89,3 @@
   }
 }
 
-void G1StringDedup::oops_do(OopClosure* keep_alive) {
-  assert(is_enabled(), "String deduplication not enabled");
-  unlink_or_oops_do(NULL, keep_alive, true /* allow_resize_and_rehash */);
-}
-
-void G1StringDedup::parallel_unlink(G1StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id) {
-  assert(is_enabled(), "String deduplication not enabled");
-  StringDedupQueue::unlink_or_oops_do(unlink);
-  StringDedupTable::unlink_or_oops_do(unlink, worker_id);
-}
-
-//
-// Task for parallel unlink_or_oops_do() operation on the deduplication queue
-// and table.
-//
-class G1StringDedupUnlinkOrOopsDoTask : public AbstractGangTask {
-private:
-  G1StringDedupUnlinkOrOopsDoClosure _cl;
-  G1GCPhaseTimes* _phase_times;
-
-public:
-  G1StringDedupUnlinkOrOopsDoTask(BoolObjectClosure* is_alive,
-                                  OopClosure* keep_alive,
-                                  bool allow_resize_and_rehash,
-                                  G1GCPhaseTimes* phase_times) :
-    AbstractGangTask("G1StringDedupUnlinkOrOopsDoTask"),
-    _cl(is_alive, keep_alive, allow_resize_and_rehash), _phase_times(phase_times) { }
-
-  virtual void work(uint worker_id) {
-    {
-      G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupQueueFixup, worker_id);
-      StringDedupQueue::unlink_or_oops_do(&_cl);
-    }
-    {
-      G1GCParPhaseTimesTracker x(_phase_times, G1GCPhaseTimes::StringDedupTableFixup, worker_id);
-      StringDedupTable::unlink_or_oops_do(&_cl, worker_id);
-    }
-  }
-};
-
-void G1StringDedup::unlink_or_oops_do(BoolObjectClosure* is_alive,
-                                      OopClosure* keep_alive,
-                                      bool allow_resize_and_rehash,
-                                      G1GCPhaseTimes* phase_times) {
-  assert(is_enabled(), "String deduplication not enabled");
-
-  G1StringDedupUnlinkOrOopsDoTask task(is_alive, keep_alive, allow_resize_and_rehash, phase_times);
-  G1CollectedHeap* g1h = G1CollectedHeap::heap();
-  g1h->workers()->run_task(&task);
-}
--- a/src/hotspot/share/gc/g1/g1StringDedup.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/g1/g1StringDedup.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -78,35 +78,6 @@
   static void enqueue_from_mark(oop java_string, uint worker_id);
   static void enqueue_from_evacuation(bool from_young, bool to_young,
                                       unsigned int queue, oop java_string);
-
-  static void oops_do(OopClosure* keep_alive);
-  static void parallel_unlink(G1StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id);
-  static void unlink_or_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive,
-                                bool allow_resize_and_rehash, G1GCPhaseTimes* phase_times = NULL);
-};
-
-//
-// This closure encapsulates the state and the closures needed when scanning
-// the deduplication queue and table during the unlink_or_oops_do() operation.
-// A single instance of this closure is created and then shared by all worker
-// threads participating in the scan.
-//
-class G1StringDedupUnlinkOrOopsDoClosure : public StringDedupUnlinkOrOopsDoClosure {
-public:
-  G1StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
-                                     OopClosure* keep_alive,
-                                     bool allow_resize_and_rehash) :
-    StringDedupUnlinkOrOopsDoClosure(is_alive, keep_alive) {
-      if (G1StringDedup::is_enabled()) {
-        G1StringDedup::gc_prologue(allow_resize_and_rehash);
-      }
-    }
-
-  ~G1StringDedupUnlinkOrOopsDoClosure() {
-    if (G1StringDedup::is_enabled()) {
-      G1StringDedup::gc_epilogue();
-    }
-  }
 };
 
 #endif // SHARE_GC_G1_G1STRINGDEDUP_HPP
--- a/src/hotspot/share/gc/parallel/psMarkSweep.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/parallel/psMarkSweep.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -567,12 +567,6 @@
   }
 
   {
-    GCTraceTime(Debug, gc, phases) t("Scrub String Table", _gc_timer);
-    // Delete entries for dead interned strings.
-    StringTable::unlink(is_alive_closure());
-  }
-
-  {
     GCTraceTime(Debug, gc, phases) t("Scrub Symbol Table", _gc_timer);
     // Clean up unreferenced symbols in symbol table.
     SymbolTable::unlink();
@@ -630,7 +624,6 @@
   CodeBlobToOopClosure adjust_from_blobs(adjust_pointer_closure(), CodeBlobToOopClosure::FixRelocations);
   CodeCache::blobs_do(&adjust_from_blobs);
   AOTLoader::oops_do(adjust_pointer_closure());
-  StringTable::oops_do(adjust_pointer_closure());
   ref_processor()->weak_oops_do(adjust_pointer_closure());
   PSScavenge::reference_processor()->weak_oops_do(adjust_pointer_closure());
 
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2019, 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
@@ -2185,12 +2185,6 @@
   }
 
   {
-    GCTraceTime(Debug, gc, phases) t("Scrub String Table", &_gc_timer);
-    // Delete entries for dead interned strings.
-    StringTable::unlink(is_alive_closure());
-  }
-
-  {
     GCTraceTime(Debug, gc, phases) t("Scrub Symbol Table", &_gc_timer);
     // Clean up unreferenced symbols in symbol table.
     SymbolTable::unlink();
@@ -2226,7 +2220,6 @@
   CodeBlobToOopClosure adjust_from_blobs(&oop_closure, CodeBlobToOopClosure::FixRelocations);
   CodeCache::blobs_do(&adjust_from_blobs);
   AOTLoader::oops_do(&oop_closure);
-  StringTable::oops_do(&oop_closure);
   ref_processor()->weak_oops_do(&oop_closure);
   // Roots were visited so references into the young gen in roots
   // may have been scanned.  Process them also.
--- a/src/hotspot/share/gc/parallel/psScavenge.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/parallel/psScavenge.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2019, 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
@@ -430,12 +430,6 @@
       WeakProcessor::weak_oops_do(&_is_alive_closure, &root_closure);
     }
 
-    {
-      GCTraceTime(Debug, gc, phases) tm("Scrub String Table", &_gc_timer);
-      // Unlink any dead interned Strings and process the remaining live ones.
-      StringTable::unlink_or_oops_do(&_is_alive_closure, &root_closure);
-    }
-
     // Verify that usage of root_closure didn't copy any objects.
     assert(promotion_manager->stacks_empty(),"stacks should be empty at this point");
 
--- a/src/hotspot/share/gc/serial/genMarkSweep.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/serial/genMarkSweep.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, 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
@@ -240,12 +240,6 @@
   }
 
   {
-    GCTraceTime(Debug, gc, phases) t("Scrub String Table", gc_timer());
-    // Delete entries for dead interned strings.
-    StringTable::unlink(&is_alive);
-  }
-
-  {
     GCTraceTime(Debug, gc, phases) t("Scrub Symbol Table", gc_timer());
     // Clean up unreferenced symbols in symbol table.
     SymbolTable::unlink();
--- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2019, 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
@@ -849,33 +849,14 @@
   }
 }
 
-void GenCollectedHeap::process_string_table_roots(StrongRootsScope* scope,
-                                                  OopClosure* root_closure,
-                                                  OopStorage::ParState<false, false>* par_state_string) {
-  assert(root_closure != NULL, "Must be set");
-  // All threads execute the following. A specific chunk of buckets
-  // from the StringTable are the individual tasks.
-
-  // Either we should be single threaded or have a ParState
-  assert((scope->n_threads() <= 1) || par_state_string != NULL, "Parallel but no ParState");
-
-  if (scope->n_threads() > 1) {
-    StringTable::possibly_parallel_oops_do(par_state_string, root_closure);
-  } else {
-    StringTable::oops_do(root_closure);
-  }
-}
-
 void GenCollectedHeap::young_process_roots(StrongRootsScope* scope,
                                            OopsInGenClosure* root_closure,
                                            OopsInGenClosure* old_gen_closure,
-                                           CLDClosure* cld_closure,
-                                           OopStorage::ParState<false, false>* par_state_string) {
+                                           CLDClosure* cld_closure) {
   MarkingCodeBlobClosure mark_code_closure(root_closure, CodeBlobToOopClosure::FixRelocations);
 
   process_roots(scope, SO_ScavengeCodeCache, root_closure,
                 cld_closure, cld_closure, &mark_code_closure);
-  process_string_table_roots(scope, root_closure, par_state_string);
 
   if (_process_strong_tasks->try_claim_task(GCH_PS_younger_gens)) {
     root_closure->reset_generation();
@@ -895,19 +876,11 @@
                                           ScanningOption so,
                                           bool only_strong_roots,
                                           OopsInGenClosure* root_closure,
-                                          CLDClosure* cld_closure,
-                                          OopStorage::ParState<false, false>* par_state_string) {
+                                          CLDClosure* cld_closure) {
   MarkingCodeBlobClosure mark_code_closure(root_closure, is_adjust_phase);
   CLDClosure* weak_cld_closure = only_strong_roots ? NULL : cld_closure;
 
   process_roots(scope, so, root_closure, cld_closure, weak_cld_closure, &mark_code_closure);
-  if (is_adjust_phase) {
-    // We never treat the string table as roots during marking
-    // for the full gc, so we only need to process it during
-    // the adjust phase.
-    process_string_table_roots(scope, root_closure, par_state_string);
-  }
-
   _process_strong_tasks->all_tasks_completed(scope->n_threads());
 }
 
--- a/src/hotspot/share/gc/shared/genCollectedHeap.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -400,10 +400,6 @@
                      CLDClosure* weak_cld_closure,
                      CodeBlobToOopClosure* code_roots);
 
-  void process_string_table_roots(StrongRootsScope* scope,
-                                  OopClosure* root_closure,
-                                  OopStorage::ParState<false, false>* par_state_string);
-
   // Accessor for memory state verification support
   NOT_PRODUCT(
     virtual size_t skip_header_HeapWords() { return 0; }
@@ -416,16 +412,14 @@
   void young_process_roots(StrongRootsScope* scope,
                            OopsInGenClosure* root_closure,
                            OopsInGenClosure* old_gen_closure,
-                           CLDClosure* cld_closure,
-                           OopStorage::ParState<false, false>* par_state_string = NULL);
+                           CLDClosure* cld_closure);
 
   void full_process_roots(StrongRootsScope* scope,
                           bool is_adjust_phase,
                           ScanningOption so,
                           bool only_strong_roots,
                           OopsInGenClosure* root_closure,
-                          CLDClosure* cld_closure,
-                          OopStorage::ParState<false, false>* par_state_string = NULL);
+                          CLDClosure* cld_closure);
 
   // Apply "root_closure" to all the weak roots of the system.
   // These include JNI weak roots, string table,
--- a/src/hotspot/share/gc/shared/parallelCleaning.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,48 +31,35 @@
 #include "memory/resourceArea.hpp"
 #include "logging/log.hpp"
 
-StringCleaningTask::StringCleaningTask(BoolObjectClosure* is_alive, StringDedupUnlinkOrOopsDoClosure* dedup_closure, bool process_strings) :
-  AbstractGangTask("String Unlinking"),
-  _is_alive(is_alive),
-  _dedup_closure(dedup_closure),
-  _par_state_string(StringTable::weak_storage()),
-  _initial_string_table_size((int) StringTable::the_table()->table_size()),
-  _process_strings(process_strings), _strings_processed(0), _strings_removed(0) {
+StringDedupCleaningTask::StringDedupCleaningTask(BoolObjectClosure* is_alive,
+                                                 OopClosure* keep_alive,
+                                                 bool resize_table) :
+  AbstractGangTask("String Dedup Cleaning"),
+  _dedup_closure(is_alive, keep_alive) {
 
-  if (process_strings) {
-    StringTable::reset_dead_counter();
+  if (StringDedup::is_enabled()) {
+    StringDedup::gc_prologue(resize_table);
   }
 }
 
-StringCleaningTask::~StringCleaningTask() {
-  log_info(gc, stringtable)(
-      "Cleaned string table, "
-      "strings: " SIZE_FORMAT " processed, " SIZE_FORMAT " removed",
-      strings_processed(), strings_removed());
-  if (_process_strings) {
-    StringTable::finish_dead_counter();
+StringDedupCleaningTask::~StringDedupCleaningTask() {
+  if (StringDedup::is_enabled()) {
+    StringDedup::gc_epilogue();
   }
 }
 
-void StringCleaningTask::work(uint worker_id) {
-  size_t strings_processed = 0;
-  size_t strings_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 (_dedup_closure != NULL) {
-    StringDedup::parallel_unlink(_dedup_closure, worker_id);
+void StringDedupCleaningTask::work(uint worker_id) {
+  if (StringDedup::is_enabled()) {
+    StringDedup::parallel_unlink(&_dedup_closure, worker_id);
   }
 }
 
 CodeCacheUnloadingTask::CodeCacheUnloadingTask(uint num_workers, BoolObjectClosure* is_alive, bool unloading_occurred) :
-      _unloading_scope(is_alive),
-      _unloading_occurred(unloading_occurred),
-      _num_workers(num_workers),
-      _first_nmethod(NULL),
-      _claimed_nmethod(NULL) {
+  _unloading_scope(is_alive),
+  _unloading_occurred(unloading_occurred),
+  _num_workers(num_workers),
+  _first_nmethod(NULL),
+  _claimed_nmethod(NULL) {
   // Get first alive nmethod
   CompiledMethodIterator iter(CompiledMethodIterator::only_alive);
   if(iter.next()) {
@@ -175,10 +162,12 @@
 }
 
 ParallelCleaningTask::ParallelCleaningTask(BoolObjectClosure* is_alive,
-  StringDedupUnlinkOrOopsDoClosure* dedup_closure, uint num_workers, bool unloading_occurred) :
+                                           uint num_workers,
+                                           bool unloading_occurred,
+                                           bool resize_dedup_table) :
   AbstractGangTask("Parallel Cleaning"),
   _unloading_occurred(unloading_occurred),
-  _string_task(is_alive, StringDedup::is_enabled() ? dedup_closure : NULL, true),
+  _string_dedup_task(is_alive, NULL, resize_dedup_table),
   _code_cache_task(num_workers, is_alive, unloading_occurred),
   _klass_cleaning_task() {
 }
@@ -188,8 +177,8 @@
   // Do first pass of code cache cleaning.
   _code_cache_task.work(worker_id);
 
-  // Clean the Strings and Symbols.
-  _string_task.work(worker_id);
+  // Clean the string dedup data structures.
+  _string_dedup_task.work(worker_id);
 
   // Clean all klasses that were not unloaded.
   // The weak metadata in klass doesn't need to be
--- a/src/hotspot/share/gc/shared/parallelCleaning.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/parallelCleaning.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -33,27 +33,14 @@
 
 class ParallelCleaningTask;
 
-class StringCleaningTask : public AbstractGangTask {
-private:
-  BoolObjectClosure* _is_alive;
-  StringDedupUnlinkOrOopsDoClosure * const _dedup_closure;
-
-  OopStorage::ParState<false /* concurrent */, false /* const */> _par_state_string;
-
-  int _initial_string_table_size;
-
-  bool            _process_strings;
-  volatile size_t _strings_processed;
-  volatile size_t _strings_removed;
+class StringDedupCleaningTask : public AbstractGangTask {
+  StringDedupUnlinkOrOopsDoClosure _dedup_closure;
 
 public:
-  StringCleaningTask(BoolObjectClosure* is_alive, StringDedupUnlinkOrOopsDoClosure* dedup_closure, bool process_strings);
-  ~StringCleaningTask();
+  StringDedupCleaningTask(BoolObjectClosure* is_alive, OopClosure* keep_alive, bool resize_table);
+  ~StringDedupCleaningTask();
 
   void work(uint worker_id);
-
-  size_t strings_processed() const { return _strings_processed; }
-  size_t strings_removed()   const { return _strings_removed; }
 };
 
 class CodeCacheUnloadingTask {
@@ -100,18 +87,21 @@
   void work();
 };
 
-// To minimize the remark pause times, the tasks below are done in parallel.
+// Do cleanup of some weakly held data in the same parallel task.
+// Assumes a non-moving context.
 class ParallelCleaningTask : public AbstractGangTask {
 private:
-  bool                        _unloading_occurred;
-  StringCleaningTask          _string_task;
-  CodeCacheUnloadingTask      _code_cache_task;
-  KlassCleaningTask           _klass_cleaning_task;
+  bool                    _unloading_occurred;
+  StringDedupCleaningTask _string_dedup_task;
+  CodeCacheUnloadingTask  _code_cache_task;
+  KlassCleaningTask       _klass_cleaning_task;
 
 public:
   // The constructor is run in the VMThread.
-  ParallelCleaningTask(BoolObjectClosure* is_alive, StringDedupUnlinkOrOopsDoClosure* dedup_closure,
-    uint num_workers, bool unloading_occurred);
+  ParallelCleaningTask(BoolObjectClosure* is_alive,
+                       uint num_workers,
+                       bool unloading_occurred,
+                       bool resize_dedup_table);
 
   void work(uint worker_id);
 };
--- a/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2019, 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
@@ -53,7 +53,6 @@
   StringDedupTable::deduplicate(java_string, &dummy);
 }
 
-
 void StringDedup::parallel_unlink(StringDedupUnlinkOrOopsDoClosure* unlink, uint worker_id) {
   assert(is_enabled(), "String deduplication not enabled");
   StringDedupQueue::unlink_or_oops_do(unlink);
@@ -80,5 +79,8 @@
 
 StringDedupUnlinkOrOopsDoClosure::StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
                                                                    OopClosure* keep_alive) :
-  _is_alive(is_alive), _keep_alive(keep_alive) {
+  _always_true(),
+  _do_nothing(),
+  _is_alive(is_alive != NULL ? is_alive : &_always_true),
+  _keep_alive(keep_alive != NULL ? keep_alive : &_do_nothing) {
 }
--- a/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/stringdedup/stringDedup.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -113,30 +113,18 @@
 // the deduplication queue and table during the unlink_or_oops_do() operation.
 //
 class StringDedupUnlinkOrOopsDoClosure : public StackObj {
-private:
+  AlwaysTrueClosure   _always_true;
+  DoNothingClosure    _do_nothing;
   BoolObjectClosure*  _is_alive;
   OopClosure*         _keep_alive;
 
 public:
   StringDedupUnlinkOrOopsDoClosure(BoolObjectClosure* is_alive,
-                                     OopClosure* keep_alive);
+                                   OopClosure* keep_alive);
 
-  // Applies and returns the result from the is_alive closure, or
-  // returns true if no such closure was provided.
-  bool is_alive(oop o) {
-    if (_is_alive != NULL) {
-      return _is_alive->do_object_b(o);
-    }
-    return true;
-  }
+  bool is_alive(oop o) { return _is_alive->do_object_b(o); }
 
-  // Applies the keep_alive closure, or does nothing if no such
-  // closure was provided.
-  void keep_alive(oop* p) {
-    if (_keep_alive != NULL) {
-      _keep_alive->do_oop(p);
-    }
-  }
+  void keep_alive(oop* p) { _keep_alive->do_oop(p); }
 };
 
 #endif // SHARE_GC_SHARED_STRINGDEDUP_STRINGDEDUP_HPP
--- a/src/hotspot/share/gc/shared/weakProcessor.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/weakProcessor.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -23,9 +23,9 @@
  */
 
 #include "precompiled.hpp"
+#include "classfile/stringTable.hpp"
 #include "gc/shared/oopStorage.inline.hpp"
 #include "gc/shared/oopStorageParState.inline.hpp"
-#include "gc/shared/weakProcessor.hpp"
 #include "gc/shared/weakProcessor.inline.hpp"
 #include "gc/shared/weakProcessorPhases.hpp"
 #include "gc/shared/weakProcessorPhaseTimes.hpp"
@@ -35,13 +35,19 @@
 #include "utilities/macros.hpp"
 
 void WeakProcessor::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive) {
+  StringTable::reset_dead_counter();
+  CountingIsAliveClosure<BoolObjectClosure> cl(is_alive);
   FOR_EACH_WEAK_PROCESSOR_PHASE(phase) {
     if (WeakProcessorPhases::is_serial(phase)) {
-      WeakProcessorPhases::processor(phase)(is_alive, keep_alive);
+      WeakProcessorPhases::processor(phase)(&cl, keep_alive);
     } else {
-      WeakProcessorPhases::oop_storage(phase)->weak_oops_do(is_alive, keep_alive);
+      WeakProcessorPhases::oop_storage(phase)->weak_oops_do(&cl, keep_alive);
+    }
+    if (WeakProcessorPhases::is_stringtable(phase)) {
+      StringTable::inc_dead_counter(cl.num_dead());
     }
   }
+  StringTable::finish_dead_counter();
 }
 
 void WeakProcessor::oops_do(OopClosure* closure) {
@@ -93,6 +99,7 @@
     OopStorage* storage = WeakProcessorPhases::oop_storage(phase);
     new (states++) StorageState(storage, _nworkers);
   }
+  StringTable::reset_dead_counter();
 }
 
 WeakProcessor::Task::Task(uint nworkers) :
@@ -122,6 +129,7 @@
     }
     FREE_C_HEAP_ARRAY(StorageState, _storage_states);
   }
+  StringTable::finish_dead_counter();
 }
 
 void WeakProcessor::GangTask::work(uint worker_id) {
--- a/src/hotspot/share/gc/shared/weakProcessor.inline.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/weakProcessor.inline.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -25,6 +25,7 @@
 #ifndef SHARE_GC_SHARED_WEAKPROCESSOR_INLINE_HPP
 #define SHARE_GC_SHARED_WEAKPROCESSOR_INLINE_HPP
 
+#include "classfile/stringTable.hpp"
 #include "gc/shared/oopStorage.inline.hpp"
 #include "gc/shared/oopStorageParState.inline.hpp"
 #include "gc/shared/weakProcessor.hpp"
@@ -36,6 +37,27 @@
 class BoolObjectClosure;
 class OopClosure;
 
+template<typename T>
+class CountingIsAliveClosure : public BoolObjectClosure {
+  T* _inner;
+
+  size_t _num_dead;
+  size_t _num_total;
+
+public:
+  CountingIsAliveClosure(T* cl) : _inner(cl), _num_dead(0), _num_total(0) { }
+
+  virtual bool do_object_b(oop obj) {
+    bool result = _inner->do_object_b(obj);
+    _num_dead += !result;
+    _num_total++;
+    return result;
+  }
+
+  size_t num_dead() const { return _num_dead; }
+  size_t num_total() const { return _num_total; }
+};
+
 template<typename IsAlive, typename KeepAlive>
 void WeakProcessor::Task::work(uint worker_id,
                                IsAlive* is_alive,
@@ -45,16 +67,26 @@
          worker_id, _nworkers);
 
   FOR_EACH_WEAK_PROCESSOR_PHASE(phase) {
+    CountingIsAliveClosure<IsAlive> cl(is_alive);
     if (WeakProcessorPhases::is_serial(phase)) {
       uint serial_index = WeakProcessorPhases::serial_index(phase);
       if (_serial_phases_done.try_claim_task(serial_index)) {
         WeakProcessorPhaseTimeTracker pt(_phase_times, phase);
-        WeakProcessorPhases::processor(phase)(is_alive, keep_alive);
+        WeakProcessorPhases::processor(phase)(&cl, keep_alive);
+        if (_phase_times != NULL) {
+          _phase_times->record_phase_items(phase, cl.num_dead(), cl.num_total());
+        }
       }
     } else {
       WeakProcessorPhaseTimeTracker pt(_phase_times, phase, worker_id);
       uint storage_index = WeakProcessorPhases::oop_storage_index(phase);
-      _storage_states[storage_index].weak_oops_do(is_alive, keep_alive);
+      _storage_states[storage_index].weak_oops_do(&cl, keep_alive);
+      if (_phase_times != NULL) {
+        _phase_times->record_worker_items(worker_id, phase, cl.num_dead(), cl.num_total());
+      }
+    }
+    if (WeakProcessorPhases::is_stringtable(phase)) {
+      StringTable::inc_dead_counter(cl.num_dead());
     }
   }
 
--- a/src/hotspot/share/gc/shared/weakProcessorPhaseTimes.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/weakProcessorPhaseTimes.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -49,6 +49,7 @@
 
 #ifdef ASSERT
 static bool is_initialized_time(double t) { return t >= 0.0; }
+static bool is_initialized_items(size_t i) { return i != 0; }
 #endif // ASSERT
 
 static void reset_times(double* times, size_t ntimes) {
@@ -57,28 +58,43 @@
   }
 }
 
+static void reset_items(size_t* items, size_t nitems) {
+  for (size_t i = 0; i < nitems; ++i) {
+    items[i] = 0;
+  }
+}
+
 WeakProcessorPhaseTimes::WeakProcessorPhaseTimes(uint max_threads) :
   _max_threads(max_threads),
   _active_workers(0),
   _total_time_sec(uninitialized_time),
-  _worker_phase_times_sec()
+  _worker_data(),
+  _worker_dead_items(),
+  _worker_total_items()
 {
   assert(_max_threads > 0, "max_threads must not be zero");
 
   reset_times(_phase_times_sec, ARRAY_SIZE(_phase_times_sec));
+  reset_items(_phase_dead_items, ARRAY_SIZE(_phase_dead_items));
+  reset_items(_phase_total_items, ARRAY_SIZE(_phase_total_items));
 
   if (_max_threads > 1) {
-    WorkerDataArray<double>** wpt = _worker_phase_times_sec;
+    WorkerDataArray<double>** wpt = _worker_data;
     FOR_EACH_WEAK_PROCESSOR_OOP_STORAGE_PHASE(phase) {
       const char* description = WeakProcessorPhases::description(phase);
-      *wpt++ = new WorkerDataArray<double>(_max_threads, description);
+      *wpt = new WorkerDataArray<double>(_max_threads, description);
+      (*wpt)->link_thread_work_items(new WorkerDataArray<size_t>(_max_threads, "Dead"), DeadItems);
+      (*wpt)->link_thread_work_items(new WorkerDataArray<size_t>(_max_threads, "Total"), TotalItems);
+      wpt++;
     }
   }
 }
 
 WeakProcessorPhaseTimes::~WeakProcessorPhaseTimes() {
-  for (size_t i = 0; i < ARRAY_SIZE(_worker_phase_times_sec); ++i) {
-    delete _worker_phase_times_sec[i];
+  for (size_t i = 0; i < ARRAY_SIZE(_worker_data); ++i) {
+    delete _worker_data[i];
+    delete _worker_dead_items[i];
+    delete _worker_total_items[i];
   }
 }
 
@@ -100,9 +116,11 @@
   _active_workers = 0;
   _total_time_sec = uninitialized_time;
   reset_times(_phase_times_sec, ARRAY_SIZE(_phase_times_sec));
+  reset_items(_phase_dead_items, ARRAY_SIZE(_phase_dead_items));
+  reset_items(_phase_total_items, ARRAY_SIZE(_phase_total_items));
   if (_max_threads > 1) {
-    for (size_t i = 0; i < ARRAY_SIZE(_worker_phase_times_sec); ++i) {
-      _worker_phase_times_sec[i]->reset();
+    for (size_t i = 0; i < ARRAY_SIZE(_worker_data); ++i) {
+      _worker_data[i]->reset();
     }
   }
 }
@@ -129,10 +147,20 @@
   _phase_times_sec[phase_index(phase)] = time_sec;
 }
 
+void WeakProcessorPhaseTimes::record_phase_items(WeakProcessorPhase phase, size_t num_dead, size_t num_total) {
+  uint p = phase_index(phase);
+  assert(!is_initialized_items(_phase_dead_items[p]),
+         "Already set dead items for phase %u", p);
+  assert(!is_initialized_items(_phase_total_items[p]),
+         "Already set total items for phase %u", p);
+  _phase_dead_items[p] = num_dead;
+  _phase_total_items[p] = num_total;
+}
+
 WorkerDataArray<double>* WeakProcessorPhaseTimes::worker_data(WeakProcessorPhase phase) const {
   assert_oop_storage_phase(phase);
   assert(active_workers() > 1, "No worker data when single-threaded");
-  return _worker_phase_times_sec[WeakProcessorPhases::oop_storage_index(phase)];
+  return _worker_data[WeakProcessorPhases::oop_storage_index(phase)];
 }
 
 double WeakProcessorPhaseTimes::worker_time_sec(uint worker_id, WeakProcessorPhase phase) const {
@@ -155,6 +183,18 @@
   }
 }
 
+void WeakProcessorPhaseTimes::record_worker_items(uint worker_id,
+                                                  WeakProcessorPhase phase,
+                                                  size_t num_dead,
+                                                  size_t num_total) {
+  if (active_workers() == 1) {
+    record_phase_items(phase, num_dead, num_total);
+  } else {
+    worker_data(phase)->set_or_add_thread_work_item(worker_id, num_dead, DeadItems);
+    worker_data(phase)->set_or_add_thread_work_item(worker_id, num_total, TotalItems);
+  }
+}
+
 static double elapsed_time_sec(Ticks start_time, Ticks end_time) {
   return (end_time - start_time).seconds();
 }
@@ -223,6 +263,16 @@
                         indent_str(indent),
                         WeakProcessorPhases::description(phase),
                         phase_time_sec(phase) * MILLIUNITS);
+
+  log_debug(gc, phases)("%s%s: " SIZE_FORMAT,
+                        indent_str(indent + 1),
+                        "Dead",
+                        _phase_dead_items[phase_index(phase)]);
+
+  log_debug(gc, phases)("%s%s: " SIZE_FORMAT,
+                        indent_str(indent + 1),
+                        "Total",
+                        _phase_total_items[phase_index(phase)]);
 }
 
 void WeakProcessorPhaseTimes::log_mt_phase_summary(WeakProcessorPhase phase,
@@ -231,27 +281,36 @@
   LogStream ls(lt);
   ls.print("%s", indents[indent]);
   worker_data(phase)->print_summary_on(&ls, true);
+  log_mt_phase_details(worker_data(phase), indent + 1);
+
+  for (uint i = 0; i < worker_data(phase)->MaxThreadWorkItems; i++) {
+    WorkerDataArray<size_t>* work_items = worker_data(phase)->thread_work_items(i);
+    if (work_items != NULL) {
+      ls.print("%s", indents[indent + 1]);
+      work_items->print_summary_on(&ls, true);
+      log_mt_phase_details(work_items, indent + 1);
+    }
+  }
 }
 
-void WeakProcessorPhaseTimes::log_mt_phase_details(WeakProcessorPhase phase,
+template <typename T>
+void WeakProcessorPhaseTimes::log_mt_phase_details(WorkerDataArray<T>* data,
                                                    uint indent) const {
   LogTarget(Trace, gc, phases) lt;
-  LogStream ls(lt);
-  ls.print("%s", indents[indent]);
-  worker_data(phase)->print_details_on(&ls);
+  if (lt.is_enabled()) {
+    LogStream ls(lt);
+    ls.print("%s", indents[indent]);
+    data->print_details_on(&ls);
+  }
 }
 
 void WeakProcessorPhaseTimes::log_print_phases(uint indent) const {
   if (log_is_enabled(Debug, gc, phases)) {
-    bool details_enabled = log_is_enabled(Trace, gc, phases);
     FOR_EACH_WEAK_PROCESSOR_PHASE(phase) {
       if (is_serial_phase(phase) || (active_workers() == 1)) {
         log_st_phase(phase, indent);
       } else {
         log_mt_phase_summary(phase, indent);
-        if (details_enabled) {
-          log_mt_phase_details(phase, indent + 1);
-        }
       }
     }
   }
--- a/src/hotspot/share/gc/shared/weakProcessorPhaseTimes.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/weakProcessorPhaseTimes.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -33,6 +33,10 @@
 template<typename T> class WorkerDataArray;
 
 class WeakProcessorPhaseTimes : public CHeapObj<mtGC> {
+  enum {
+    DeadItems,
+    TotalItems
+  };
   uint _max_threads;
   uint _active_workers;
 
@@ -43,15 +47,20 @@
   // processed by multiple threads are unused, as are entries for
   // unexecuted phases.
   double _phase_times_sec[WeakProcessorPhases::phase_count];
+  size_t _phase_dead_items[WeakProcessorPhases::phase_count];
+  size_t _phase_total_items[WeakProcessorPhases::phase_count];
 
-  // Per-worker times, if multiple threads used and the phase was executed.
-  WorkerDataArray<double>* _worker_phase_times_sec[WeakProcessorPhases::oop_storage_phase_count];
+  // Per-worker times and linked items, if multiple threads used and the phase was executed.
+  WorkerDataArray<double>* _worker_data[WeakProcessorPhases::oop_storage_phase_count];
+  WorkerDataArray<size_t>* _worker_dead_items[WeakProcessorPhases::oop_storage_phase_count];
+  WorkerDataArray<size_t>* _worker_total_items[WeakProcessorPhases::oop_storage_phase_count];
 
   WorkerDataArray<double>* worker_data(WeakProcessorPhase phase) const;
 
   void log_st_phase(WeakProcessorPhase phase, uint indent) const;
   void log_mt_phase_summary(WeakProcessorPhase phase, uint indent) const;
-  void log_mt_phase_details(WeakProcessorPhase phase, uint indent) const;
+  template <typename T>
+  void log_mt_phase_details(WorkerDataArray<T>* data, uint indent) const;
 
 public:
   WeakProcessorPhaseTimes(uint max_threads);
@@ -67,7 +76,9 @@
 
   void record_total_time_sec(double time_sec);
   void record_phase_time_sec(WeakProcessorPhase phase, double time_sec);
+  void record_phase_items(WeakProcessorPhase phase, size_t num_dead, size_t num_total);
   void record_worker_time_sec(uint worker_id, WeakProcessorPhase phase, double time_sec);
+  void record_worker_items(uint worker_id, WeakProcessorPhase phase, size_t num_dead, size_t num_total);
 
   void reset();
 
--- a/src/hotspot/share/gc/shared/weakProcessorPhases.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/weakProcessorPhases.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "classfile/stringTable.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "gc/shared/weakProcessorPhases.hpp"
 #include "runtime/jniHandles.hpp"
@@ -78,6 +79,7 @@
   JVMTI_ONLY(case jvmti: return "JVMTI weak processing";)
   JFR_ONLY(case jfr: return "JFR weak processing";)
   case jni: return "JNI weak processing";
+  case stringtable: return "StringTable weak processing";
   case vm: return "VM weak processing";
   default:
     ShouldNotReachHere();
@@ -98,9 +100,14 @@
 OopStorage* WeakProcessorPhases::oop_storage(Phase phase) {
   switch (phase) {
   case jni: return JNIHandles::weak_global_handles();
+  case stringtable: return StringTable::weak_storage();
   case vm: return SystemDictionary::vm_weak_oop_storage();
   default:
     ShouldNotReachHere();
     return NULL;
   }
 }
+
+bool WeakProcessorPhases::is_stringtable(Phase phase) {
+  return phase == stringtable;
+}
--- a/src/hotspot/share/gc/shared/weakProcessorPhases.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shared/weakProcessorPhases.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -44,6 +44,7 @@
 
     // OopStorage phases.
     jni,
+    stringtable,
     vm
   };
 
@@ -65,6 +66,8 @@
   static const char* description(Phase phase);
   static Processor processor(Phase phase); // Precondition: is_serial(phase)
   static OopStorage* oop_storage(Phase phase); // Precondition: is_oop_storage(phase)
+
+  static bool is_stringtable(Phase phase);
 };
 
 typedef WeakProcessorPhases::Phase WeakProcessorPhase;
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -1923,8 +1923,7 @@
                             ShenandoahPhaseTimings::full_gc_purge_par :
                             ShenandoahPhaseTimings::purge_par);
     uint active = _workers->active_workers();
-    StringDedupUnlinkOrOopsDoClosure dedup_cl(is_alive, NULL);
-    ParallelCleaningTask unlink_task(is_alive, &dedup_cl, active, purged_class);
+    ParallelCleaningTask unlink_task(is_alive, active, purged_class, true);
     _workers->run_task(&unlink_task);
   }
 
--- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp	Tue Jan 29 11:30:17 2019 +0100
@@ -78,7 +78,6 @@
   WeakProcessor::oops_do(oops);
   ObjectSynchronizer::oops_do(oops);
   SystemDictionary::oops_do(oops);
-  StringTable::oops_do(oops);
 
   if (ShenandoahStringDedup::is_enabled()) {
     ShenandoahStringDedup::oops_do_slow(oops);
@@ -209,13 +208,6 @@
       ObjectSynchronizer::oops_do(strong_roots);
     }
   }
-
-  // All threads execute the following. A specific chunk of buckets
-  // from the StringTable are the individual tasks.
-  if (weak_roots != NULL) {
-    ShenandoahWorkerTimingsTracker timer(worker_times, ShenandoahPhaseTimings::StringTableRoots, worker_id);
-    StringTable::possibly_parallel_oops_do(&_par_state_string, weak_roots);
-  }
 }
 
 uint ShenandoahRootProcessor::n_workers() const {
--- a/src/hotspot/share/logging/logPrefix.hpp	Tue Jan 29 10:13:23 2019 +0100
+++ b/src/hotspot/share/logging/logPrefix.hpp	Tue Jan 29 11:30:17 2019 +0100
@@ -79,7 +79,6 @@
   LOG_PREFIX(GCId::print_prefix, LOG_TAGS(gc, ref, start)) \
   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)) \
--- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java	Tue Jan 29 10:13:23 2019 +0100
+++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java	Tue Jan 29 11:30:17 2019 +0100
@@ -108,7 +108,6 @@
         new LogMessageWithLevel("Skipped Cards", Level.DEBUG),
         // Ext Root Scan
         new LogMessageWithLevel("Thread Roots", Level.TRACE),
-        new LogMessageWithLevel("StringTable Roots", Level.TRACE),
         new LogMessageWithLevel("Universe Roots", Level.TRACE),
         new LogMessageWithLevel("JNI Handles Roots", Level.TRACE),
         new LogMessageWithLevel("ObjectSynchronizer Roots", Level.TRACE),
@@ -126,7 +125,9 @@
         new LogMessageWithLevel("Redirtied Cards", Level.TRACE),
         // Misc Top-level
         new LogMessageWithLevel("Code Roots Purge", Level.DEBUG),
-        new LogMessageWithLevel("String Dedup Fixup", Level.DEBUG),
+        new LogMessageWithLevel("String Deduplication", Level.DEBUG),
+        new LogMessageWithLevel("Queue Fixup", Level.DEBUG),
+        new LogMessageWithLevel("Table Fixup", Level.DEBUG),
         new LogMessageWithLevel("Expand Heap After Collection", Level.DEBUG),
         // Free CSet
         new LogMessageWithLevel("Free Collection Set", Level.DEBUG),
@@ -145,6 +146,9 @@
         new LogMessageWithLevel("Reference Processing", Level.DEBUG),
         // VM internal reference processing
         new LogMessageWithLevel("Weak Processing", Level.DEBUG),
+        new LogMessageWithLevel("JNI weak processing", Level.DEBUG),
+        new LogMessageWithLevel("StringTable weak processing", Level.DEBUG),
+        new LogMessageWithLevel("VM weak processing", Level.DEBUG),
 
         new LogMessageWithLevelC2OrJVMCIOnly("DerivedPointerTable Update", Level.DEBUG),
         new LogMessageWithLevel("Start New Collection Set", Level.DEBUG),
--- a/test/hotspot/jtreg/gc/g1/TestStringTableStats.java	Tue Jan 29 10:13:23 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2013, 2019, 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.
- */
-
-package gc.g1;
-
-/*
- * @test TestStringTableStats
- * @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
- * @run main gc.g1.TestStringTableStats
- */
-
-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();
-    }
-  }
-}
--- a/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java	Tue Jan 29 10:13:23 2019 +0100
+++ b/test/jdk/jdk/jfr/event/gc/collection/TestG1ParallelPhases.java	Tue Jan 29 11:30:17 2019 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2019, 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
@@ -90,7 +90,6 @@
         Set<String> allPhases = of(
             "ExtRootScan",
             "ThreadRoots",
-            "StringTableRoots",
             "UniverseRoots",
             "JNIRoots",
             "ObjectSynchronizerRoots",