changeset 7499:b35313b1dff1

8064669: compiler/whitebox/AllocationCodeBlobTest.java crashes / asserts Reviewed-by: kvn, anoll
author iignatyev
date Tue, 02 Dec 2014 12:36:03 +0300
parents adbc6a1e1ce7
children 9cd872c1370e
files src/share/vm/prims/whitebox.cpp src/share/vm/prims/whitebox.hpp src/share/vm/runtime/sweeper.cpp src/share/vm/runtime/sweeper.hpp src/share/vm/runtime/thread.cpp src/share/vm/runtime/thread.hpp test/compiler/whitebox/AllocationCodeBlobTest.java test/compiler/whitebox/ForceNMethodSweepTest.java test/testlibrary/com/oracle/java/testlibrary/InfiniteLoop.java test/testlibrary/whitebox/sun/hotspot/WhiteBox.java
diffstat 10 files changed, 308 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/vm/prims/whitebox.cpp	Mon Dec 01 22:41:16 2014 +0300
+++ b/src/share/vm/prims/whitebox.cpp	Tue Dec 02 12:36:03 2014 +0300
@@ -41,6 +41,7 @@
 #include "runtime/interfaceSupport.hpp"
 #include "runtime/os.hpp"
 #include "runtime/sweeper.hpp"
+#include "runtime/javaCalls.hpp"
 #include "runtime/thread.hpp"
 #include "runtime/vm_version.hpp"
 #include "utilities/array.hpp"
@@ -759,8 +760,8 @@
   mo.notify_all();
 WB_END
 
