8064669: compiler/whitebox/AllocationCodeBlobTest.java crashes / asserts

Reviewed-by: kvn, anoll
This commit is contained in:
Igor Ignatyev 2014-12-02 12:36:03 +03:00
parent 768b1d81a0
commit 3a9c14c70a
10 changed files with 308 additions and 80 deletions

View file

@ -41,6 +41,7 @@
#include "runtime/interfaceSupport.hpp" #include "runtime/interfaceSupport.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "runtime/sweeper.hpp" #include "runtime/sweeper.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/thread.hpp" #include "runtime/thread.hpp"
#include "runtime/vm_version.hpp" #include "runtime/vm_version.hpp"
#include "utilities/array.hpp" #include "utilities/array.hpp"
@ -759,8 +760,8 @@ WB_ENTRY(void, WB_UnlockCompilation(JNIEnv* env, jobject o))
mo.notify_all(); mo.notify_all();
WB_END WB_END
void WhiteBox::force_sweep() { void WhiteBox::sweeper_thread_entry(JavaThread* thread, TRAPS) {
guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to enabled"); guarantee(WhiteBoxAPI, "internal testing API :: WhiteBox has to be enabled");
{ {
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
NMethodSweeper::_should_sweep = true; NMethodSweeper::_should_sweep = true;
@ -768,8 +769,37 @@ void WhiteBox::force_sweep() {
NMethodSweeper::possibly_sweep(); NMethodSweeper::possibly_sweep();
} }
WB_ENTRY(void, WB_ForceNMethodSweep(JNIEnv* env, jobject o)) JavaThread* WhiteBox::create_sweeper_thread(TRAPS) {
WhiteBox::force_sweep(); // 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_END
WB_ENTRY(jboolean, WB_IsInStringTable(JNIEnv* env, jobject o, jstring javaString)) WB_ENTRY(jboolean, WB_IsInStringTable(JNIEnv* env, jobject o, jstring javaString))
@ -819,12 +849,12 @@ WB_ENTRY(jstring, WB_GetCPUFeatures(JNIEnv* env, jobject o))
WB_END WB_END
int WhiteBox::get_blob_type(const CodeBlob* code) { 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(); return CodeCache::get_code_heap(code)->code_blob_type();
} }
CodeHeap* WhiteBox::get_code_heap(int 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); return CodeCache::get_code_heap(blob_type);
} }
@ -900,7 +930,7 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo
WB_END WB_END
CodeBlob* WhiteBox::allocate_code_blob(int size, int blob_type) { 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; BufferBlob* blob;
int full_size = CodeBlob::align_code_offset(sizeof(BufferBlob)); int full_size = CodeBlob::align_code_offset(sizeof(BufferBlob));
if (full_size < size) { if (full_size < size) {
@ -909,10 +939,10 @@ CodeBlob* WhiteBox::allocate_code_blob(int size, int blob_type) {
{ {
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
blob = (BufferBlob*) CodeCache::allocate(full_size, blob_type); blob = (BufferBlob*) CodeCache::allocate(full_size, blob_type);
::new (blob) BufferBlob("WB::DummyBlob", full_size);
} }
// Track memory usage statistic after releasing CodeCache_lock // Track memory usage statistic after releasing CodeCache_lock
MemoryService::track_code_cache_memory_usage(); MemoryService::track_code_cache_memory_usage();
::new (blob) BufferBlob("WB::DummyBlob", full_size);
return blob; return blob;
} }
@ -1221,7 +1251,7 @@ static JNINativeMethod methods[] = {
{CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures }, {CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures },
{CC"getNMethod", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;", {CC"getNMethod", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;",
(void*)&WB_GetNMethod }, (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"allocateCodeBlob", CC"(II)J", (void*)&WB_AllocateCodeBlob },
{CC"freeCodeBlob", CC"(J)V", (void*)&WB_FreeCodeBlob }, {CC"freeCodeBlob", CC"(J)V", (void*)&WB_FreeCodeBlob },
{CC"getCodeHeapEntries", CC"(I)[Ljava/lang/Object;",(void*)&WB_GetCodeHeapEntries }, {CC"getCodeHeapEntries", CC"(I)[Ljava/lang/Object;",(void*)&WB_GetCodeHeapEntries },

View file

@ -27,6 +27,7 @@
#include "prims/jni.h" #include "prims/jni.h"
#include "utilities/exceptions.hpp"
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "oops/oopsHierarchy.hpp" #include "oops/oopsHierarchy.hpp"
#include "oops/symbol.hpp" #include "oops/symbol.hpp"
@ -56,6 +57,7 @@
class CodeBlob; class CodeBlob;
class CodeHeap; class CodeHeap;
class JavaThread;
class WhiteBox : public AllStatic { class WhiteBox : public AllStatic {
private: private:
@ -68,7 +70,8 @@ class WhiteBox : public AllStatic {
Symbol* signature_symbol); Symbol* signature_symbol);
static const char* lookup_jstring(const char* field_name, oop object); static const char* lookup_jstring(const char* field_name, oop object);
static bool lookup_bool(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 int get_blob_type(const CodeBlob* code);
static CodeHeap* get_code_heap(int blob_type); static CodeHeap* get_code_heap(int blob_type);
static CodeBlob* allocate_code_blob(int blob_type, int size); static CodeBlob* allocate_code_blob(int blob_type, int size);

View file

@ -142,9 +142,6 @@ long NMethodSweeper::_total_nof_code_cache_sweeps = 0; // Total number o
long NMethodSweeper::_time_counter = 0; // Virtual time used to periodically invoke sweeper 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 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::_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 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: volatile int NMethodSweeper::_bytes_changed = 0; // Counts the total nmethod size if the nmethod changed from:
@ -161,6 +158,7 @@ Tickspan NMethodSweeper::_total_time_this_sweep; // Total time thi
Tickspan NMethodSweeper::_peak_sweep_time; // Peak time for a full sweep Tickspan NMethodSweeper::_peak_sweep_time; // Peak time for a full sweep
Tickspan NMethodSweeper::_peak_sweep_fraction_time; // Peak time sweeping one fraction 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 { class MarkActivationClosure: public CodeBlobClosure {
public: public:
@ -370,9 +368,10 @@ void NMethodSweeper::sweep_code_cache() {
ResourceMark rm; ResourceMark rm;
Ticks sweep_start_counter = Ticks::now(); Ticks sweep_start_counter = Ticks::now();
_flushed_count = 0; int flushed_count = 0;
_zombified_count = 0; int zombified_count = 0;
_marked_for_reclamation_count = 0; int marked_for_reclamation_count = 0;
int flushed_c2_count = 0;
if (PrintMethodFlushing && Verbose) { if (PrintMethodFlushing && Verbose) {
tty->print_cr("### Sweep at %d out of %d", _seen, CodeCache::nof_nmethods()); tty->print_cr("### Sweep at %d out of %d", _seen, CodeCache::nof_nmethods());
@ -386,10 +385,8 @@ void NMethodSweeper::sweep_code_cache() {
{ {
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
// The last invocation iterates until there are no more nmethods
while (!_current.end()) { while (!_current.end()) {
swept_count++; swept_count++;
handle_safepoint_request();
// Since we will give up the CodeCache_lock, always skip ahead // Since we will give up the CodeCache_lock, always skip ahead
// to the next nmethod. Other blobs can be deleted by other // to the next nmethod. Other blobs can be deleted by other
// threads but nmethods are only reclaimed by the sweeper. // threads but nmethods are only reclaimed by the sweeper.
@ -399,9 +396,32 @@ void NMethodSweeper::sweep_code_cache() {
// Now ready to process nmethod and give up CodeCache_lock // Now ready to process nmethod and give up CodeCache_lock
{ {
MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); 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++; _seen++;
handle_safepoint_request();
} }
} }
@ -409,21 +429,25 @@ void NMethodSweeper::sweep_code_cache() {
const Ticks sweep_end_counter = Ticks::now(); const Ticks sweep_end_counter = Ticks::now();
const Tickspan sweep_time = sweep_end_counter - sweep_start_counter; const Tickspan sweep_time = sweep_end_counter - sweep_start_counter;
_total_time_sweeping += sweep_time; {
_total_time_this_sweep += sweep_time; MutexLockerEx mu(_stat_lock, Mutex::_no_safepoint_check_flag);
_peak_sweep_fraction_time = MAX2(sweep_time, _peak_sweep_fraction_time); _total_time_sweeping += sweep_time;
_total_flushed_size += freed_memory; _total_time_this_sweep += sweep_time;
_total_nof_methods_reclaimed += _flushed_count; _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); EventSweepCodeCache event(UNTIMED);
if (event.should_commit()) { if (event.should_commit()) {
event.set_starttime(sweep_start_counter); event.set_starttime(sweep_start_counter);
event.set_endtime(sweep_end_counter); event.set_endtime(sweep_end_counter);
event.set_sweepIndex(_traversals); event.set_sweepIndex(_traversals);
event.set_sweptCount(swept_count); event.set_sweptCount(swept_count);
event.set_flushedCount(_flushed_count); event.set_flushedCount(flushed_count);
event.set_markedCount(_marked_for_reclamation_count); event.set_markedCount(marked_for_reclamation_count);
event.set_zombifiedCount(_zombified_count); event.set_zombifiedCount(zombified_count);
event.commit(); event.commit();
} }
@ -433,7 +457,6 @@ void NMethodSweeper::sweep_code_cache() {
} }
#endif #endif
_peak_sweep_time = MAX2(_peak_sweep_time, _total_time_this_sweep);
log_sweep("finished"); log_sweep("finished");
// Sweeper is the only case where memory is released, check here if it // Sweeper is the only case where memory is released, check here if it
@ -511,10 +534,11 @@ void NMethodSweeper::release_nmethod(nmethod* nm) {
nm->flush(); 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"); 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, // Make sure this nmethod doesn't get unloaded during the scan,
// since safepoints may happen during acquired below locks. // since safepoints may happen during acquired below locks.
NMethodMarker nmm(nm); NMethodMarker nmm(nm);
@ -529,7 +553,7 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
nm->cleanup_inline_caches(); nm->cleanup_inline_caches();
SWEEP(nm); SWEEP(nm);
} }
return freed_memory; return result;
} }
if (nm->is_zombie()) { if (nm->is_zombie()) {
@ -541,12 +565,9 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
if (PrintMethodFlushing && Verbose) { if (PrintMethodFlushing && Verbose) {
tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (marked for reclamation) being flushed", nm->compile_id(), nm); 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); release_nmethod(nm);
_flushed_count++; assert(result == None, "sanity");
result = Flushed;
} else { } else {
if (PrintMethodFlushing && Verbose) { if (PrintMethodFlushing && Verbose) {
tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (zombie) being marked for reclamation", nm->compile_id(), nm); tty->print_cr("### Nmethod %3d/" PTR_FORMAT " (zombie) being marked for reclamation", nm->compile_id(), nm);
@ -554,8 +575,9 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
nm->mark_for_reclamation(); nm->mark_for_reclamation();
// Keep track of code cache state change // Keep track of code cache state change
_bytes_changed += nm->total_size(); _bytes_changed += nm->total_size();
_marked_for_reclamation_count++;
SWEEP(nm); SWEEP(nm);
assert(result == None, "sanity");
result = MarkedForReclamation;
} }
} else if (nm->is_not_entrant()) { } else if (nm->is_not_entrant()) {
// If there are no current activations of this method on the // If there are no current activations of this method on the
@ -576,8 +598,9 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
} }
// Code cache state change is tracked in make_zombie() // Code cache state change is tracked in make_zombie()
nm->make_zombie(); nm->make_zombie();
_zombified_count++;
SWEEP(nm); SWEEP(nm);
assert(result == None, "sanity");
result = MadeZombie;
} }
assert(nm->is_zombie(), "nmethod must be zombie"); assert(nm->is_zombie(), "nmethod must be zombie");
} else { } else {
@ -594,17 +617,15 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
if (nm->is_osr_method()) { if (nm->is_osr_method()) {
SWEEP(nm); SWEEP(nm);
// No inline caches will ever point to osr methods, so we can just remove it // 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); release_nmethod(nm);
_flushed_count++; assert(result == None, "sanity");
result = Flushed;
} else { } else {
// Code cache state change is tracked in make_zombie() // Code cache state change is tracked in make_zombie()
nm->make_zombie(); nm->make_zombie();
_zombified_count++;
SWEEP(nm); SWEEP(nm);
assert(result == None, "sanity");
result = MadeZombie;
} }
} else { } else {
possibly_flush(nm); possibly_flush(nm);
@ -613,7 +634,7 @@ int NMethodSweeper::process_nmethod(nmethod* nm) {
nm->cleanup_inline_caches(); nm->cleanup_inline_caches();
SWEEP(nm); SWEEP(nm);
} }
return freed_memory; return result;
} }

View file

@ -56,15 +56,18 @@ class WhiteBox;
class NMethodSweeper : public AllStatic { class NMethodSweeper : public AllStatic {
friend class WhiteBox; friend class WhiteBox;
private: private:
enum MethodStateChange {
None,
MadeZombie,
MarkedForReclamation,
Flushed
};
static long _traversals; // Stack scan count, also sweep ID. 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 _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 _time_counter; // Virtual time used to periodically invoke sweeper
static long _last_sweep; // Value of _time_counter when the last sweep happened static long _last_sweep; // Value of _time_counter when the last sweep happened
static NMethodIterator _current; // Current nmethod static NMethodIterator _current; // Current nmethod
static int _seen; // Nof. nmethod we have currently processed in current pass of CodeCache 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 int _sweep_started; // Flag to control conc sweeper
static volatile bool _should_sweep; // Indicates if we should invoke the sweeper static volatile bool _should_sweep; // Indicates if we should invoke the sweeper
@ -83,8 +86,10 @@ class NMethodSweeper : public AllStatic {
static Tickspan _peak_sweep_time; // Peak time for a full sweep static Tickspan _peak_sweep_time; // Peak time for a full sweep
static Tickspan _peak_sweep_fraction_time; // Peak time sweeping one fraction static Tickspan _peak_sweep_fraction_time; // Peak time sweeping one fraction
static int process_nmethod(nmethod *nm); static Monitor* _stat_lock;
static void release_nmethod(nmethod* nm);
static MethodStateChange process_nmethod(nmethod *nm);
static void release_nmethod(nmethod* nm);
static void init_sweeper_log() NOT_DEBUG_RETURN; static void init_sweeper_log() NOT_DEBUG_RETURN;
static bool wait_for_stack_scanning(); static bool wait_for_stack_scanning();

View file

@ -1076,7 +1076,7 @@ static void reset_vm_info_property(TRAPS) {
} }
void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name, void JavaThread::allocate_threadObj(Handle thread_group, const char* thread_name,
bool daemon, TRAPS) { bool daemon, TRAPS) {
assert(thread_group.not_null(), "thread group should be specified"); assert(thread_group.not_null(), "thread group should be specified");
assert(threadObj() == NULL, "should only create Java thread object once"); assert(threadObj() == NULL, "should only create Java thread object once");
@ -1123,8 +1123,8 @@ void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name,
return; return;
} }
KlassHandle group(this, SystemDictionary::ThreadGroup_klass()); KlassHandle group(THREAD, SystemDictionary::ThreadGroup_klass());
Handle threadObj(this, this->threadObj()); Handle threadObj(THREAD, this->threadObj());
JavaCalls::call_special(&result, JavaCalls::call_special(&result,
thread_group, thread_group,
@ -1133,8 +1133,6 @@ void JavaThread::allocate_threadObj(Handle thread_group, char* thread_name,
vmSymbols::thread_void_signature(), vmSymbols::thread_void_signature(),
threadObj, // Arg 1 threadObj, // Arg 1
THREAD); THREAD);
} }
// NamedThread -- non-JavaThread subclasses with multiple // NamedThread -- non-JavaThread subclasses with multiple

View file

@ -749,6 +749,7 @@ typedef void (*ThreadFunction)(JavaThread*, TRAPS);
class JavaThread: public Thread { class JavaThread: public Thread {
friend class VMStructs; friend class VMStructs;
friend class WhiteBox;
private: private:
JavaThread* _next; // The next thread in the Threads list JavaThread* _next; // The next thread in the Threads list
oop _threadObj; // The Java level thread object oop _threadObj; // The Java level thread object
@ -1000,7 +1001,7 @@ class JavaThread: public Thread {
ThreadFunction entry_point() const { return _entry_point; } ThreadFunction entry_point() const { return _entry_point; }
// Allocates a new Java level thread object for this thread. thread_name may be NULL. // 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 // Last frame anchor routines

View file

@ -29,10 +29,11 @@ import java.util.ArrayList;
import sun.hotspot.WhiteBox; import sun.hotspot.WhiteBox;
import sun.hotspot.code.BlobType; import sun.hotspot.code.BlobType;
import com.oracle.java.testlibrary.Asserts; import com.oracle.java.testlibrary.Asserts;
import com.oracle.java.testlibrary.InfiniteLoop;
/* /*
* @test AllocationCodeBlobTest * @test AllocationCodeBlobTest
* @bug 8059624 * @bug 8059624 8064669
* @library /testlibrary /testlibrary/whitebox * @library /testlibrary /testlibrary/whitebox
* @build AllocationCodeBlobTest * @build AllocationCodeBlobTest
* @run main ClassFileInstaller sun.hotspot.WhiteBox * @run main ClassFileInstaller sun.hotspot.WhiteBox
@ -53,11 +54,32 @@ public class AllocationCodeBlobTest {
public static void main(String[] args) { public static void main(String[] args) {
// check that Sweeper handels dummy blobs correctly // 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(); EnumSet<BlobType> blobTypes = BlobType.getAvailable();
for (BlobType type : blobTypes) { for (BlobType type : blobTypes) {
new AllocationCodeBlobTest(type).test(); 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; private final BlobType type;
@ -105,24 +127,4 @@ public class AllocationCodeBlobTest {
private long getUsage() { private long getUsage() {
return bean.getUsage().getUsed(); 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);
}
}
}
} }

View file

@ -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();
}
}
}

View file

@ -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);
}
}
}

View file

@ -153,7 +153,14 @@ public class WhiteBox {
public native Object[] getNMethod(Executable method, boolean isOsr); public native Object[] getNMethod(Executable method, boolean isOsr);
public native long allocateCodeBlob(int size, int type); public native long allocateCodeBlob(int size, int type);
public native void freeCodeBlob(long addr); 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 Object[] getCodeHeapEntries(int type);
public native int getCompilationActivityMode(); public native int getCompilationActivityMode();
public native Object[] getCodeBlob(long addr); public native Object[] getCodeBlob(long addr);