changeset 53614:739eaf4ac4ad

8213753: SymbolTable is double walked during class unloading and clean up table timing in do_unloading Summary: remove gc timing for short runtime cleanup triggering; make symbol table cleaning triggered automatically on unloading Reviewed-by: shade, stefank, gziemski
author coleenp
date Thu, 31 Jan 2019 10:29:53 -0500
parents 5bc1634bc0ca
children a5f46c4690f8
files src/hotspot/share/classfile/stringTable.cpp src/hotspot/share/classfile/symbolTable.cpp src/hotspot/share/classfile/symbolTable.hpp src/hotspot/share/classfile/systemDictionary.cpp src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp src/hotspot/share/gc/parallel/psMarkSweep.cpp src/hotspot/share/gc/parallel/psParallelCompact.cpp src/hotspot/share/gc/serial/genMarkSweep.cpp
diffstat 8 files changed, 40 insertions(+), 132 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/classfile/stringTable.cpp	Thu Jan 31 10:18:41 2019 -0500
+++ b/src/hotspot/share/classfile/stringTable.cpp	Thu Jan 31 10:29:53 2019 -0500
@@ -468,7 +468,7 @@
   if ((dead_factor > load_factor) ||
       (load_factor > PREF_AVG_LIST_LEN) ||
       (dead_factor > CLEAN_DEAD_HIGH_WATER_MARK)) {
-    log_debug(stringtable)("Concurrent work triggered, live factor:%g dead factor:%g",
+    log_debug(stringtable)("Concurrent work triggered, live factor: %g dead factor: %g",
                            load_factor, dead_factor);
     trigger_concurrent_work();
   }
--- a/src/hotspot/share/classfile/symbolTable.cpp	Thu Jan 31 10:18:41 2019 -0500
+++ b/src/hotspot/share/classfile/symbolTable.cpp	Thu Jan 31 10:29:53 2019 -0500
@@ -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
@@ -49,10 +49,6 @@
 const size_t END_SIZE = 17;
 // If a chain gets to 100 something might be wrong
 const size_t 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
-const double CLEAN_DEAD_HIGH_WATER_MARK = 0.0;
 
 const size_t ON_STACK_BUFFER_LENGTH = 128;
 
@@ -142,7 +138,7 @@
 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) {
+  _items_count(0), _has_items_to_clean(false) {
 
   size_t start_size_log_2 = ceil_log2(SymbolTableSize);
   _current_size = ((size_t)1) << start_size_log_2;
@@ -171,22 +167,14 @@
   }
 }
 
+void SymbolTable::reset_has_items_to_clean() { Atomic::store(false, &_has_items_to_clean); }
+void SymbolTable::mark_has_items_to_clean()  { Atomic::store(true, &_has_items_to_clean); }
+bool SymbolTable::has_items_to_clean() const { return Atomic::load(&_has_items_to_clean); }
+
 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);
-}
-
-// Mark one item as needing to be cleaned, but only if no other items are marked yet
-void SymbolTable::mark_item_clean_count() {
-  if (Atomic::cmpxchg((size_t)1, &(SymbolTable::the_table()->_uncleaned_items_count), (size_t)0) == 0) {
-    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));
@@ -196,15 +184,11 @@
   return (double)_items_count/_current_size;
 }
 
-double SymbolTable::get_dead_factor() const {
-  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() {
+void SymbolTable::trigger_cleanup() {
   MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
   SymbolTable::the_table()->_has_work = true;
   Service_lock->notify_all();
@@ -490,12 +474,7 @@
   update_needs_rehash(rehash_warning);
 
   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();
+    mark_has_items_to_clean();
     check_concurrent_work();
   }
 
@@ -697,7 +676,7 @@
       }
       bdt.cont(jt);
     }
-    SymbolTable::the_table()->set_item_clean_count(0);
+    SymbolTable::the_table()->reset_has_items_to_clean();
     bdt.done(jt);
   }
 
@@ -711,17 +690,13 @@
   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,
+  // We should clean/resize if we have
   // 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();
+  if (has_items_to_clean() || (get_load_factor() > PREF_AVG_LIST_LEN)) {
+    log_debug(symboltable)("Concurrent work triggered, load factor: %f, items to clean: %s",
+                           get_load_factor(), has_items_to_clean() ? "true" : "false");
+    trigger_cleanup();
   }
 }
 
@@ -737,34 +712,6 @@
   _has_work = false;
 }
 
-class CountDead : StackObj {
-  size_t _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;
-  };
-  size_t get_dead_count() const {
-    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);
 }
@@ -800,7 +747,7 @@
   if (get_load_factor() > PREF_AVG_LIST_LEN &&
       !_local_table->is_max_size_reached()) {
     log_debug(symboltable)("Choosing growing over rehashing.");
-    trigger_concurrent_work();
+    trigger_cleanup();
     _needs_rehashing = false;
     return;
   }
@@ -808,7 +755,7 @@
   // Already rehashed.
   if (rehashed) {
     log_warning(symboltable)("Rehashing already done, still long lists.");
-    trigger_concurrent_work();
+    trigger_cleanup();
     _needs_rehashing = false;
     return;
   }
--- a/src/hotspot/share/classfile/symbolTable.hpp	Thu Jan 31 10:18:41 2019 -0500
+++ b/src/hotspot/share/classfile/symbolTable.hpp	Thu Jan 31 10:29:53 2019 -0500
@@ -123,18 +123,19 @@
   volatile bool _needs_rehashing;
 
   volatile size_t _items_count;
-  volatile size_t _uncleaned_items_count;
+  volatile bool   _has_items_to_clean;
 
   double get_load_factor() const;