-void WhiteBox::force_sweep() {
-  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled");
+void WhiteBox::sweeper_thread_entry(JavaThread* thread, TRAPS) {
+  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
   {
     MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
     NMethodSweeper::_should_sweep = true;
@@ -768,8 +769,37 @@
   NMethodSweeper::possibly_sweep();
 }
 
-WB_ENTRY(void, WB_ForceNMethodSweep(JNIEnv* env, jobject o))
-  WhiteBox::force_sweep();
+JavaThread* WhiteBox::create_sweeper_thread(TRAPS) {
+  // create sweeper thread w/ custom entry -- one iteration instead of loop
+  CodeCacheSweeperThread* sweeper_thread = new CodeCacheSweeperThread();
+  sweeper_thread->set_entry_point(&WhiteBox::sweeper_thread_entry);
+
+  // create j.l.Thread object and associate it w/ sweeper thread
+  {
+    // inherit deamon property from current thread
+    bool is_daemon = java_lang_Thread::is_daemon(JavaThread::current()->threadObj());
+
+    HandleMark hm(THREAD);
+    Handle thread_group(THREAD, Universe::system_thread_group());
+    const char* name = "WB Sweeper thread";
+    sweeper_thread->allocate_threadObj(thread_group, name, is_daemon, THREAD);
+  }
+
+  {
+    MutexLocker mu(Threads_lock, THREAD);
+    Threads::add(sweeper_thread);
+  }
+  return sweeper_thread;
+}
+
+WB_ENTRY(jobject, WB_ForceNMethodSweep(JNIEnv* env, jobject o))
+  JavaThread* sweeper_thread = WhiteBox::create_sweeper_thread(Thread::current());
+  if (sweeper_thread == NULL) {
+    return NULL;
+  }
+  jobject result = JNIHandles::make_local(env, sweeper_thread->threadObj());
+  Thread::start(sweeper_thread);
+  return result;
 WB_END
 
 WB_ENTRY(jboolean, WB_IsInStringTable(JNIEnv* env, jobject o, jstring javaString))
@@ -819,12 +849,12 @@
 WB_END
 
 int WhiteBox::get_blob_type(const CodeBlob* code) {
-  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled");
+  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
   return CodeCache::get_code_heap(code)->code_blob_type();
 }
 
 CodeHeap* WhiteBox::get_code_heap(int blob_type) {
-  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled");
+  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
   return CodeCache::get_code_heap(blob_type);
 }
 
@@ -900,7 +930,7 @@
 WB_END
 
 CodeBlob* WhiteBox::allocate_code_blob(int size, int blob_type) {
-  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled");
+  guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
   BufferBlob* blob;
   int full_size = CodeBlob::align_code_offset(sizeof(BufferBlob));
   if (full_size < size) {
@@ -909,10 +939,10 @@
   {
     MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
     blob = (BufferBlob*) CodeCache::allocate(full_size, blob_type);
+    ::new (blob) BufferBlob("WB::DummyBlob", full_size);
   }
   // Track memory usage statistic after releasing CodeCache_lock
   MemoryService::track_code_cache_memory_usage();
-  ::new (blob) BufferBlob("WB::DummyBlob", full_size);
   return blob;
 }
 
@@ -1221,7 +1251,7 @@
   {CC"getCPUFeatures",     CC"()Ljava/lang/String;",  (void*)&WB_GetCPUFeatures     },
   {CC"getNMethod",         CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;",
                                                       (void*)&WB_GetNMethod         },
-  {CC"forceNMethodSweep",  CC"()V",                   (void*)&WB_ForceNMethodSweep  },
+  {CC"forceNMethodSweep0", CC"()Ljava/lang/Thread;",  (void*)&WB_ForceNMethodSweep  },
   {CC"allocateCodeBlob",   CC"(II)J",                 (void*)&WB_AllocateCodeBlob   },
   {CC"freeCodeBlob",       CC"(J)V",                  (void*)&WB_FreeCodeBlob       },
   {CC"getCodeHeapEntries", CC"(I)[Ljava/lang/Object;",(void*)&WB_GetCodeHeapEntries },
--- a/src/share/vm/prims/whitebox.hpp	Mon Dec 01 22:41:16 2014 +0300
+++ b/src/share/vm/prims/whitebox.hpp	Tue Dec 02 12:36:03 2014 +0300
@@ -27,6 +27,7 @@
 
 #include "prims/jni.h"
 
+#include "utilities/exceptions.hpp"
 #include "memory/allocation.hpp"
 #include "oops/oopsHierarchy.hpp"
 #include "oops/symbol.hpp"
@@ -56,6 +57,7 @@
 
 class CodeBlob;
 class CodeHeap;
+class JavaThread;
 
 class WhiteBox : public AllStatic {
  private:
@@ -68,7 +70,8 @@
     Symbol* signature_symbol);
   static const char* lookup_jstring(const char* field_name, oop object);
   static bool lookup_bool(const char* field_name, oop object);
-  static void force_sweep();
+  static void sweeper_thread_entry(JavaThread* thread, TRAPS);
+  static JavaThread* create_sweeper_thread(TRAPS);
   static int get_blob_type(const CodeBlob* code);
   static CodeHeap* get_code_heap(int blob_type);
   static CodeBlob* allocate_code_blob(int blob_type, int size);
--- a/src/share/vm/runtime/sweeper.cpp	Mon Dec 01 22:41:16 2014 +0300
+++ b/src/share/vm/runtime/sweeper.cpp	Tue Dec 02 12:36:03 2014 +0300
@@ -142,9 +142,6 @@
 long     NMethodSweeper::_time_counter                 = 0;    // Virtual time used to periodically invoke sweeper
 long     NMethodSweeper::_last_sweep                   = 0;    // Value of _time_counter when the last sweep happened
 int      NMethodSweeper::_seen                         = 0;    // Nof. nmethod we have currently processed in current pass of CodeCache
-int      NMethodSweeper::_flushed_count                = 0;    // Nof. nmethods flushed in current sweep
-int      NMethodSweeper::_zombified_count              = 0;    // Nof. nmethods made zombie in current sweep
-int      NMethodSweeper::_marked_for_reclamation_count = 0;    // Nof. nmethods marked for reclaim in current sweep
 
 volatile bool NMethodSweeper::_should_sweep            = true; // Indicates if we should invoke the sweeper
 volatile int  NMethodSweeper::_bytes_changed           = 0;    // Counts the total nmethod size if the nmethod changed from:
@@ -161,6 +158,7 @@
 Tickspan NMethodSweeper::_peak_sweep_time;                     // Peak time for a full sweep
 Tickspan NMethodSweeper::_peak_sweep_fraction_time;            // Peak time sweeping one fraction
 
+Monitor* NMethodSweeper::_stat_lock = new Monitor(Mutex::special, "Sweeper::Statistics", true);
 
 class MarkActivationClosure: public CodeBlobClosure {
 public:
@@ -370,9 +368,10 @@
   ResourceMark rm;
   Ticks sweep_start_counter = Ticks::now();
 
-  _flushed_count                = 0;
-  _zombified_count              = 0;
-  _marked_for_reclamation_count = 0;
+  int flushed_count                = 0;
+  int zombified_count              = 0;
+  int marked_for_reclamation_count = 0;
+  int flushed_c2_count     = 0;
 
   if (PrintMethodFlushing && Verbose) {
     tty->print_cr("### Sweep at %d out of %d", _seen, CodeCache::nof_nmethods());
@@ -386,10 +385,8 @@
   {
     MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
 
-    // The last invocation iterates until there are no more nmethods
     while (!_current.end()) {
       swept_count++;
-      handle_safepoint_request();
       // Since we will give up the CodeCache_lock, always skip ahead
       // to the next nmethod.  Other blobs can be deleted by other
       // threads but nmethods are only reclaimed by the sweeper.
@@ -399,9 +396,32 @@
       // Now ready to process nmethod and give up CodeCache_lock
       {
         MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
-        freed_memory += process_nmethod(nm);
+        int size = nm->total_size();
+        bool is_c2_method = nm->is_compiled_by_c2();
+
+        MethodStateChange type = process_nmethod(nm);
+        switch (type) {
+          case Flushed:
+            freed_memory += size;
+            ++flushed_count;
+            if (is_c2_method) {
+              ++flushed_c2_count;
+            }
+            break;
+          case MarkedForReclamation:
+            ++marked_for_reclamation_count;
+            break;
+          case MadeZombie:
+            ++zombified_count;
+            break;
+          case None:
+            break;
+          default:
+           ShouldNotReachHere();
+        }
       }
       _seen++;
+      handle_safepoint_request();
     }
   }
 
@@ -409,21 +429,25 @@
 
   const Ticks sweep_end_counter = Ticks::now();
   const Tickspan sweep_time = sweep_end_counter - sweep_start_counter;
-  _total_time_sweeping  += sweep_time;
-  _total_time_this_sweep += sweep_time;
-  _peak_sweep_fraction_time = MAX2(sweep_time, _peak_sweep_fraction_time);
-  _total_flushed_size += freed_memory;
-  _total_nof_methods_reclaimed += _flushed_count;
-
+  {
+    MutexLockerEx mu(_stat_lock, Mutex::_no_safepoint_check_flag);
+    _total_time_sweeping  += sweep_time;
+    _total_time_this_sweep += sweep_time;
+    _peak_sweep_fraction_time = MAX2(sweep_time, _peak_sweep_fraction_time);
+    _total_flushed_size += freed_memory;
+    _total_nof_methods_reclaimed += flushed_count;
+    _total_nof_c2_methods_reclaimed += flushed_c2_count;
+    _peak_sweep_time = MAX2(_peak_sweep_time, _total_time_this_sweep);
+  }
   EventSweepCodeCache event(UNTIMED);
   if (event.should_commit()) {
     event.set_starttime(sweep_start_counter);
     event.set_endtime(sweep_end_counter);
     event.set_sweepIndex(_traversals);
     event.set_sweptCount(swept_count);
-    event.set_flushedCount(_flushed_count);
-    event.set_markedCount(_marked_for_reclamation_count);
-    event.set_zombifiedCount(_zombified_count);
+    event.set_flushedCount(flushed_count);
+    event.set_markedCount(marked_for_reclamation_count);
+    event.set_zombifiedCount(zombified_count);
     event.commit();
   }
 
@@ -433,7 +457,6 @@
   }
 #endif
 
-  _peak_sweep_time = MAX2(_peak_sweep_time, _total_time_this_sweep);
   log_sweep("finished");
 
   // Sweeper is the only case where memory is released, check here if it
@@ -511,10 +534,11 @@
   nm->flush();
 }
 
-int NMethodSweeper::process_nmethod(nmethod* nm) {
+NMethodSweeper::MethodStateChange NMethodSweeper::process_nmethod(nmethod* nm) {
+  assert(nm != NULL, "sanity");
   assert(!CodeCache_lock->owned_by_self(), "just checking");
 
-  int freed_memory = 0;
+  MethodStateChange result = None;
   // Make sure this nmethod doesn't get unloaded during the scan,
   // since safepoints may happen during acquired below locks.
   NMethodMarker nmm(nm);
@@ -529,7 +553,7 @@
       nm->cleanup_inline_caches();
       SWEEP(nm);
     }
-    return freed_memory;
+    return result;
   }
 
   if (nm->is_zombie()) {
@@ -541,12 +565,9 @@
       if (PrintMethodFlushing && Verbose) {
         tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (marked for reclamation) being flushed", nm->compile_id(), nm);
       }
-      freed_memory = nm->total_size();
-      if (nm->is_compiled_by_c2()) {
-        _total_nof_c2_methods_reclaimed++;
-      }
       release_nmethod(nm);
-      _flushed_count++;
+      assert(result == None, "sanity");
+      result = Flushed;
     } else {
       if (PrintMethodFlushing && Verbose) {
         tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (zombie) being marked for reclamation", nm->compile_id(), nm);
@@ -554,8 +575,9 @@
       nm->mark_for_reclamation();
       // Keep track of code cache state change
       _bytes_changed += nm->total_size();
-      _marked_for_reclamation_count++;
       SWEEP(nm);
+      assert(result == None, "sanity");
+      result = MarkedForReclamation;
     }
   } else if (nm->is_not_entrant()) {
     // If there are no current activations of this method on the
@@ -576,8 +598,9 @@
         }
         // Code cache state change is tracked in make_zombie()
         nm->make_zombie();
-        _zombified_count++;
         SWEEP(nm);
+        assert(result == None, "sanity");
+        result = MadeZombie;
       }
       assert(nm->is_zombie(), "nmethod must be zombie");
     } else {
@@ -594,17 +617,15 @@
     if (nm->is_osr_method()) {
       SWEEP(nm);
       // No inline caches will ever point to osr methods, so we can just remove it
-      freed_memory = nm->total_size();
-      if (nm->is_compiled_by_c2()) {
-        _total_nof_c2_methods_reclaimed++;
-      }
       release_nmethod(nm);
-      _flushed_count++;
+      assert(result == None, "sanity");
+      result = Flushed;
     } else {
       // Code cache state change is tracked in make_zombie()
       nm->make_zombie();
-      _zombified_count++;
       SWEEP(nm);
+      assert(result == None, "sanity");
+      result = MadeZombie;
     }
   } else {
     possibly_flush(nm);
@@ -613,7 +634,7 @@
     nm->cleanup_inline_caches();
     SWEEP(nm);
   }
-  return freed_memory;
+  return result;
 }
 
 
--- a/src/share/vm/runtime/sweeper.hpp	Mon Dec 01 22:41:16 2014 +0300
+++ b/src/share/vm/runtime/sweeper.hpp	Tue Dec 02 12:36:03 2014 +0300
@@ -56,15 +56,18 @@
 class NMethodSweeper : public AllStatic {
   friend class WhiteBox;
  private:
+  enum MethodStateChange {
+    None,
+    MadeZombie,
+    MarkedForReclamation,
+    Flushed
+  };
   static long      _traversals;                   // Stack scan count, also sweep ID.
   static long      _total_nof_code_cache_sweeps;  // Total number of full sweeps of the code cache
   static long      _time_counter;                 // Virtual time used to periodically invoke sweeper
   static long      _last_sweep;                   // Value of _time_counter when the last sweep happened
   static NMethodIterator _current;                // Current nmethod
   static int       _seen;                         // Nof. nmethod we have currently processed in current pass of CodeCache
-  static int       _flushed_count;                // Nof. nmethods flushed in current sweep
-  static int       _zombified_count;              // Nof. nmethods made zombie in current sweep
-  static int       _marked_for_reclamation_count; // Nof. nmethods marked for reclaim in current sweep
 
   static volatile int  _sweep_started;            // Flag to control conc sweeper
   static volatile bool _should_sweep;             // Indicates if we should invoke the sweeper
@@ -83,8 +86,10 @@
   static Tickspan  _peak_sweep_time;              // Peak time for a full sweep
   static Tickspan  _peak_sweep_fraction_time;     // Peak time sweeping one fraction
 
-  static int  process_nmethod(nmethod *nm);
-  static void release_nmethod(nmethod* nm);
+  static Monitor*  _stat_lock;
+
+  static MethodStateChange process_nmethod(nmethod *nm);
+  static void              release_nmethod(nmethod* nm);
 
   static void init_sweeper_log() NOT_DEBUG_RETURN;
   static bool wait_for_stack_scanning();
--- a/src/share/vm/runtime/thread.cpp	Mon Dec 01 22:41:16 2014 +0300
+++ b/src/share/vm/runtime/thread.cpp	Tue Dec 02 12:36:03 2014 +0300
@@ -1076,7 +1076,7 @@
 }
 
 
