8209189: Make CompiledMethod::do_unloading more concurrent
authoreosterlund
Fri, 02 Nov 2018 08:33:59 +0100
changeset 52385 5c679ec60888
parent 52384 d6dc479bcdd3
child 52386 e256b3b62e20
8209189: Make CompiledMethod::do_unloading more concurrent Reviewed-by: kvn, coleenp
src/hotspot/share/aot/aotCodeHeap.cpp
src/hotspot/share/aot/aotCompiledMethod.cpp
src/hotspot/share/aot/aotCompiledMethod.hpp
src/hotspot/share/code/codeCache.cpp
src/hotspot/share/code/codeCache.hpp
src/hotspot/share/code/compiledMethod.cpp
src/hotspot/share/code/compiledMethod.hpp
src/hotspot/share/code/nmethod.cpp
src/hotspot/share/code/nmethod.hpp
src/hotspot/share/gc/g1/g1CollectedHeap.cpp
src/hotspot/share/gc/shared/gcBehaviours.cpp
src/hotspot/share/gc/shared/gcBehaviours.hpp
src/hotspot/share/gc/shared/parallelCleaning.cpp
src/hotspot/share/gc/shared/parallelCleaning.hpp
src/hotspot/share/runtime/sweeper.cpp
--- a/src/hotspot/share/aot/aotCodeHeap.cpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/aot/aotCodeHeap.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -926,7 +926,7 @@
       continue; // Skip uninitialized entries.
     }
     AOTCompiledMethod* aot = _code_to_aot[index]._aot;
-    aot->cleanup_inline_caches();
+    aot->cleanup_inline_caches(false);
   }
 }
 
--- a/src/hotspot/share/aot/aotCompiledMethod.cpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/aot/aotCompiledMethod.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -75,10 +75,6 @@
   return (address*) ((address)fr->unextended_sp() + _meta->orig_pc_offset());
 }
 