-  double get_dead_factor() const;
 
   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();
+
+  // For cleaning
+  void reset_has_items_to_clean();
+  void mark_has_items_to_clean();
+  bool has_items_to_clean() const;
 
   SymbolTable();
 
@@ -190,12 +191,9 @@
     initialize_symbols(symbol_alloc_arena_size);
   }
 
-  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; }
+  static void trigger_cleanup();
 
   // Probing
   static Symbol* lookup(const char* name, int len, TRAPS);
--- a/src/hotspot/share/classfile/systemDictionary.cpp	Thu Jan 31 10:18:41 2019 -0500
+++ b/src/hotspot/share/classfile/systemDictionary.cpp	Thu Jan 31 10:29:53 2019 -0500
@@ -1825,40 +1825,27 @@
     if (unloading_occurred) {
       MutexLockerEx ml2(is_concurrent ? Module_lock : NULL);
       JFR_ONLY(Jfr::on_unloading_classes();)
+
       MutexLockerEx ml1(is_concurrent ? SystemDictionary_lock : NULL);
       ClassLoaderDataGraph::clean_module_and_package_info();
-    }
-  }
-
-  // Cleanup ResolvedMethodTable even if no unloading occurred.
-  {
-    GCTraceTime(Debug, gc, phases) t("ResolvedMethodTable", gc_timer);
-    ResolvedMethodTable::trigger_cleanup();
-  }
-
-  if (unloading_occurred) {
-    {
-      GCTraceTime(Debug, gc, phases) t("SymbolTable", gc_timer);
-      // Check if there's work to do in the SymbolTable
-      SymbolTable::do_check_concurrent_work();
-    }
-
-    {
-      MutexLockerEx ml(is_concurrent ? SystemDictionary_lock : NULL);
-      GCTraceTime(Debug, gc, phases) t("Dictionary", gc_timer);
       constraints()->purge_loader_constraints();
       resolution_errors()->purge_resolution_errors();
     }
-
-    {
-      GCTraceTime(Debug, gc, phases) t("ResolvedMethodTable", gc_timer);
-      // Oops referenced by the protection domain cache table may get unreachable independently
-      // of the class loader (eg. cached protection domain oops). So we need to
-      // explicitly unlink them here.
-      // All protection domain oops are linked to the caller class, so if nothing
-      // unloads, this is not needed.
-      _pd_cache_table->trigger_cleanup();
-    }
+  }
+
+  GCTraceTime(Debug, gc, phases) t("Trigger cleanups", gc_timer);
+  // Trigger cleaning the ResolvedMethodTable even if no unloading occurred.
+  ResolvedMethodTable::trigger_cleanup();
+
+  if (unloading_occurred) {
+    SymbolTable::trigger_cleanup();
+
+    // Oops referenced by the protection domain cache table may get unreachable independently
+    // of the class loader (eg. cached protection domain oops). So we need to
+    // explicitly unlink them here.
+    // All protection domain oops are linked to the caller class, so if nothing
+    // unloads, this is not needed.
+    _pd_cache_table->trigger_cleanup();
   }
 
   return unloading_occurred;
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp	Thu Jan 31 10:18:41 2019 -0500
+++ b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp	Thu Jan 31 10:29:53 2019 -0500
@@ -5269,12 +5269,6 @@
       // Prune dead klasses from subklass/sibling/implementor lists.
       Klass::clean_weak_klass_links(purged_class);
     }
-
-    {
-      GCTraceTime(Debug, gc, phases) t("Scrub Symbol Table", _gc_timer_cm);
-      // Clean up unreferenced symbols in symbol table.
-      SymbolTable::unlink();
-    }
   }
 
   // Restore any preserved marks as a result of mark stack or
--- a/src/hotspot/share/gc/parallel/psMarkSweep.cpp	Thu Jan 31 10:18:41 2019 -0500
+++ b/src/hotspot/share/gc/parallel/psMarkSweep.cpp	Thu Jan 31 10:29:53 2019 -0500
@@ -566,12 +566,6 @@
     Klass::clean_weak_klass_links(purged_class);
   }
 
-  {
-    GCTraceTime(Debug, gc, phases) t("Scrub Symbol Table", _gc_timer);
-    // Clean up unreferenced symbols in symbol table.
-    SymbolTable::unlink();
-  }
-
   _gc_tracer->report_object_count_after_gc(is_alive_closure());
 }
 
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp	Thu Jan 31 10:18:41 2019 -0500
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp	Thu Jan 31 10:29:53 2019 -0500
@@ -2184,12 +2184,6 @@
     Klass::clean_weak_klass_links(purged_class);
   }
 
-  {
-    GCTraceTime(Debug, gc, phases) t("Scrub Symbol Table", &_gc_timer);
-    // Clean up unreferenced symbols in symbol table.
-    SymbolTable::unlink();
-  }
-
   _gc_tracer.report_object_count_after_gc(is_alive_closure());
 }
 
--- a/src/hotspot/share/gc/serial/genMarkSweep.cpp	Thu Jan 31 10:18:41 2019 -0500
+++ b/src/hotspot/share/gc/serial/genMarkSweep.cpp	Thu Jan 31 10:29:53 2019 -0500
@@ -239,12 +239,6 @@
     Klass::clean_weak_klass_links(purged_class);
   }
 
-  {
-    GCTraceTime(Debug, gc, phases) t("Scrub Symbol Table", gc_timer());
-    // Clean up unreferenced symbols in symbol table.
-    SymbolTable::unlink();
-  }
-
   gc_tracer()->report_object_count_after_gc(&is_alive);
 }