提交 422e8094 编写于 作者: N never

7129164: JNI Get/ReleasePrimitiveArrayCritical doesn't scale

Reviewed-by: kvn, iveresov, dholmes
上级 756b4e6e
/* /*
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -31,38 +31,93 @@ volatile jint GC_locker::_jni_lock_count = 0; ...@@ -31,38 +31,93 @@ volatile jint GC_locker::_jni_lock_count = 0;
volatile jint GC_locker::_lock_count = 0; volatile jint GC_locker::_lock_count = 0;
volatile bool GC_locker::_needs_gc = false; volatile bool GC_locker::_needs_gc = false;
volatile bool GC_locker::_doing_gc = false; volatile bool GC_locker::_doing_gc = false;
jlong GC_locker::_wait_begin = 0;
#ifdef ASSERT
volatile jint GC_locker::_debug_jni_lock_count = 0;
#endif
#ifdef ASSERT
void GC_locker::verify_critical_count() {
if (SafepointSynchronize::is_at_safepoint()) {
assert(!needs_gc() || _debug_jni_lock_count == _jni_lock_count, "must agree");
int count = 0;
// Count the number of threads with critical operations in progress
for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) {
if (thr->in_critical()) {
count++;
}
}
if (_jni_lock_count != count) {
tty->print_cr("critical counts don't match: %d != %d", _jni_lock_count, count);
for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) {
if (thr->in_critical()) {
tty->print_cr(INTPTR_FORMAT " in_critical %d", thr, thr->in_critical());
}
}
}
assert(_jni_lock_count == count, "must be equal");
}
}
#endif
bool GC_locker::check_active_before_gc() {
assert(SafepointSynchronize::is_at_safepoint(), "only read at safepoint");
if (is_active() && !_needs_gc) {
verify_critical_count();
_needs_gc = true;
if (PrintJNIGCStalls && PrintGCDetails) {
ResourceMark rm; // JavaThread::name() allocates to convert to UTF8
_wait_begin = tty->time_stamp().milliseconds();
gclog_or_tty->print_cr(INT64_FORMAT ": Setting _needs_gc. Thread \"%s\" %d locked.",
_wait_begin, Thread::current()->name(), _jni_lock_count);
}
}
return is_active();
}
void GC_locker::stall_until_clear() { void GC_locker::stall_until_clear() {
assert(!JavaThread::current()->in_critical(), "Would deadlock"); assert(!JavaThread::current()->in_critical(), "Would deadlock");
MutexLocker ml(JNICritical_lock);
if (needs_gc()) {
if (PrintJNIGCStalls && PrintGCDetails) { if (PrintJNIGCStalls && PrintGCDetails) {
ResourceMark rm; // JavaThread::name() allocates to convert to UTF8 ResourceMark rm; // JavaThread::name() allocates to convert to UTF8
gclog_or_tty->print_cr( gclog_or_tty->print_cr(INT64_FORMAT ": Allocation failed. Thread \"%s\" is stalled by JNI critical section, %d locked.",
"Allocation failed. Thread \"%s\" is stalled by JNI critical section.", tty->time_stamp().milliseconds() - _wait_begin, Thread::current()->name(), _jni_lock_count);
JavaThread::current()->name());
} }
MutexLocker ml(JNICritical_lock); }
// Wait for _needs_gc to be cleared // Wait for _needs_gc to be cleared
while (GC_locker::needs_gc()) { while (needs_gc()) {
JNICritical_lock->wait(); JNICritical_lock->wait();
} }
} }
void GC_locker::jni_lock_slow() { void GC_locker::jni_lock(JavaThread* thread) {
assert(!thread->in_critical(), "shouldn't currently be in a critical region");
MutexLocker mu(JNICritical_lock); MutexLocker mu(JNICritical_lock);
// Block entering threads if we know at least one thread is in a // Block entering threads if we know at least one thread is in a
// JNI critical region and we need a GC. // JNI critical region and we need a GC.
// We check that at least one thread is in a critical region before // We check that at least one thread is in a critical region before
// blocking because blocked threads are woken up by a thread exiting // blocking because blocked threads are woken up by a thread exiting
// a JNI critical region. // a JNI critical region.
while ((is_jni_active() && needs_gc()) || _doing_gc) { while ((needs_gc() && is_jni_active()) || _doing_gc) {
JNICritical_lock->wait(); JNICritical_lock->wait();
} }
jni_lock(); thread->enter_critical();
_jni_lock_count++;
increment_debug_jni_lock_count();
} }
void GC_locker::jni_unlock_slow() { void GC_locker::jni_unlock(JavaThread* thread) {
assert(thread->in_last_critical(), "should be exiting critical region");
MutexLocker mu(JNICritical_lock); MutexLocker mu(JNICritical_lock);
jni_unlock(); _jni_lock_count--;
decrement_debug_jni_lock_count();
thread->exit_critical();
if (needs_gc() && !is_jni_active()) { if (needs_gc() && !is_jni_active()) {
// We're the last thread out. Cause a GC to occur. // We're the last thread out. Cause a GC to occur.
// GC will also check is_active, so this check is not // GC will also check is_active, so this check is not
...@@ -74,11 +129,17 @@ void GC_locker::jni_unlock_slow() { ...@@ -74,11 +129,17 @@ void GC_locker::jni_unlock_slow() {
{ {
// Must give up the lock while at a safepoint // Must give up the lock while at a safepoint
MutexUnlocker munlock(JNICritical_lock); MutexUnlocker munlock(JNICritical_lock);
if (PrintJNIGCStalls && PrintGCDetails) {
ResourceMark rm; // JavaThread::name() allocates to convert to UTF8
gclog_or_tty->print_cr(INT64_FORMAT ": Thread \"%s\" is performing GC after exiting critical section, %d locked",
tty->time_stamp().milliseconds() - _wait_begin, Thread::current()->name(), _jni_lock_count);
}
Universe::heap()->collect(GCCause::_gc_locker); Universe::heap()->collect(GCCause::_gc_locker);
} }
_doing_gc = false; _doing_gc = false;
} }
clear_needs_gc();
_needs_gc = false;
JNICritical_lock->notify_all(); JNICritical_lock->notify_all();
} }
} }
......
/* /*
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -51,53 +51,70 @@ ...@@ -51,53 +51,70 @@
class GC_locker: public AllStatic { class GC_locker: public AllStatic {
private: private:
static volatile jint _jni_lock_count; // number of jni active instances // The _jni_lock_count keeps track of the number of threads that are
// currently in a critical region. It's only kept up to date when
// _needs_gc is true. The current value is computed during
// safepointing and decremented during the slow path of GC_locker
// unlocking.
static volatile jint _jni_lock_count; // number of jni active instances.
static volatile jint _lock_count; // number of other active instances static volatile jint _lock_count; // number of other active instances
static volatile bool _needs_gc; // heap is filling, we need a GC static volatile bool _needs_gc; // heap is filling, we need a GC
// note: bool is typedef'd as jint // note: bool is typedef'd as jint
static volatile bool _doing_gc; // unlock_critical() is doing a GC static volatile bool _doing_gc; // unlock_critical() is doing a GC
static jlong _wait_begin; // Timestamp for the setting of _needs_gc.
// Used only by printing code.
#ifdef ASSERT
// This lock count is updated for all operations and is used to
// validate the jni_lock_count that is computed during safepoints.
static volatile jint _debug_jni_lock_count;
#endif
// Accessors // Accessors
static bool is_jni_active() { static bool is_jni_active() {
assert(_needs_gc, "only valid when _needs_gc is set");
return _jni_lock_count > 0; return _jni_lock_count > 0;
} }
static void set_needs_gc() { // At a safepoint, visit all threads and count the number of active
assert(SafepointSynchronize::is_at_safepoint(), // critical sections. This is used to ensure that all active
"needs_gc is only set at a safepoint"); // critical sections are exited before a new one is started.
_needs_gc = true; static void verify_critical_count() NOT_DEBUG_RETURN;
}
static void clear_needs_gc() {
assert_lock_strong(JNICritical_lock);
_needs_gc = false;
}
static void jni_lock() {
Atomic::inc(&_jni_lock_count);
CHECK_UNHANDLED_OOPS_ONLY(
if (CheckUnhandledOops) { Thread::current()->_gc_locked_out_count++; })
assert(Universe::heap() == NULL || !Universe::heap()->is_gc_active(),
"locking failed");
}
static void jni_unlock() {
Atomic::dec(&_jni_lock_count);
CHECK_UNHANDLED_OOPS_ONLY(
if (CheckUnhandledOops) { Thread::current()->_gc_locked_out_count--; })
}
static void jni_lock_slow(); static void jni_lock(JavaThread* thread);
static void jni_unlock_slow(); static void jni_unlock(JavaThread* thread);
public: public:
// Accessors // Accessors
static bool is_active(); static bool is_active();
static bool needs_gc() { return _needs_gc; } static bool needs_gc() { return _needs_gc; }
// Shorthand // Shorthand
static bool is_active_and_needs_gc() { return is_active() && needs_gc();} static bool is_active_and_needs_gc() { return needs_gc() && is_active(); }
// In debug mode track the locking state at all times
static void increment_debug_jni_lock_count() {
#ifdef ASSERT
assert(_debug_jni_lock_count >= 0, "bad value");
Atomic::inc(&_debug_jni_lock_count);
#endif
}
static void decrement_debug_jni_lock_count() {
#ifdef ASSERT
assert(_debug_jni_lock_count > 0, "bad value");
Atomic::dec(&_debug_jni_lock_count);
#endif
}
// Set the current lock count
static void set_jni_lock_count(int count) {
_jni_lock_count = count;
verify_critical_count();
}
// Calls set_needs_gc() if is_active() is true. Returns is_active(). // Sets _needs_gc if is_active() is true. Returns is_active().
static bool check_active_before_gc(); static bool check_active_before_gc();
// Stalls the caller (who should not be in a jni critical section) // Stalls the caller (who should not be in a jni critical section)
...@@ -131,20 +148,20 @@ class GC_locker: public AllStatic { ...@@ -131,20 +148,20 @@ class GC_locker: public AllStatic {
// JNI critical regions are the only participants in this scheme // JNI critical regions are the only participants in this scheme
// because they are, by spec, well bounded while in a critical region. // because they are, by spec, well bounded while in a critical region.
// //
// Each of the following two method is split into a fast path and a slow // Each of the following two method is split into a fast path and a
// path. JNICritical_lock is only grabbed in the slow path. // slow path. JNICritical_lock is only grabbed in the slow path.
// _needs_gc is initially false and every java thread will go // _needs_gc is initially false and every java thread will go
// through the fast path (which does the same thing as the slow path // through the fast path, which simply increments or decrements the
// when _needs_gc is false). When GC happens at a safepoint, // current thread's critical count. When GC happens at a safepoint,
// GC_locker::is_active() is checked. Since there is no safepoint in the // GC_locker::is_active() is checked. Since there is no safepoint in
// fast path of lock_critical() and unlock_critical(), there is no race // the fast path of lock_critical() and unlock_critical(), there is
// condition between the fast path and GC. After _needs_gc is set at a // no race condition between the fast path and GC. After _needs_gc
// safepoint, every thread will go through the slow path after the safepoint. // is set at a safepoint, every thread will go through the slow path
// Since after a safepoint, each of the following two methods is either // after the safepoint. Since after a safepoint, each of the
// entered from the method entry and falls into the slow path, or is // following two methods is either entered from the method entry and
// resumed from the safepoints in the method, which only exist in the slow // falls into the slow path, or is resumed from the safepoints in
// path. So when _needs_gc is set, the slow path is always taken, till // the method, which only exist in the slow path. So when _needs_gc
// _needs_gc is cleared. // is set, the slow path is always taken, till _needs_gc is cleared.
static void lock_critical(JavaThread* thread); static void lock_critical(JavaThread* thread);
static void unlock_critical(JavaThread* thread); static void unlock_critical(JavaThread* thread);
}; };
......
/* /*
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -28,16 +28,11 @@ ...@@ -28,16 +28,11 @@
#include "memory/gcLocker.hpp" #include "memory/gcLocker.hpp"
inline bool GC_locker::is_active() { inline bool GC_locker::is_active() {
assert(_needs_gc || SafepointSynchronize::is_at_safepoint(), "only read at safepoint");
verify_critical_count();
return _lock_count > 0 || _jni_lock_count > 0; return _lock_count > 0 || _jni_lock_count > 0;
} }
inline bool GC_locker::check_active_before_gc() {
if (is_active()) {
set_needs_gc();
}
return is_active();
}
inline void GC_locker::lock() { inline void GC_locker::lock() {
// cast away volatile // cast away volatile
Atomic::inc(&_lock_count); Atomic::inc(&_lock_count);
...@@ -56,24 +51,28 @@ inline void GC_locker::unlock() { ...@@ -56,24 +51,28 @@ inline void GC_locker::unlock() {
inline void GC_locker::lock_critical(JavaThread* thread) { inline void GC_locker::lock_critical(JavaThread* thread) {
if (!thread->in_critical()) { if (!thread->in_critical()) {
if (!needs_gc()) { if (needs_gc()) {
jni_lock(); // jni_lock call calls enter_critical under the lock so that the
} else { // global lock count and per thread count are in agreement.
jni_lock_slow(); jni_lock(thread);
return;
} }
increment_debug_jni_lock_count();
} }
thread->enter_critical(); thread->enter_critical();
} }
inline void GC_locker::unlock_critical(JavaThread* thread) { inline void GC_locker::unlock_critical(JavaThread* thread) {
thread->exit_critical(); if (thread->in_last_critical()) {
if (!thread->in_critical()) { if (needs_gc()) {
if (!needs_gc()) { // jni_unlock call calls exit_critical under the lock so that
jni_unlock(); // the global lock count and per thread count are in agreement.
} else { jni_unlock(thread);
jni_unlock_slow(); return;
} }
decrement_debug_jni_lock_count();
} }
thread->exit_critical();
} }
#endif // SHARE_VM_MEMORY_GCLOCKER_INLINE_HPP #endif // SHARE_VM_MEMORY_GCLOCKER_INLINE_HPP
/* /*
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -95,6 +95,7 @@ ...@@ -95,6 +95,7 @@
SafepointSynchronize::SynchronizeState volatile SafepointSynchronize::_state = SafepointSynchronize::_not_synchronized; SafepointSynchronize::SynchronizeState volatile SafepointSynchronize::_state = SafepointSynchronize::_not_synchronized;
volatile int SafepointSynchronize::_waiting_to_block = 0; volatile int SafepointSynchronize::_waiting_to_block = 0;
volatile int SafepointSynchronize::_safepoint_counter = 0; volatile int SafepointSynchronize::_safepoint_counter = 0;
int SafepointSynchronize::_current_jni_active_count = 0;
long SafepointSynchronize::_end_of_last_safepoint = 0; long SafepointSynchronize::_end_of_last_safepoint = 0;
static volatile int PageArmed = 0 ; // safepoint polling page is RO|RW vs PROT_NONE static volatile int PageArmed = 0 ; // safepoint polling page is RO|RW vs PROT_NONE
static volatile int TryingToBlock = 0 ; // proximate value -- for advisory use only static volatile int TryingToBlock = 0 ; // proximate value -- for advisory use only
...@@ -138,6 +139,9 @@ void SafepointSynchronize::begin() { ...@@ -138,6 +139,9 @@ void SafepointSynchronize::begin() {
{ {
MutexLocker mu(Safepoint_lock); MutexLocker mu(Safepoint_lock);
// Reset the count of active JNI critical threads
_current_jni_active_count = 0;
// Set number of threads to wait for, before we initiate the callbacks // Set number of threads to wait for, before we initiate the callbacks
_waiting_to_block = nof_threads; _waiting_to_block = nof_threads;
TryingToBlock = 0 ; TryingToBlock = 0 ;
...@@ -375,6 +379,9 @@ void SafepointSynchronize::begin() { ...@@ -375,6 +379,9 @@ void SafepointSynchronize::begin() {
OrderAccess::fence(); OrderAccess::fence();
// Update the count of active JNI critical regions
GC_locker::set_jni_lock_count(_current_jni_active_count);
if (TraceSafepoint) { if (TraceSafepoint) {
VM_Operation *op = VMThread::vm_operation(); VM_Operation *op = VMThread::vm_operation();
tty->print_cr("Entering safepoint region: %s", (op != NULL) ? op->name() : "no vm operation"); tty->print_cr("Entering safepoint region: %s", (op != NULL) ? op->name() : "no vm operation");
...@@ -585,6 +592,11 @@ void SafepointSynchronize::block(JavaThread *thread) { ...@@ -585,6 +592,11 @@ void SafepointSynchronize::block(JavaThread *thread) {
_waiting_to_block--; _waiting_to_block--;
thread->safepoint_state()->set_has_called_back(true); thread->safepoint_state()->set_has_called_back(true);
if (thread->in_critical()) {
// Notice that this thread is in a critical section
increment_jni_active_count();
}
// Consider (_waiting_to_block < 2) to pipeline the wakeup of the VM thread // Consider (_waiting_to_block < 2) to pipeline the wakeup of the VM thread
if (_waiting_to_block == 0) { if (_waiting_to_block == 0) {
Safepoint_lock->notify_all(); Safepoint_lock->notify_all();
...@@ -862,6 +874,10 @@ void ThreadSafepointState::examine_state_of_thread() { ...@@ -862,6 +874,10 @@ void ThreadSafepointState::examine_state_of_thread() {
// agree and update the safepoint state here. // agree and update the safepoint state here.
if (SafepointSynchronize::safepoint_safe(_thread, state)) { if (SafepointSynchronize::safepoint_safe(_thread, state)) {
roll_forward(_at_safepoint); roll_forward(_at_safepoint);
if (_thread->in_critical()) {
// Notice that this thread is in a critical section
SafepointSynchronize::increment_jni_active_count();
}
return; return;
} }
......
/* /*
* Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "code/nmethod.hpp" #include "code/nmethod.hpp"
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "runtime/extendedPC.hpp" #include "runtime/extendedPC.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp" #include "runtime/os.hpp"
#include "utilities/ostream.hpp" #include "utilities/ostream.hpp"
...@@ -92,6 +93,7 @@ class SafepointSynchronize : AllStatic { ...@@ -92,6 +93,7 @@ class SafepointSynchronize : AllStatic {
private: private:
static volatile SynchronizeState _state; // Threads might read this flag directly, without acquireing the Threads_lock static volatile SynchronizeState _state; // Threads might read this flag directly, without acquireing the Threads_lock
static volatile int _waiting_to_block; // number of threads we are waiting for to block static volatile int _waiting_to_block; // number of threads we are waiting for to block
static int _current_jni_active_count; // Counts the number of active critical natives during the safepoint
// This counter is used for fast versions of jni_Get<Primitive>Field. // This counter is used for fast versions of jni_Get<Primitive>Field.
// An even value means there is no ongoing safepoint operations. // An even value means there is no ongoing safepoint operations.
...@@ -146,6 +148,11 @@ public: ...@@ -146,6 +148,11 @@ public:
return (_state != _not_synchronized); return (_state != _not_synchronized);
} }
inline static void increment_jni_active_count() {
assert_locked_or_safepoint(Safepoint_lock);
_current_jni_active_count++;
}
// Called when a thread volantary blocks // Called when a thread volantary blocks
static void block(JavaThread *thread); static void block(JavaThread *thread);
static void signal_thread_at_safepoint() { _waiting_to_block--; } static void signal_thread_at_safepoint() { _waiting_to_block--; }
......
/* /*
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -1310,6 +1310,7 @@ class JavaThread: public Thread { ...@@ -1310,6 +1310,7 @@ class JavaThread: public Thread {
// JNI critical regions. These can nest. // JNI critical regions. These can nest.
bool in_critical() { return _jni_active_critical > 0; } bool in_critical() { return _jni_active_critical > 0; }
bool in_last_critical() { return _jni_active_critical == 1; }
void enter_critical() { assert(Thread::current() == this, void enter_critical() { assert(Thread::current() == this,
"this must be current thread"); "this must be current thread");
_jni_active_critical++; } _jni_active_critical++; }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册