-bool AOTCompiledMethod::do_unloading_oops(address low_boundary, BoolObjectClosure* is_alive) {
-  return false;
-}
-
 oop AOTCompiledMethod::oop_at(int index) const {
   if (index == 0) { // 0 is reserved
     return NULL;
@@ -352,7 +348,7 @@
   log->print(" aot='%2d'", _heap->dso_id());
 }
 
-void AOTCompiledMethod::log_state_change(oop cause) const {
+void AOTCompiledMethod::log_state_change() const {
   if (LogCompilation) {
     ResourceMark m;
     if (xtty != NULL) {
--- a/src/hotspot/share/aot/aotCompiledMethod.hpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/aot/aotCompiledMethod.hpp	Fri Nov 02 08:33:59 2018 +0100
@@ -193,7 +193,7 @@
   virtual int comp_level() const { return CompLevel_aot; }
   virtual address verified_entry_point() const { return _code + _meta->verified_entry_offset(); }
   virtual void log_identity(xmlStream* stream) const;
-  virtual void log_state_change(oop cause = NULL) const;
+  virtual void log_state_change() const;
   virtual bool make_entrant() NOT_TIERED({ ShouldNotReachHere(); return false; });
   virtual bool make_not_entrant() { return make_not_entrant_helper(not_entrant); }
   virtual bool make_not_used() { return make_not_entrant_helper(not_used); }
@@ -277,11 +277,6 @@
   CompiledStaticCall* compiledStaticCall_before(address addr) const;
 private:
   bool is_aot_runtime_stub() const { return _method == NULL; }
-
-protected:
-  virtual bool do_unloading_oops(address low_boundary, BoolObjectClosure* is_alive);
-  virtual bool do_unloading_jvmci() { return false; }
-
 };
 
 class PltNativeCallWrapper: public NativeCallWrapper {
--- a/src/hotspot/share/code/codeCache.cpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/code/codeCache.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -144,7 +144,6 @@
 address CodeCache::_low_bound = 0;
 address CodeCache::_high_bound = 0;
 int CodeCache::_number_of_nmethods_with_dependencies = 0;
-bool CodeCache::_needs_cache_clean = false;
 nmethod* CodeCache::_scavenge_root_nmethods = NULL;
 
 // Initialize arrays of CodeHeap subsets
@@ -683,17 +682,11 @@
 // Mark nmethods for unloading if they contain otherwise unreachable oops.
 void CodeCache::do_unloading(BoolObjectClosure* is_alive, bool unloading_occurred) {
   assert_locked_or_safepoint(CodeCache_lock);
+  UnloadingScope scope(is_alive);
   CompiledMethodIterator iter;
   while(iter.next_alive()) {
-    iter.method()->do_unloading(is_alive);
+    iter.method()->do_unloading(unloading_occurred);
   }
-
-  // Now that all the unloaded nmethods are known, cleanup caches
-  // before CLDG is purged.
-  // This is another code cache walk but it is moved from gc_epilogue.
-  // G1 does a parallel walk of the nmethods so cleans them up
-  // as it goes and doesn't call this.
-  do_unloading_nmethod_caches(unloading_occurred);
 }
 
 void CodeCache::blobs_do(CodeBlobClosure* f) {
@@ -908,28 +901,14 @@
   prune_scavenge_root_nmethods();
 }
 
+uint8_t CodeCache::_unloading_cycle = 1;
 
-void CodeCache::do_unloading_nmethod_caches(bool class_unloading_occurred) {
-  assert_locked_or_safepoint(CodeCache_lock);
-  // Even if classes are not unloaded, there may have been some nmethods that are
-  // unloaded because oops in them are no longer reachable.
-  NOT_DEBUG(if (needs_cache_clean() || class_unloading_occurred)) {
-    CompiledMethodIterator iter;
-    while(iter.next_alive()) {
-      CompiledMethod* cm = iter.method();
-      assert(!cm->is_unloaded(), "Tautology");
-      DEBUG_ONLY(if (needs_cache_clean() || class_unloading_occurred)) {
-        // Clean up both unloaded klasses from nmethods and unloaded nmethods
-        // from inline caches.
-        cm->unload_nmethod_caches(/*parallel*/false, class_unloading_occurred);
-      }
-      DEBUG_ONLY(cm->verify());
-      DEBUG_ONLY(cm->verify_oop_relocations());
-    }
+void CodeCache::increment_unloading_cycle() {
+  if (_unloading_cycle == 1) {
+    _unloading_cycle = 2;
+  } else {
+    _unloading_cycle = 1;
   }
-
-  set_needs_cache_clean(false);
-  verify_icholder_relocations();
 }
 
 void CodeCache::verify_oops() {
--- a/src/hotspot/share/code/codeCache.hpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/code/codeCache.hpp	Fri Nov 02 08:33:59 2018 +0100
@@ -27,6 +27,7 @@
 
 #include "code/codeBlob.hpp"
 #include "code/nmethod.hpp"
+#include "gc/shared/gcBehaviours.hpp"
 #include "memory/allocation.hpp"
 #include "memory/heap.hpp"
 #include "oops/instanceKlass.hpp"
@@ -90,8 +91,8 @@
   static address _low_bound;                            // Lower bound of CodeHeap addresses
   static address _high_bound;                           // Upper bound of CodeHeap addresses
   static int _number_of_nmethods_with_dependencies;     // Total number of nmethods with dependencies
-  static bool _needs_cache_clean;                       // True if inline caches of the nmethods needs to be flushed
   static nmethod* _scavenge_root_nmethods;              // linked via nm->scavenge_root_link()
+  static uint8_t _unloading_cycle;                      // Global state for recognizing old nmethods that need to be unloaded
 
   static void mark_scavenge_root_nmethods() PRODUCT_RETURN;
   static void verify_perm_nmethods(CodeBlobClosure* f_or_null) PRODUCT_RETURN;
@@ -172,7 +173,24 @@
   // to) any unmarked codeBlobs in the cache.  Sets "marked_for_unloading"
   // to "true" iff some code got unloaded.
   // "unloading_occurred" controls whether metadata should be cleaned because of class unloading.
+  class UnloadingScope: StackObj {
+    ClosureIsUnloadingBehaviour _is_unloading_behaviour;
+
+  public:
+    UnloadingScope(BoolObjectClosure* is_alive)
+      : _is_unloading_behaviour(is_alive)
+    {
+      IsUnloadingBehaviour::set_current(&_is_unloading_behaviour);
+      increment_unloading_cycle();
+    }
+
+    ~UnloadingScope() {
+      IsUnloadingBehaviour::set_current(NULL);
+    }
+  };
   static void do_unloading(BoolObjectClosure* is_alive, bool unloading_occurred);
+  static uint16_t unloading_cycle() { return _unloading_cycle; }
+  static void increment_unloading_cycle();
   static void asserted_non_scavengable_nmethods_do(CodeBlobClosure* f = NULL) PRODUCT_RETURN;
 
   // Apply f to every live code blob in scavengable nmethods. Prune nmethods
@@ -222,12 +240,8 @@
 
   static double reverse_free_ratio(int code_blob_type);
 
-  static bool needs_cache_clean()                     { return _needs_cache_clean; }
-  static void set_needs_cache_clean(bool v)           { _needs_cache_clean = v;    }
-
   static void clear_inline_caches();                  // clear all inline caches
   static void cleanup_inline_caches();                // clean unloaded/zombie nmethods from inline caches
-  static void do_unloading_nmethod_caches(bool class_unloading_occurred);  // clean all nmethod caches for unloading, including inline caches
 
   // Returns true if an own CodeHeap for the given CodeBlobType is available
   static bool heap_available(int code_blob_type);
--- a/src/hotspot/share/code/compiledMethod.cpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/code/compiledMethod.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -27,6 +27,8 @@
 #include "code/compiledMethod.inline.hpp"
 #include "code/scopeDesc.hpp"
 #include "code/codeCache.hpp"
+#include "gc/shared/barrierSet.hpp"
+#include "gc/shared/gcBehaviours.hpp"
 #include "interpreter/bytecode.inline.hpp"
 #include "logging/log.hpp"
 #include "logging/logTag.hpp"
@@ -37,16 +39,29 @@
 #include "runtime/handles.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 
-CompiledMethod::CompiledMethod(Method* method, const char* name, CompilerType type, const CodeBlobLayout& layout, int frame_complete_offset, int frame_size, ImmutableOopMapSet* oop_maps, bool caller_must_gc_arguments)
+CompiledMethod::CompiledMethod(Method* method, const char* name, CompilerType type, const CodeBlobLayout& layout,
+                               int frame_complete_offset, int frame_size, ImmutableOopMapSet* oop_maps,
+                               bool caller_must_gc_arguments)
   : CodeBlob(name, type, layout, frame_complete_offset, frame_size, oop_maps, caller_must_gc_arguments),
-  _mark_for_deoptimization_status(not_marked), _method(method) {
+    _mark_for_deoptimization_status(not_marked),
+    _is_unloading_state(0),
+    _method(method)
+{
   init_defaults();
+  clear_unloading_state();
 }
 
-CompiledMethod::CompiledMethod(Method* method, const char* name, CompilerType type, int size, int header_size, CodeBuffer* cb, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments)
-  : CodeBlob(name, type, CodeBlobLayout((address) this, size, header_size, cb), cb, frame_complete_offset, frame_size, oop_maps, caller_must_gc_arguments),
-  _mark_for_deoptimization_status(not_marked), _method(method) {
+CompiledMethod::CompiledMethod(Method* method, const char* name, CompilerType type, int size,
+                               int header_size, CodeBuffer* cb, int frame_complete_offset, int frame_size,
+                               OopMapSet* oop_maps, bool caller_must_gc_arguments)
+  : CodeBlob(name, type, CodeBlobLayout((address) this, size, header_size, cb), cb,
+             frame_complete_offset, frame_size, oop_maps, caller_must_gc_arguments),
+    _mark_for_deoptimization_status(not_marked),
+    _is_unloading_state(0),
+    _method(method)
+{
   init_defaults();
+  clear_unloading_state();
 }
 
 void CompiledMethod::init_defaults() {
@@ -54,7 +69,6 @@
   _has_method_handle_invokes  = 0;
   _lazy_critical_native       = 0;
   _has_wide_vectors           = 0;
-  _unloading_clock            = 0;
 }
 
 bool CompiledMethod::is_method_handle_return(address return_pc) {
@@ -385,26 +399,6 @@
   ic->set_to_clean();
 }
 
-unsigned char CompiledMethod::_global_unloading_clock = 0;
-
-void CompiledMethod::increase_unloading_clock() {
-  _global_unloading_clock++;
-  if (_global_unloading_clock == 0) {
-    // _nmethods are allocated with _unloading_clock == 0,
-    // so 0 is never used as a clock value.
-    _global_unloading_clock = 1;
-  }
-}
-
-void CompiledMethod::set_unloading_clock(unsigned char unloading_clock) {
-  OrderAccess::release_store(&_unloading_clock, unloading_clock);
-}
-
-unsigned char CompiledMethod::unloading_clock() {
-  return OrderAccess::load_acquire(&_unloading_clock);
-}
-
-
 // static_stub_Relocations may have dangling references to
 // nmethods so trim them out here.  Otherwise it looks like
 // compiled code is maintaining a link to dead metadata.
@@ -438,84 +432,30 @@
 #endif
 }
 
-// This is called at the end of the strong tracing/marking phase of a
-// GC to unload an nmethod if it contains otherwise unreachable
-// oops.
-
-void CompiledMethod::do_unloading(BoolObjectClosure* is_alive) {
-  // Make sure the oop's ready to receive visitors
-  assert(!is_zombie() && !is_unloaded(),
-         "should not call follow on zombie or unloaded nmethod");
-
-  address low_boundary = oops_reloc_begin();
-
-  if (do_unloading_oops(low_boundary, is_alive)) {
-    return;
-  }
-
-#if INCLUDE_JVMCI
-  if (do_unloading_jvmci()) {
-    return;
-  }
-#endif
-
-  // Cleanup exception cache and inline caches happens
-  // after all the unloaded methods are found.
-}
-
 // Clean references to unloaded nmethods at addr from this one, which is not unloaded.
 template <class CompiledICorStaticCall>
-static bool clean_if_nmethod_is_unloaded(CompiledICorStaticCall *ic, address addr, CompiledMethod* from,
-                                         bool parallel, bool clean_all) {
+static void clean_if_nmethod_is_unloaded(CompiledICorStaticCall *ic, address addr, CompiledMethod* from,
+                                         bool clean_all) {
   // Ok, to lookup references to zombies here
   CodeBlob *cb = CodeCache::find_blob_unsafe(addr);
   CompiledMethod* nm = (cb != NULL) ? cb->as_compiled_method_or_null() : NULL;
   if (nm != NULL) {
-    if (parallel && nm->unloading_clock() != CompiledMethod::global_unloading_clock()) {
-      // The nmethod has not been processed yet.
-      return true;
-    }
-
     // Clean inline caches pointing to both zombie and not_entrant methods
-    if (clean_all || !nm->is_in_use() || (nm->method()->code() != nm)) {
+    if (clean_all || !nm->is_in_use() || nm->is_unloading() || (nm->method()->code() != nm)) {
       ic->set_to_clean(from->is_alive());
       assert(ic->is_clean(), "nmethod " PTR_FORMAT "not clean %s", p2i(from), from->method()->name_and_sig_as_C_string());
     }
   }
-
-  return false;
-}
-
-static bool clean_if_nmethod_is_unloaded(CompiledIC *ic, CompiledMethod* from,
-                                         bool parallel, bool clean_all = false) {
-  return clean_if_nmethod_is_unloaded(ic, ic->ic_destination(), from, parallel, clean_all);
-}
-
-static bool clean_if_nmethod_is_unloaded(CompiledStaticCall *csc, CompiledMethod* from,
-                                         bool parallel, bool clean_all = false) {
-  return clean_if_nmethod_is_unloaded(csc, csc->destination(), from, parallel, clean_all);
 }
 
-bool CompiledMethod::do_unloading_parallel(BoolObjectClosure* is_alive, bool unloading_occurred) {
-  ResourceMark rm;
-
-  // Make sure the oop's ready to receive visitors
-  assert(!is_zombie() && !is_unloaded(),
-         "should not call follow on zombie or unloaded nmethod");
-
-  address low_boundary = oops_reloc_begin();
+static void clean_if_nmethod_is_unloaded(CompiledIC *ic, CompiledMethod* from,
+                                         bool clean_all) {
+  clean_if_nmethod_is_unloaded(ic, ic->ic_destination(), from, clean_all);
+}
 
-  if (do_unloading_oops(low_boundary, is_alive)) {
-    return false;
-  }
-
-#if INCLUDE_JVMCI
-  if (do_unloading_jvmci()) {
-    return false;
-  }
-#endif
-
-  return unload_nmethod_caches(/*parallel*/true, unloading_occurred);
+static void clean_if_nmethod_is_unloaded(CompiledStaticCall *csc, CompiledMethod* from,
+                                         bool clean_all) {
+  clean_if_nmethod_is_unloaded(csc, csc->destination(), from, clean_all);
 }
 
 // Cleans caches in nmethods that point to either classes that are unloaded
@@ -525,29 +465,70 @@
 // nmethods are unloaded.  Return postponed=true in the parallel case for
 // inline caches found that point to nmethods that are not yet visited during
 // the do_unloading walk.
-bool CompiledMethod::unload_nmethod_caches(bool parallel, bool unloading_occurred) {
+void CompiledMethod::unload_nmethod_caches(bool unloading_occurred) {
+  ResourceMark rm;
 
   // Exception cache only needs to be called if unloading occurred
   if (unloading_occurred) {
     clean_exception_cache();
   }
 
-  bool postponed = cleanup_inline_caches_impl(parallel, unloading_occurred, /*clean_all*/false);
+  cleanup_inline_caches_impl(unloading_occurred, false);
 
   // All static stubs need to be cleaned.
   clean_ic_stubs();
 
   // Check that the metadata embedded in the nmethod is alive
   DEBUG_ONLY(metadata_do(check_class));
+}
 
-  return postponed;
+// The IsUnloadingStruct represents a tuple comprising a result of
+// IsUnloadingBehaviour::is_unloading() for a given unloading cycle.
+struct IsUnloadingStruct {
+  unsigned int _is_unloading:1;
+  unsigned int _unloading_cycle:2;
+};
+
+// The IsUnloadingUnion allows treating the tuple of the IsUnloadingStruct
+// like a uint8_t, making it possible to read and write the tuple atomically.
+union IsUnloadingUnion {
+  IsUnloadingStruct _inflated;
+  uint8_t _value;
+};
+
+bool CompiledMethod::is_unloading() {
+  IsUnloadingUnion state;
+  state._value = RawAccess<MO_RELAXED>::load(&_is_unloading_state);
+  if (state._inflated._is_unloading == 1) {
+    return true;
+  }
+  if (state._inflated._unloading_cycle == CodeCache::unloading_cycle()) {
+    return state._inflated._is_unloading == 1;
+  }
+
+  // The IsUnloadingBehaviour is responsible for checking if there are any dead
+  // oops in the CompiledMethod, by calling oops_do on it.
+  bool result = IsUnloadingBehaviour::current()->is_unloading(this);
+
+  state._inflated._unloading_cycle = CodeCache::unloading_cycle();
+  state._inflated._is_unloading = result ? 1 : 0;
+
+  RawAccess<MO_RELAXED>::store(&_is_unloading_state, state._value);
+
+  return result;
+}
+
+void CompiledMethod::clear_unloading_state() {
+  IsUnloadingUnion state;
+  state._inflated._unloading_cycle = CodeCache::unloading_cycle();
+  state._inflated._is_unloading = 0;
+  RawAccess<MO_RELAXED>::store(&_is_unloading_state, state._value);
 }
 
 // Called to clean up after class unloading for live nmethods and from the sweeper
 // for all methods.
-bool CompiledMethod::cleanup_inline_caches_impl(bool parallel, bool unloading_occurred, bool clean_all) {
+void CompiledMethod::cleanup_inline_caches_impl(bool unloading_occurred, bool clean_all) {
   assert(CompiledICLocker::is_safe(this), "mt unsafe call");
-  bool postponed = false;
   ResourceMark rm;
 
   // Find all calls in an nmethod and clear the ones that point to non-entrant,
@@ -564,19 +545,18 @@
         clean_ic_if_metadata_is_dead(CompiledIC_at(&iter));
       }
 
-      postponed |= clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, parallel, clean_all);
+      clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, clean_all);
       break;
 
     case relocInfo::opt_virtual_call_type:
-      postponed |= clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, parallel, clean_all);
+      clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, clean_all);
       break;
 
     case relocInfo::static_call_type:
-      postponed |= clean_if_nmethod_is_unloaded(compiledStaticCall_at(iter.reloc()), this, parallel, clean_all);
+      clean_if_nmethod_is_unloaded(compiledStaticCall_at(iter.reloc()), this, clean_all);
       break;
 
     case relocInfo::oop_type:
-      // handled by do_unloading_oops already
       break;
 
     case relocInfo::metadata_type:
@@ -586,38 +566,6 @@
       break;
     }
   }
-
-  return postponed;
-}
-
-void CompiledMethod::do_unloading_parallel_postponed() {
-  ResourceMark rm;
-
-  // Make sure the oop's ready to receive visitors
-  assert(!is_zombie(),
-         "should not call follow on zombie nmethod");
-
-  RelocIterator iter(this, oops_reloc_begin());
-  while(iter.next()) {
-
-    switch (iter.type()) {
-
-    case relocInfo::virtual_call_type:
-      clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, true);
-      break;
-
-    case relocInfo::opt_virtual_call_type:
-      clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), this, true);
-      break;
-
-    case relocInfo::static_call_type:
-      clean_if_nmethod_is_unloaded(compiledStaticCall_at(iter.reloc()), this, true);
-      break;
-
-    default:
-      break;
-    }
-  }
 }
 
 // Iterating over all nmethods, e.g. with the help of CodeCache::nmethods_do(fun) was found
--- a/src/hotspot/share/code/compiledMethod.hpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/code/compiledMethod.hpp	Fri Nov 02 08:33:59 2018 +0100
@@ -147,6 +147,9 @@
 
   bool _is_far_code; // Code is far from CodeCache.
                      // Have to use far call instructions to call it from code in CodeCache.
+
+  volatile uint8_t _is_unloading_state;      // Local state used to keep track of whether unloading is happening or not
+
   // set during construction
   unsigned int _has_unsafe_access:1;         // May fault due to unsafe access.
   unsigned int _has_method_handle_invokes:1; // Has this method MethodHandle invokes?
@@ -202,7 +205,7 @@
 
   virtual address verified_entry_point() const = 0;
   virtual void log_identity(xmlStream* log) const = 0;
-  virtual void log_state_change(oop cause = NULL) const = 0;
+  virtual void log_state_change() const = 0;
   virtual bool make_not_used() = 0;
   virtual bool make_not_entrant() = 0;
   virtual bool make_entrant() = 0;
@@ -333,17 +336,13 @@
 
   static address get_deopt_original_pc(const frame* fr);
 
-  // GC unloading support
-  // Cleans unloaded klasses and unloaded nmethods in inline caches
-  bool unload_nmethod_caches(bool parallel, bool class_unloading_occurred);
-
   // Inline cache support for class unloading and nmethod unloading
  private:
-  bool cleanup_inline_caches_impl(bool parallel, bool unloading_occurred, bool clean_all);
+  void cleanup_inline_caches_impl(bool unloading_occurred, bool clean_all);
  public:
-  bool cleanup_inline_caches(bool clean_all = false) {
+  void cleanup_inline_caches(bool clean_all) {
     // Serial version used by sweeper and whitebox test
-    return cleanup_inline_caches_impl(false, false, clean_all);
+    cleanup_inline_caches_impl(false, clean_all);
   }
 
   virtual void clear_inline_caches();
@@ -373,53 +372,32 @@
   virtual void metadata_do(void f(Metadata*)) = 0;
 
   // GC support
-
-  void set_unloading_next(CompiledMethod* next) { _unloading_next = next; }
-  CompiledMethod* unloading_next()              { return _unloading_next; }
-
  protected:
   address oops_reloc_begin() const;
+
  private:
   void static clean_ic_if_metadata_is_dead(CompiledIC *ic);
 
   void clean_ic_stubs();
 
  public:
-  virtual void do_unloading(BoolObjectClosure* is_alive);
-  //  The parallel versions are used by G1.
-  virtual bool do_unloading_parallel(BoolObjectClosure* is_alive, bool unloading_occurred);
-  virtual void do_unloading_parallel_postponed();
-
-  static unsigned char global_unloading_clock()   { return _global_unloading_clock; }
-  static void increase_unloading_clock();
+  // GC unloading support
+  // Cleans unloaded klasses and unloaded nmethods in inline caches
 
-  void set_unloading_clock(unsigned char unloading_clock);
-  unsigned char unloading_clock();
+  bool is_unloading();
 
-protected:
-  virtual bool do_unloading_oops(address low_boundary, BoolObjectClosure* is_alive) = 0;
-#if INCLUDE_JVMCI
-  virtual bool do_unloading_jvmci() = 0;
-#endif
+  void unload_nmethod_caches(bool class_unloading_occurred);
+  void clear_unloading_state();
+  virtual void do_unloading(bool unloading_occurred) { }
 
 private:
-  // GC support to help figure out if an nmethod has been
-  // cleaned/unloaded by the current GC.
-  static unsigned char _global_unloading_clock;
-
-  volatile unsigned char _unloading_clock;   // Incremented after GC unloaded/cleaned the nmethod
-
   PcDesc* find_pc_desc(address pc, bool approximate) {
     return _pc_desc_container.find_pc_desc(pc, approximate, PcDescSearch(code_begin(), scopes_pcs_begin(), scopes_pcs_end()));
   }
 
 protected:
-  union {
-    // Used by G1 to chain nmethods.
-    CompiledMethod* _unloading_next;
-    // Used by non-G1 GCs to chain nmethods.
-    nmethod* _scavenge_root_link; // from CodeCache::scavenge_root_nmethods
-  };
+  // Used by some GCs to chain nmethods.
+  nmethod* _scavenge_root_link; // from CodeCache::scavenge_root_nmethods
 };
 
 #endif //SHARE_VM_CODE_COMPILEDMETHOD_HPP
--- a/src/hotspot/share/code/nmethod.cpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/code/nmethod.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -413,7 +413,6 @@
   _oops_do_mark_link       = NULL;
   _jmethod_id              = NULL;
   _osr_link                = NULL;
-  _unloading_next          = NULL;
   _scavenge_root_link      = NULL;
   _scavenge_root_state     = 0;
 #if INCLUDE_RTM_OPT
@@ -599,6 +598,7 @@
 
     code_buffer->copy_code_and_locs_to(this);
     code_buffer->copy_values_to(this);
+
     if (ScavengeRootsInCode) {
       Universe::heap()->register_nmethod(this);
     }
@@ -757,6 +757,7 @@
     code_buffer->copy_values_to(this);
     debug_info->copy_to(this);
     dependencies->copy_to(this);
+    clear_unloading_state();
     if (ScavengeRootsInCode) {
       Universe::heap()->register_nmethod(this);
     }
@@ -1025,8 +1026,7 @@
   mdo->inc_decompile_count();
 }
 
-void nmethod::make_unloaded(oop cause) {
-
+void nmethod::make_unloaded() {
   post_compiled_method_unload();
 
   // This nmethod is being unloaded, make sure that dependencies
@@ -1042,11 +1042,8 @@
     LogStream ls(lt);
     ls.print("making nmethod " INTPTR_FORMAT
              " unloadable, Method*(" INTPTR_FORMAT
-             "), cause(" INTPTR_FORMAT ") ",
-             p2i(this), p2i(_method), p2i(cause));
-     if (cause != NULL) {
-       cause->print_value_on(&ls);
-     }
+             ") ",
+             p2i(this), p2i(_method));
      ls.cr();
   }
   // Unlink the osr method, so we do not look this up again
@@ -1079,12 +1076,6 @@
 
   // Make the class unloaded - i.e., change state and notify sweeper
   assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
-  if (is_in_use()) {
-    // Transitioning directly from live to unloaded -- so
-    // we need to force a cache clean-up; remember this
-    // for later on.
-    CodeCache::set_needs_cache_clean(true);
-  }
 
   // Unregister must be done before the state change
   Universe::heap()->unregister_nmethod(this);
@@ -1092,7 +1083,7 @@
   _state = unloaded;
 
   // Log the unloading.
-  log_state_change(cause);
+  log_state_change();
 
 #if INCLUDE_JVMCI
   // The method can only be unloaded after the pointer to the installed code
@@ -1116,7 +1107,7 @@
   }
 }
 
-void nmethod::log_state_change(oop cause) const {
+void nmethod::log_state_change() const {
   if (LogCompilation) {
     if (xtty != NULL) {
       ttyLocker ttyl;  // keep the following output all in one block
@@ -1129,9 +1120,6 @@
                          (_state == zombie ? " zombie='1'" : ""));
       }
       log_identity(xtty);
-      if (cause != NULL) {
-        xtty->print(" cause='%s'", cause->klass()->external_name());
-      }
       xtty->stamp();
       xtty->end_elem();
     }
@@ -1380,21 +1368,6 @@
   }
 }
 
-
-// If this oop is not live, the nmethod can be unloaded.
-bool nmethod::can_unload(BoolObjectClosure* is_alive, oop* root) {
-  assert(root != NULL, "just checking");
-  oop obj = *root;
-  if (obj == NULL || is_alive->do_object_b(obj)) {
-      return false;
-  }
-
-  // An nmethod might be unloaded simply because one of its constant oops has gone dead.
-  // No actual classes need to be unloaded in order for this to occur.
-  make_unloaded(obj);
-  return true;
-}
-
 // ------------------------------------------------------------------
 // post_compiled_method_load_event
 // new method for install_code() path
@@ -1468,70 +1441,6 @@
   set_unload_reported();
 }
 
-bool nmethod::unload_if_dead_at(RelocIterator* iter_at_oop, BoolObjectClosure *is_alive) {
-  assert(iter_at_oop->type() == relocInfo::oop_type, "Wrong relocation type");
-
-  oop_Relocation* r = iter_at_oop->oop_reloc();
-  // Traverse those oops directly embedded in the code.
-  // Other oops (oop_index>0) are seen as part of scopes_oops.
-  assert(1 == (r->oop_is_immediate()) +
-         (r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()),
-         "oop must be found in exactly one place");
-  if (r->oop_is_immediate() && r->oop_value() != NULL) {
-    // Unload this nmethod if the oop is dead.
-    if (can_unload(is_alive, r->oop_addr())) {
-      return true;;
-    }
-  }
-
-  return false;
-}
-
-bool nmethod::do_unloading_scopes(BoolObjectClosure* is_alive) {
-  // Scopes
-  for (oop* p = oops_begin(); p < oops_end(); p++) {
-    if (*p == Universe::non_oop_word())  continue;  // skip non-oops
-    if (can_unload(is_alive, p)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-bool nmethod::do_unloading_oops(address low_boundary, BoolObjectClosure* is_alive) {
-  // Compiled code
-
-  // Prevent extra code cache walk for platforms that don't have immediate oops.
-  if (relocInfo::mustIterateImmediateOopsInCode()) {
-    RelocIterator iter(this, low_boundary);
-    while (iter.next()) {
-      if (iter.type() == relocInfo::oop_type) {
-        if (unload_if_dead_at(&iter, is_alive)) {
-          return true;
-        }
-      }
-    }
-  }
-
-  return do_unloading_scopes(is_alive);
-}
-
-#if INCLUDE_JVMCI
-bool nmethod::do_unloading_jvmci() {
-  if (_jvmci_installed_code != NULL) {
-    if (JNIHandles::is_global_weak_cleared(_jvmci_installed_code)) {
-      if (_jvmci_installed_code_triggers_invalidation) {
-        // The reference to the installed code has been dropped so invalidate
-        // this nmethod and allow the sweeper to reclaim it.
-        make_not_entrant();
-      }
-      clear_jvmci_installed_code();
-    }
-  }
-  return false;
-}
-#endif
-
 // Iterate over metadata calling this function.   Used by RedefineClasses
 void nmethod::metadata_do(void f(Metadata*)) {
   {
@@ -1579,6 +1488,34 @@
   if (_method != NULL) f(_method);
 }
 
+
+// This is called at the end of the strong tracing/marking phase of a
+// GC to unload an nmethod if it contains otherwise unreachable
+// oops.
+
+void nmethod::do_unloading(bool unloading_occurred) {
+  // Make sure the oop's ready to receive visitors
+  assert(!is_zombie() && !is_unloaded(),
+         "should not call follow on zombie or unloaded nmethod");
+
+  if (is_unloading()) {
+    make_unloaded();
+  } else {
+#if INCLUDE_JVMCI
+    if (_jvmci_installed_code != NULL) {
+      if (JNIHandles::is_global_weak_cleared(_jvmci_installed_code)) {
+        if (_jvmci_installed_code_triggers_invalidation) {
+          make_not_entrant();
+        }
+        clear_jvmci_installed_code();
+      }
+    }
+#endif
+
+    unload_nmethod_caches(unloading_occurred);
+  }
+}
+
 void nmethod::oops_do(OopClosure* f, bool allow_zombie) {
   // make sure the oops ready to receive visitors
   assert(allow_zombie || !is_zombie(), "should not call follow on zombie nmethod");
--- a/src/hotspot/share/code/nmethod.hpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/code/nmethod.hpp	Fri Nov 02 08:33:59 2018 +0100
@@ -323,6 +323,8 @@
   bool  is_zombie() const                         { return _state == zombie; }
   bool  is_unloaded() const                       { return _state == unloaded; }
 
+  virtual void do_unloading(bool unloading_occurred);
+
 #if INCLUDE_RTM_OPT
   // rtm state accessing and manipulating
   RTMState  rtm_state() const                     { return _rtm_state; }
@@ -349,7 +351,7 @@
     return _state;
   }
 
-  void  make_unloaded(oop cause);
+  void  make_unloaded();
 
   bool has_dependencies()                         { return dependencies_size() != 0; }
   void flush_dependencies(bool delete_immediately);
@@ -483,20 +485,6 @@
  public:
 #endif
 
- protected:
-  virtual bool do_unloading_oops(address low_boundary, BoolObjectClosure* is_alive);
-#if INCLUDE_JVMCI
-  // See comment for _jvmci_installed_code_triggers_invalidation field.
-  // Returns whether this nmethod was unloaded.
-  virtual bool do_unloading_jvmci();
-#endif
-
- private:
-  bool do_unloading_scopes(BoolObjectClosure* is_alive);
-  //  Unload a nmethod if the *root object is dead.
-  bool can_unload(BoolObjectClosure* is_alive, oop* root);
-  bool unload_if_dead_at(RelocIterator *iter_at_oop, BoolObjectClosure* is_alive);
-
  public:
   void oops_do(OopClosure* f) { oops_do(f, false); }
   void oops_do(OopClosure* f, bool allow_zombie);
@@ -555,7 +543,7 @@
   // Logging
   void log_identity(xmlStream* log) const;
   void log_new_nmethod() const;
-  void log_state_change(oop cause = NULL) const;
+  void log_state_change() const;
 
   // Prints block-level comments, including nmethod specific block labels:
   virtual void print_block_comment(outputStream* stream, address block_begin) const {
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -62,6 +62,7 @@
 #include "gc/g1/heapRegionSet.inline.hpp"
 #include "gc/g1/vm_operations_g1.hpp"
 #include "gc/shared/adaptiveSizePolicy.hpp"
+#include "gc/shared/gcBehaviours.hpp"
 #include "gc/shared/gcHeapSummary.hpp"
 #include "gc/shared/gcId.hpp"
 #include "gc/shared/gcLocker.hpp"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/gcBehaviours.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "code/compiledMethod.hpp"
+#include "code/nmethod.hpp"
+#include "gc/shared/gcBehaviours.hpp"
+
+IsUnloadingBehaviour* IsUnloadingBehaviour::_current = NULL;
+
+class IsCompiledMethodUnloadingOopClosure: public OopClosure {
+  BoolObjectClosure *_cl;
+  bool _is_unloading;
+
+public:
+  IsCompiledMethodUnloadingOopClosure(BoolObjectClosure* cl)
+    : _cl(cl),
+      _is_unloading(false)
+  { }
+
+  virtual void do_oop(oop* p) {
+    if (_is_unloading) {
+      return;
+    }
+    oop obj = *p;
+    if (obj == NULL) {
+      return;
+    }
+    if (!_cl->do_object_b(obj)) {
+      _is_unloading = true;
+    }
+  }
+
+  virtual void do_oop(narrowOop* p) {
+    ShouldNotReachHere();
+  }
+
+  bool is_unloading() const {
+    return _is_unloading;
+  }
+};
+
+bool ClosureIsUnloadingBehaviour::is_unloading(CompiledMethod* cm) const {
+  if (cm->is_nmethod()) {
+    IsCompiledMethodUnloadingOopClosure cl(_cl);
+    static_cast<nmethod*>(cm)->oops_do(&cl);
+    return cl.is_unloading();
+  } else {
+    return false;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/shared/gcBehaviours.hpp	Fri Nov 02 08:33:59 2018 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_GC_SHARED_BEHAVIOURS_HPP
+#define SHARE_GC_SHARED_BEHAVIOURS_HPP
+
+#include "memory/iterator.hpp"
+#include "oops/oopsHierarchy.hpp"
+
+// This is the behaviour for checking if a CompiledMethod is unloading
+// or has unloaded due to having phantomly dead oops in it after a GC.
+class IsUnloadingBehaviour {
+  static IsUnloadingBehaviour* _current;
+
+public:
+  virtual bool is_unloading(CompiledMethod* cm) const = 0;
+  static IsUnloadingBehaviour* current() { return _current; }
+  static void set_current(IsUnloadingBehaviour* current) { _current = current; }
+};
+
+class ClosureIsUnloadingBehaviour: public IsUnloadingBehaviour {
+  BoolObjectClosure *const _cl;
+
+public:
+  ClosureIsUnloadingBehaviour(BoolObjectClosure* is_alive)
+    : _cl(is_alive)
+  { }
+
+  virtual bool is_unloading(CompiledMethod* cm) const;
+};
+
+#endif // SHARE_GC_SHARED_BEHAVIOURS_HPP
--- a/src/hotspot/share/gc/shared/parallelCleaning.cpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -27,6 +27,7 @@
 #include "classfile/stringTable.hpp"
 #include "code/codeCache.hpp"
 #include "gc/shared/parallelCleaning.hpp"
+#include "logging/log.hpp"
 #include "memory/resourceArea.hpp"
 #include "logging/log.hpp"
 
@@ -67,14 +68,11 @@
 }
 
 CodeCacheUnloadingTask::CodeCacheUnloadingTask(uint num_workers, BoolObjectClosure* is_alive, bool unloading_occurred) :
-      _is_alive(is_alive),
+      _unloading_scope(is_alive),
       _unloading_occurred(unloading_occurred),
       _num_workers(num_workers),
       _first_nmethod(NULL),
-      _claimed_nmethod(NULL),
-      _postponed_list(NULL),
-      _num_entered_barrier(0) {
-  CompiledMethod::increase_unloading_clock();
+      _claimed_nmethod(NULL) {
   // Get first alive nmethod
   CompiledMethodIterator iter = CompiledMethodIterator();
   if(iter.next_alive()) {
@@ -86,7 +84,6 @@
 CodeCacheUnloadingTask::~CodeCacheUnloadingTask() {
   CodeCache::verify_clean_inline_caches();
 
-  CodeCache::set_needs_cache_clean(false);
   guarantee(CodeCache::scavenge_root_nmethods() == NULL, "Must be");
 
   CodeCache::verify_icholder_relocations();
@@ -94,31 +91,6 @@
 
 Monitor* CodeCacheUnloadingTask::_lock = new Monitor(Mutex::leaf, "Code Cache Unload lock", false, Monitor::_safepoint_check_never);
 
-void CodeCacheUnloadingTask::add_to_postponed_list(CompiledMethod* nm) {
-  CompiledMethod* old;
-  do {
-    old = _postponed_list;
-    nm->set_unloading_next(old);
-  } while (Atomic::cmpxchg(nm, &_postponed_list, old) != old);
-}
-
-void CodeCacheUnloadingTask::clean_nmethod(CompiledMethod* nm) {
-  bool postponed = nm->do_unloading_parallel(_is_alive, _unloading_occurred);
-
-  if (postponed) {
-    // This nmethod referred to an nmethod that has not been cleaned/unloaded yet.
-    add_to_postponed_list(nm);
-  }
-
-  // Mark that this nmethod has been cleaned/unloaded.
-  // After this call, it will be safe to ask if this nmethod was unloaded or not.
-  nm->set_unloading_clock(CompiledMethod::global_unloading_clock());
-}
-
-void CodeCacheUnloadingTask::clean_nmethod_postponed(CompiledMethod* nm) {
-  nm->do_unloading_parallel_postponed();
-}
-
 void CodeCacheUnloadingTask::claim_nmethods(CompiledMethod** claimed_nmethods, int *num_claimed_nmethods) {
   CompiledMethod* first;
   CompiledMethodIterator last;
@@ -143,44 +115,10 @@
   } while (Atomic::cmpxchg(last.method(), &_claimed_nmethod, first) != first);
 }
 
-CompiledMethod* CodeCacheUnloadingTask::claim_postponed_nmethod() {
-  CompiledMethod* claim;
-  CompiledMethod* next;
-
-  do {
-    claim = _postponed_list;
-    if (claim == NULL) {
-      return NULL;
-    }
-
-    next = claim->unloading_next();
-
-  } while (Atomic::cmpxchg(next, &_postponed_list, claim) != claim);
-
-  return claim;
-}
-
-void CodeCacheUnloadingTask::barrier_mark(uint worker_id) {
-  MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag);
-  _num_entered_barrier++;
-  if (_num_entered_barrier == _num_workers) {
-    ml.notify_all();
-  }
-}
-
-void CodeCacheUnloadingTask::barrier_wait(uint worker_id) {
-  if (_num_entered_barrier < _num_workers) {
-    MonitorLockerEx ml(_lock, Mutex::_no_safepoint_check_flag);
-    while (_num_entered_barrier < _num_workers) {
-        ml.wait(Mutex::_no_safepoint_check_flag, 0, false);
-    }
-  }
-}
-
-void CodeCacheUnloadingTask::work_first_pass(uint worker_id) {
+void CodeCacheUnloadingTask::work(uint worker_id) {
   // The first nmethods is claimed by the first worker.
   if (worker_id == 0 && _first_nmethod != NULL) {
-    clean_nmethod(_first_nmethod);
+    _first_nmethod->do_unloading(_unloading_occurred);
     _first_nmethod = NULL;
   }
 
@@ -195,19 +133,11 @@
     }
 
     for (int i = 0; i < num_claimed_nmethods; i++) {
-      clean_nmethod(claimed_nmethods[i]);
+      claimed_nmethods[i]->do_unloading(_unloading_occurred);
     }
   }
 }
 
-void CodeCacheUnloadingTask::work_second_pass(uint worker_id) {
-  CompiledMethod* nm;
-  // Take care of postponed nmethods.
-  while ((nm = claim_postponed_nmethod()) != NULL) {
-    clean_nmethod_postponed(nm);
-  }
-}
-
 KlassCleaningTask::KlassCleaningTask() :
   _clean_klass_tree_claimed(0),
   _klass_iterator() {
@@ -257,21 +187,11 @@
 
 // The parallel work done by all worker threads.
 void ParallelCleaningTask::work(uint worker_id) {
-    // Do first pass of code cache cleaning.
-    _code_cache_task.work_first_pass(worker_id);
-
-    // Let the threads mark that the first pass is done.
-    _code_cache_task.barrier_mark(worker_id);
+  // Do first pass of code cache cleaning.
+  _code_cache_task.work(worker_id);
 
-    // Clean the Strings and Symbols.
-    _string_task.work(worker_id);
-
-    // Wait for all workers to finish the first code cache cleaning pass.
-    _code_cache_task.barrier_wait(worker_id);
-
-    // Do the second code cache cleaning work, which realize on
-    // the liveness information gathered during the first pass.
-    _code_cache_task.work_second_pass(worker_id);
+  // Clean the Strings and Symbols.
+  _string_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	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/gc/shared/parallelCleaning.hpp	Fri Nov 02 08:33:59 2018 +0100
@@ -56,46 +56,27 @@
 };
 
 class CodeCacheUnloadingTask {
-private:
   static Monitor* _lock;
 
-  BoolObjectClosure* const _is_alive;
-  const bool               _unloading_occurred;
-  const uint               _num_workers;
+  CodeCache::UnloadingScope _unloading_scope;
+  const bool                _unloading_occurred;
+  const uint                _num_workers;
 
   // Variables used to claim nmethods.
   CompiledMethod* _first_nmethod;
   CompiledMethod* volatile _claimed_nmethod;
 
-  // The list of nmethods that need to be processed by the second pass.
-  CompiledMethod* volatile _postponed_list;
-  volatile uint            _num_entered_barrier;
-
 public:
   CodeCacheUnloadingTask(uint num_workers, BoolObjectClosure* is_alive, bool unloading_occurred);
   ~CodeCacheUnloadingTask();
 
 private:
-  void add_to_postponed_list(CompiledMethod* nm);
-  void clean_nmethod(CompiledMethod* nm);
-  void clean_nmethod_postponed(CompiledMethod* nm);
+  static const int MaxClaimNmethods = 16;
+  void claim_nmethods(CompiledMethod** claimed_nmethods, int *num_claimed_nmethods);
 
-  static const int MaxClaimNmethods = 16;
-
-  void claim_nmethods(CompiledMethod** claimed_nmethods, int *num_claimed_nmethods);
-  CompiledMethod* claim_postponed_nmethod();
 public:
-  // Mark that we're done with the first pass of nmethod cleaning.
-  void barrier_mark(uint worker_id);
-
-  // See if we have to wait for the other workers to
-  // finish their first-pass nmethod cleaning work.
-  void barrier_wait(uint worker_id);
-
-  // Cleaning and unloading of nmethods. Some work has to be postponed
-  // to the second pass, when we know which nmethods survive.
-  void work_first_pass(uint worker_id);
-  void work_second_pass(uint worker_id);
+  // Cleaning and unloading of nmethods.
+  void work(uint worker_id);
 };
 
 
--- a/src/hotspot/share/runtime/sweeper.cpp	Thu Nov 01 14:57:26 2018 +0100
+++ b/src/hotspot/share/runtime/sweeper.cpp	Fri Nov 02 08:33:59 2018 +0100
@@ -702,7 +702,7 @@
     if (cm->is_alive()) {
       // Clean inline caches that point to zombie/non-entrant/unloaded nmethods
       CompiledICLocker ml(cm);
-      cm->cleanup_inline_caches();
+      cm->cleanup_inline_caches(false);
       SWEEP(cm);
     }
     return result;
@@ -748,7 +748,7 @@
     } else {
       // Still alive, clean up its inline caches
       CompiledICLocker ml(cm);
-      cm->cleanup_inline_caches();
+      cm->cleanup_inline_caches(false);
       SWEEP(cm);
     }
   } else if (cm->is_unloaded()) {
@@ -758,7 +758,7 @@
       // Clean ICs of unloaded nmethods as well because they may reference other
       // unloaded nmethods that may be flushed earlier in the sweeper cycle.
       CompiledICLocker ml(cm);
-      cm->cleanup_inline_caches();
+      cm->cleanup_inline_caches(false);
     }
     if (cm->is_osr_method()) {
       SWEEP(cm);
@@ -779,7 +779,7 @@
     }
     // Clean inline caches that point to zombie/non-entrant/unloaded nmethods
     CompiledICLocker ml(cm);
-    cm->cleanup_inline_caches();
+    cm->cleanup_inline_caches(false);
     SWEEP(cm);
   }
   return result;