-void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name,
+void JavaThread::allocate_threadObj(Handle thread_group, const char* thread_name,
                                     bool daemon, TRAPS) {
   assert(thread_group.not_null(), "thread group should be specified");
   assert(threadObj() == NULL, "should only create Java thread object once");
@@ -1123,8 +1123,8 @@
     return;
   }
 
-  KlassHandle group(this, SystemDictionary::ThreadGroup_klass());
-  Handle threadObj(this, this->threadObj());
+  KlassHandle group(THREAD, SystemDictionary::ThreadGroup_klass());
+  Handle threadObj(THREAD, this->threadObj());
 
   JavaCalls::call_special(&result,
                           thread_group,
@@ -1133,8 +1133,6 @@
                           vmSymbols::thread_void_signature(),
                           threadObj,          // Arg 1
                           THREAD);
-
-
 }
 
 // NamedThread --  non-JavaThread subclasses with multiple
--- a/src/share/vm/runtime/thread.hpp	Mon Dec 01 22:41:16 2014 +0300
+++ b/src/share/vm/runtime/thread.hpp	Tue Dec 02 12:36:03 2014 +0300
@@ -749,6 +749,7 @@
 
 class JavaThread: public Thread {
   friend class VMStructs;
+  friend class WhiteBox;
  private:
   JavaThread*    _next;                          // The next thread in the Threads list
   oop            _threadObj;                     // The Java level thread object
@@ -1000,7 +1001,7 @@
   ThreadFunction entry_point() const             { return _entry_point; }
 
   // Allocates a new Java level thread object for this thread. thread_name may be NULL.
-  void allocate_threadObj(Handle thread_group, char* thread_name, bool daemon, TRAPS);
+  void allocate_threadObj(Handle thread_group, const char* thread_name, bool daemon, TRAPS);
 
   // Last frame anchor routines
 
--- a/test/compiler/whitebox/AllocationCodeBlobTest.java	Mon Dec 01 22:41:16 2014 +0300
+++ b/test/compiler/whitebox/AllocationCodeBlobTest.java	Tue Dec 02 12:36:03 2014 +0300
@@ -29,10 +29,11 @@
 import sun.hotspot.WhiteBox;
 import sun.hotspot.code.BlobType;
 import com.oracle.java.testlibrary.Asserts;
+import com.oracle.java.testlibrary.InfiniteLoop;
 
 /*
  * @test AllocationCodeBlobTest
- * @bug 8059624
+ * @bug 8059624 8064669
  * @library /testlibrary /testlibrary/whitebox
  * @build AllocationCodeBlobTest
  * @run main ClassFileInstaller sun.hotspot.WhiteBox
@@ -53,11 +54,32 @@
 
     public static void main(String[] args) {
         // check that Sweeper handels dummy blobs correctly
-        new ForcedSweeper(500).start();
+        Thread t = new Thread(
+                new InfiniteLoop(WHITE_BOX::forceNMethodSweep, 1L),
+                "ForcedSweeper");
+        t.setDaemon(true);
+        System.out.println("Starting " + t.getName());
+        t.start();
+
         EnumSet<BlobType> blobTypes = BlobType.getAvailable();
         for (BlobType type : blobTypes) {
             new AllocationCodeBlobTest(type).test();
         }
+
+        // check that deoptimization works well w/ dummy blobs
+        t = new Thread(
+                new InfiniteLoop(WHITE_BOX::deoptimizeAll, 1L),
+                "Deoptimize Thread");
+        t.setDaemon(true);
+        System.out.println("Starting " + t.getName());
+        t.start();
+
+        for (int i = 0; i < 10_000; ++i) {
+            for (BlobType type : blobTypes) {
+                long addr = WHITE_BOX.allocateCodeBlob(SIZE, type.id);
+            }
+        }
+
     }
 
     private final BlobType type;
@@ -105,24 +127,4 @@
     private long getUsage() {
         return bean.getUsage().getUsed();
     }
-
-    private static class ForcedSweeper extends Thread {
-        private final int millis;
-        public ForcedSweeper(int millis) {
-            super("ForcedSweeper");
-            setDaemon(true);
-            this.millis = millis;
-        }
-        public void run() {
-            try {
-                while (true) {
-                    WHITE_BOX.forceNMethodSweep();
-                    Thread.sleep(millis);
-                }
-            } catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
-                throw new Error(e);
-            }
-        }
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/compiler/whitebox/ForceNMethodSweepTest.java	Tue Dec 02 12:36:03 2014 +0300
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.reflect.Method;
+import java.util.EnumSet;
+
+import sun.hotspot.WhiteBox;
+import sun.hotspot.code.BlobType;
+
+import com.oracle.java.testlibrary.Asserts;
+import com.oracle.java.testlibrary.InfiniteLoop;
+
+/*
+ * @test
+ * @bug 8059624 8064669
+ * @library /testlibrary /testlibrary/whitebox
+ * @build ForceNMethodSweepTest
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *                              sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
+ *                   -XX:-TieredCompilation -XX:+WhiteBoxAPI
+ *                   -XX:CompileCommand=compileonly,SimpleTestCase$Helper::*
+ *                   ForceNMethodSweepTest
+ * @summary testing of WB::forceNMethodSweep
+ */
+public class ForceNMethodSweepTest extends CompilerWhiteBoxTest {
+    public static void main(String[] args) throws Exception {
+        CompilerWhiteBoxTest.main(ForceNMethodSweepTest::new, args);
+    }
+    private final EnumSet<BlobType> blobTypes;
+    private ForceNMethodSweepTest(TestCase testCase) {
+        super(testCase);
+        // to prevent inlining of #method
+        WHITE_BOX.testSetDontInlineMethod(method, true);
+        blobTypes = BlobType.getAvailable();
+    }
+
+    @Override
+    protected void test() throws Exception {
+        checkNotCompiled();
+        guaranteedSweep();
+        int usage = getTotalUsage();
+
+        compile();
+        checkCompiled();
+        int afterCompilation = getTotalUsage();
+        Asserts.assertGT(afterCompilation, usage,
+                "compilation should increase usage");
+
+        guaranteedSweep();
+        int afterSweep = getTotalUsage();
+        Asserts.assertLTE(afterSweep, afterCompilation,
+                "sweep shouldn't increase usage");
+
+        deoptimize();
+        guaranteedSweep();
+        int afterDeoptAndSweep = getTotalUsage();
+        Asserts.assertLT(afterDeoptAndSweep, afterSweep,
+                "sweep after deoptimization should decrease usage");
+     }
+
+    private int getTotalUsage() {
+        int usage = 0;
+        for (BlobType type : blobTypes) {
+           usage += type.getMemoryPool().getUsage().getUsed();
+        }
+        return usage;
+    }
+    private void guaranteedSweep() {
+        // not entrant -> ++stack_traversal_mark -> zombie -> reclamation -> flushed
+        for (int i = 0; i < 5; ++i) {
+            WHITE_BOX.fullGC();
+            WHITE_BOX.forceNMethodSweep();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testlibrary/com/oracle/java/testlibrary/InfiniteLoop.java	Tue Dec 02 12:36:03 2014 +0300
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, 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 com.oracle.java.testlibrary;
+
+import java.util.Objects;
+
+/**
+ * Class which runs another Runnable in infinite loop with certain pauses
+ * between cycles.
+ */
+public class InfiniteLoop implements Runnable {
+    private final Runnable target;
+    private final long mills;
+
+
+    /**
+     * @param target a target to run in a loop
+     * @param mills  the length of pause time in milliseconds
+     * @throws NullPointerException if target is null
+     * @throws IllegalArgumentException if the value of millis is negative
+     */
+    public InfiniteLoop(Runnable target, long mills) {
+        Objects.requireNonNull(target);
+        if (mills < 0) {
+            throw new IllegalArgumentException("mills < 0");
+        }
+        this.target = target;
+        this.mills = mills;
+    }
+
+    @Override
+    public void run() {
+        try {
+            while (true) {
+                target.run();
+                Thread.sleep(mills);
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new Error(e);
+        }
+    }
+}
--- a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java	Mon Dec 01 22:41:16 2014 +0300
+++ b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java	Tue Dec 02 12:36:03 2014 +0300
@@ -153,7 +153,14 @@
   public native Object[] getNMethod(Executable method, boolean isOsr);
   public native long    allocateCodeBlob(int size, int type);
   public native void    freeCodeBlob(long addr);
-  public native void    forceNMethodSweep();
+  public        void    forceNMethodSweep() {
+    try {
+        forceNMethodSweep0().join();
+    } catch (InterruptedException e) {
+        Thread.currentThread().interrupt();
+    }
+  }
+  public native Thread  forceNMethodSweep0();
   public native Object[] getCodeHeapEntries(int type);
   public native int     getCompilationActivityMode();
   public native Object[] getCodeBlob(long addr);