--- old/test/compiler/whitebox/AllocationCodeBlobTest.java 2014-11-14 16:56:35.595861865 +0300 +++ new/test/compiler/whitebox/AllocationCodeBlobTest.java 2014-11-14 16:56:35.427861868 +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 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); - } - } - } } --- old/src/share/vm/prims/whitebox.cpp 2014-11-14 16:56:35.607861865 +0300 +++ new/src/share/vm/prims/whitebox.cpp 2014-11-14 16:56:35.419861868 +0300 @@ -44,6 +44,7 @@ #include "runtime/os.hpp" #include "runtime/vm_version.hpp" #include "runtime/sweeper.hpp" +#include "runtime/javaCalls.hpp" #include "utilities/array.hpp" #include "utilities/debug.hpp" @@ -742,7 +743,7 @@ mo.notify_all(); WB_END -void WhiteBox::force_sweep() { +void WhiteBox::sweeper_thread_entry(JavaThread* thread, TRAPS) { guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled"); { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); @@ -751,8 +752,41 @@ NMethodSweeper::possibly_sweep(); } -WB_ENTRY(void, WB_ForceNMethodSweep(JNIEnv* env, jobject o)) - WhiteBox::force_sweep(); +JavaThread* WhiteBox::create_sweeper_thread(TRAPS) { + Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_Thread(), true, CHECK_NULL); + instanceKlassHandle klass(THREAD, k); + instanceHandle thread_oop = klass->allocate_instance_handle(CHECK_NULL); + Handle string = java_lang_String::create_from_str("WB Sweeper thread", CHECK_NULL); + + CodeCacheSweeperThread* sweeper_thread = new CodeCacheSweeperThread(); + sweeper_thread->set_entry_point(&WhiteBox::sweeper_thread_entry); + JavaValue result(T_VOID); + JavaCalls::call_special(&result, thread_oop, + klass, + vmSymbols::object_initializer_name(), + vmSymbols::string_void_signature(), + string, + CHECK_NULL); + { + MutexLocker mu(Threads_lock, THREAD); + java_lang_Thread::set_thread(thread_oop(), sweeper_thread); + if (java_lang_Thread::is_daemon(JavaThread::current()->threadObj())) { + java_lang_Thread::set_daemon(thread_oop()); + } + sweeper_thread->set_threadObj(thread_oop()); + Threads::add(sweeper_thread); + Thread::start(sweeper_thread); + } + return sweeper_thread; +} + +WB_ENTRY(jobject, WB_ForceNMethodSweep(JNIEnv* env, jobject o)) + JavaThread* sweeper_thread = WhiteBox::create_sweeper_thread(JavaThread::current()); + if (sweeper_thread == NULL) { + return NULL; + } + jobject result = JNIHandles::make_local(env, sweeper_thread->threadObj()); + return result; WB_END WB_ENTRY(jboolean, WB_IsInStringTable(JNIEnv* env, jobject o, jstring javaString)) @@ -892,10 +926,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; } @@ -1190,7 +1224,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 }, --- old/src/share/vm/runtime/sweeper.cpp 2014-11-14 16:56:35.607861865 +0300 +++ new/src/share/vm/runtime/sweeper.cpp 2014-11-14 16:56:35.455861868 +0300 @@ -389,7 +389,6 @@ // 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. @@ -402,6 +401,7 @@ freed_memory += process_nmethod(nm); } _seen++; + handle_safepoint_request(); } } @@ -512,6 +512,7 @@ } int NMethodSweeper::process_nmethod(nmethod* nm) { + assert(nm != NULL, "sanity"); assert(!CodeCache_lock->owned_by_self(), "just checking"); int freed_memory = 0; --- old/src/share/vm/runtime/thread.hpp 2014-11-14 16:56:35.615861865 +0300 +++ new/src/share/vm/runtime/thread.hpp 2014-11-14 16:56:35.403861869 +0300 @@ -752,6 +752,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 --- old/src/share/vm/prims/whitebox.hpp 2014-11-14 16:56:35.691861864 +0300 +++ new/src/share/vm/prims/whitebox.hpp 2014-11-14 16:56:35.523861867 +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); --- old/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java 2014-11-14 16:56:35.715861863 +0300 +++ new/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java 2014-11-14 16:56:35.515861867 +0300 @@ -149,7 +149,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); // Intered strings --- /dev/null 2014-10-31 14:00:36.452833932 +0300 +++ new/test/compiler/whitebox/ForceNMethodSweepTest.java 2014-11-14 16:56:35.611861865 +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 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 2014-10-31 14:00:36.452833932 +0300 +++ new/test/testlibrary/com/oracle/java/testlibrary/InfiniteLoop.java 2014-11-14 16:56:35.619861865 +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); + } + } +}