mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8064669: compiler/whitebox/AllocationCodeBlobTest.java crashes / asserts
Reviewed-by: kvn, anoll
This commit is contained in:
parent
768b1d81a0
commit
3a9c14c70a
10 changed files with 308 additions and 80 deletions
|
@ -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 },
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
97
hotspot/test/compiler/whitebox/ForceNMethodSweepTest.java
Normal file
97
hotspot/test/compiler/whitebox/ForceNMethodSweepTest.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue