aboutsummaryrefslogtreecommitdiff
path: root/src/share/vm/runtime/biasedLocking.cpp
diff options
context:
space:
mode:
authorduke <none@none>2007-12-01 00:00:00 +0000
committerduke <none@none>2007-12-01 00:00:00 +0000
commitfa88c88cef63eb7f9083dccb60548d78635a44b1 (patch)
tree8376f6e5c41e70162b5867d9e1fea3f17f540473 /src/share/vm/runtime/biasedLocking.cpp
downloadjdk8u_hotspot-fa88c88cef63eb7f9083dccb60548d78635a44b1.tar.gz
Initial load
Diffstat (limited to 'src/share/vm/runtime/biasedLocking.cpp')
-rw-r--r--src/share/vm/runtime/biasedLocking.cpp752
1 files changed, 752 insertions, 0 deletions
diff --git a/src/share/vm/runtime/biasedLocking.cpp b/src/share/vm/runtime/biasedLocking.cpp
new file mode 100644
index 000000000..bfb3b7904
--- /dev/null
+++ b/src/share/vm/runtime/biasedLocking.cpp
@@ -0,0 +1,752 @@
+
+/*
+ * Copyright 2005-2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ *
+ */
+
+# include "incls/_precompiled.incl"
+# include "incls/_biasedLocking.cpp.incl"
+
+static bool _biased_locking_enabled = false;
+BiasedLockingCounters BiasedLocking::_counters;
+
+static GrowableArray<Handle>* _preserved_oop_stack = NULL;
+static GrowableArray<markOop>* _preserved_mark_stack = NULL;
+
+static void enable_biased_locking(klassOop k) {
+ Klass::cast(k)->set_prototype_header(markOopDesc::biased_locking_prototype());
+}
+
+class VM_EnableBiasedLocking: public VM_Operation {
+ public:
+ VM_EnableBiasedLocking() {}
+ VMOp_Type type() const { return VMOp_EnableBiasedLocking; }
+ void doit() {
+ // Iterate the system dictionary enabling biased locking for all
+ // currently loaded classes
+ SystemDictionary::classes_do(enable_biased_locking);
+ // Indicate that future instances should enable it as well
+ _biased_locking_enabled = true;
+
+ if (TraceBiasedLocking) {
+ tty->print_cr("Biased locking enabled");
+ }
+ }
+
+ bool allow_nested_vm_operations() const { return false; }
+};
+
+
+// One-shot PeriodicTask subclass for enabling biased locking
+class EnableBiasedLockingTask : public PeriodicTask {
+ public:
+ EnableBiasedLockingTask(size_t interval_time) : PeriodicTask(interval_time) {}
+
+ virtual void task() {
+ VM_EnableBiasedLocking op;
+ VMThread::execute(&op);
+
+ // Reclaim our storage and disenroll ourself
+ delete this;
+ }
+};
+
+
+void BiasedLocking::init() {
+ // If biased locking is enabled, schedule a task to fire a few
+ // seconds into the run which turns on biased locking for all
+ // currently loaded classes as well as future ones. This is a
+ // workaround for startup time regressions due to a large number of
+ // safepoints being taken during VM startup for bias revocation.
+ // Ideally we would have a lower cost for individual bias revocation
+ // and not need a mechanism like this.
+ if (UseBiasedLocking) {
+ if (BiasedLockingStartupDelay > 0) {
+ EnableBiasedLockingTask* task = new EnableBiasedLockingTask(BiasedLockingStartupDelay);
+ task->enroll();
+ } else {
+ VM_EnableBiasedLocking op;
+ VMThread::execute(&op);
+ }
+ }
+}
+
+
+bool BiasedLocking::enabled() {
+ return _biased_locking_enabled;
+}
+
+// Returns MonitorInfos for all objects locked on this thread in youngest to oldest order
+static GrowableArray<MonitorInfo*>* get_or_compute_monitor_info(JavaThread* thread) {
+ GrowableArray<MonitorInfo*>* info = thread->cached_monitor_info();
+ if (info != NULL) {
+ return info;
+ }
+
+ info = new GrowableArray<MonitorInfo*>();
+
+ // It's possible for the thread to not have any Java frames on it,
+ // i.e., if it's the main thread and it's already returned from main()
+ if (thread->has_last_Java_frame()) {
+ RegisterMap rm(thread);
+ for (javaVFrame* vf = thread->last_java_vframe(&rm); vf != NULL; vf = vf->java_sender()) {
+ GrowableArray<MonitorInfo*> *monitors = vf->monitors();
+ if (monitors != NULL) {
+ int len = monitors->length();
+ // Walk monitors youngest to oldest
+ for (int i = len - 1; i >= 0; i--) {
+ MonitorInfo* mon_info = monitors->at(i);
+ oop owner = mon_info->owner();
+ if (owner != NULL) {
+ info->append(mon_info);
+ }
+ }
+ }
+ }
+ }
+
+ thread->set_cached_monitor_info(info);
+ return info;
+}
+
+
+static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_bulk, JavaThread* requesting_thread) {
+ markOop mark = obj->mark();
+ if (!mark->has_bias_pattern()) {
+ if (TraceBiasedLocking) {
+ ResourceMark rm;
+ tty->print_cr(" (Skipping revocation of object of type %s because it's no longer biased)",
+ Klass::cast(obj->klass())->external_name());
+ }
+ return BiasedLocking::NOT_BIASED;
+ }
+
+ int age = mark->age();
+ markOop biased_prototype = markOopDesc::biased_locking_prototype()->set_age(age);
+ markOop unbiased_prototype = markOopDesc::prototype()->set_age(age);
+
+ if (TraceBiasedLocking && (Verbose || !is_bulk)) {
+ ResourceMark rm;
+ tty->print_cr("Revoking bias of object " INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s , prototype header " INTPTR_FORMAT " , allow rebias %d , requesting thread " INTPTR_FORMAT,
+ (intptr_t) obj, (intptr_t) mark, Klass::cast(obj->klass())->external_name(), (intptr_t) Klass::cast(obj->klass())->prototype_header(), (allow_rebias ? 1 : 0), (intptr_t) requesting_thread);
+ }
+
+ JavaThread* biased_thread = mark->biased_locker();
+ if (biased_thread == NULL) {
+ // Object is anonymously biased. We can get here if, for
+ // example, we revoke the bias due to an identity hash code
+ // being computed for an object.
+ if (!allow_rebias) {
+ obj->set_mark(unbiased_prototype);
+ }
+ if (TraceBiasedLocking && (Verbose || !is_bulk)) {
+ tty->print_cr(" Revoked bias of anonymously-biased object");
+ }
+ return BiasedLocking::BIAS_REVOKED;
+ }
+
+ // Handle case where the thread toward which the object was biased has exited
+ bool thread_is_alive = false;
+ if (requesting_thread == biased_thread) {
+ thread_is_alive = true;
+ } else {
+ for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
+ if (cur_thread == biased_thread) {
+ thread_is_alive = true;
+ break;
+ }
+ }
+ }
+ if (!thread_is_alive) {
+ if (allow_rebias) {
+ obj->set_mark(biased_prototype);
+ } else {
+ obj->set_mark(unbiased_prototype);
+ }
+ if (TraceBiasedLocking && (Verbose || !is_bulk)) {
+ tty->print_cr(" Revoked bias of object biased toward dead thread");
+ }
+ return BiasedLocking::BIAS_REVOKED;
+ }
+
+ // Thread owning bias is alive.
+ // Check to see whether it currently owns the lock and, if so,
+ // write down the needed displaced headers to the thread's stack.
+ // Otherwise, restore the object's header either to the unlocked
+ // or unbiased state.
+ GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(biased_thread);
+ BasicLock* highest_lock = NULL;
+ for (int i = 0; i < cached_monitor_info->length(); i++) {
+ MonitorInfo* mon_info = cached_monitor_info->at(i);
+ if (mon_info->owner() == obj) {
+ if (TraceBiasedLocking && Verbose) {
+ tty->print_cr(" mon_info->owner (" PTR_FORMAT ") == obj (" PTR_FORMAT ")",
+ (intptr_t) mon_info->owner(),
+ (intptr_t) obj);
+ }
+ // Assume recursive case and fix up highest lock later
+ markOop mark = markOopDesc::encode((BasicLock*) NULL);
+ highest_lock = mon_info->lock();
+ highest_lock->set_displaced_header(mark);
+ } else {
+ if (TraceBiasedLocking && Verbose) {
+ tty->print_cr(" mon_info->owner (" PTR_FORMAT ") != obj (" PTR_FORMAT ")",
+ (intptr_t) mon_info->owner(),
+ (intptr_t) obj);
+ }
+ }
+ }
+ if (highest_lock != NULL) {
+ // Fix up highest lock to contain displaced header and point
+ // object at it
+ highest_lock->set_displaced_header(unbiased_prototype);
+ // Reset object header to point to displaced mark
+ obj->set_mark(markOopDesc::encode(highest_lock));
+ assert(!obj->mark()->has_bias_pattern(), "illegal mark state: stack lock used bias bit");
+ if (TraceBiasedLocking && (Verbose || !is_bulk)) {
+ tty->print_cr(" Revoked bias of currently-locked object");
+ }
+ } else {
+ if (TraceBiasedLocking && (Verbose || !is_bulk)) {
+ tty->print_cr(" Revoked bias of currently-unlocked object");
+ }
+ if (allow_rebias) {
+ obj->set_mark(biased_prototype);
+ } else {
+ // Store the unlocked value into the object's header.
+ obj->set_mark(unbiased_prototype);
+ }
+ }
+
+ return BiasedLocking::BIAS_REVOKED;
+}
+
+
+enum HeuristicsResult {
+ HR_NOT_BIASED = 1,
+ HR_SINGLE_REVOKE = 2,
+ HR_BULK_REBIAS = 3,
+ HR_BULK_REVOKE = 4
+};
+
+
+static HeuristicsResult update_heuristics(oop o, bool allow_rebias) {
+ markOop mark = o->mark();
+ if (!mark->has_bias_pattern()) {
+ return HR_NOT_BIASED;
+ }
+
+ // Heuristics to attempt to throttle the number of revocations.
+ // Stages:
+ // 1. Revoke the biases of all objects in the heap of this type,
+ // but allow rebiasing of those objects if unlocked.
+ // 2. Revoke the biases of all objects in the heap of this type
+ // and don't allow rebiasing of these objects. Disable
+ // allocation of objects of that type with the bias bit set.
+ Klass* k = o->blueprint();
+ jlong cur_time = os::javaTimeMillis();
+ jlong last_bulk_revocation_time = k->last_biased_lock_bulk_revocation_time();
+ int revocation_count = k->biased_lock_revocation_count();
+ if ((revocation_count >= BiasedLockingBulkRebiasThreshold) &&
+ (revocation_count < BiasedLockingBulkRevokeThreshold) &&
+ (last_bulk_revocation_time != 0) &&
+ (cur_time - last_bulk_revocation_time >= BiasedLockingDecayTime)) {
+ // This is the first revocation we've seen in a while of an
+ // object of this type since the last time we performed a bulk
+ // rebiasing operation. The application is allocating objects in
+ // bulk which are biased toward a thread and then handing them
+ // off to another thread. We can cope with this allocation
+ // pattern via the bulk rebiasing mechanism so we reset the
+ // klass's revocation count rather than allow it to increase
+ // monotonically. If we see the need to perform another bulk
+ // rebias operation later, we will, and if subsequently we see
+ // many more revocation operations in a short period of time we
+ // will completely disable biasing for this type.
+ k->set_biased_lock_revocation_count(0);
+ revocation_count = 0;
+ }
+
+ // Make revocation count saturate just beyond BiasedLockingBulkRevokeThreshold
+ if (revocation_count <= BiasedLockingBulkRevokeThreshold) {
+ revocation_count = k->atomic_incr_biased_lock_revocation_count();
+ }
+
+ if (revocation_count == BiasedLockingBulkRevokeThreshold) {
+ return HR_BULK_REVOKE;
+ }
+
+ if (revocation_count == BiasedLockingBulkRebiasThreshold) {
+ return HR_BULK_REBIAS;
+ }
+
+ return HR_SINGLE_REVOKE;
+}
+
+
+static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
+ bool bulk_rebias,
+ bool attempt_rebias_of_object,
+ JavaThread* requesting_thread) {
+ assert(SafepointSynchronize::is_at_safepoint(), "must be done at safepoint");
+
+ if (TraceBiasedLocking) {
+ tty->print_cr("* Beginning bulk revocation (kind == %s) because of object "
+ INTPTR_FORMAT " , mark " INTPTR_FORMAT " , type %s",
+ (bulk_rebias ? "rebias" : "revoke"),
+ (intptr_t) o, (intptr_t) o->mark(), Klass::cast(o->klass())->external_name());
+ }
+
+ jlong cur_time = os::javaTimeMillis();
+ o->blueprint()->set_last_biased_lock_bulk_revocation_time(cur_time);
+
+
+ klassOop k_o = o->klass();
+ Klass* klass = Klass::cast(k_o);
+
+ if (bulk_rebias) {
+ // Use the epoch in the klass of the object to implicitly revoke
+ // all biases of objects of this data type and force them to be
+ // reacquired. However, we also need to walk the stacks of all
+ // threads and update the headers of lightweight locked objects
+ // with biases to have the current epoch.
+
+ // If the prototype header doesn't have the bias pattern, don't
+ // try to update the epoch -- assume another VM operation came in
+ // and reset the header to the unbiased state, which will
+ // implicitly cause all existing biases to be revoked
+ if (klass->prototype_header()->has_bias_pattern()) {
+ int prev_epoch = klass->prototype_header()->bias_epoch();
+ klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
+ int cur_epoch = klass->prototype_header()->bias_epoch();
+
+ // Now walk all threads' stacks and adjust epochs of any biased
+ // and locked objects of this data type we encounter
+ for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
+ GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
+ for (int i = 0; i < cached_monitor_info->length(); i++) {
+ MonitorInfo* mon_info = cached_monitor_info->at(i);
+ oop owner = mon_info->owner();
+ markOop mark = owner->mark();
+ if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
+ // We might have encountered this object already in the case of recursive locking
+ assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
+ owner->set_mark(mark->set_bias_epoch(cur_epoch));
+ }
+ }
+ }
+ }
+
+ // At this point we're done. All we have to do is potentially
+ // adjust the header of the given object to revoke its bias.
+ revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread);
+ } else {
+ if (TraceBiasedLocking) {
+ ResourceMark rm;
+ tty->print_cr("* Disabling biased locking for type %s", klass->external_name());
+ }
+
+ // Disable biased locking for this data type. Not only will this
+ // cause future instances to not be biased, but existing biased
+ // instances will notice that this implicitly caused their biases
+ // to be revoked.
+ klass->set_prototype_header(markOopDesc::prototype());
+
+ // Now walk all threads' stacks and forcibly revoke the biases of
+ // any locked and biased objects of this data type we encounter.
+ for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
+ GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
+ for (int i = 0; i < cached_monitor_info->length(); i++) {
+ MonitorInfo* mon_info = cached_monitor_info->at(i);
+ oop owner = mon_info->owner();
+ markOop mark = owner->mark();
+ if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
+ revoke_bias(owner, false, true, requesting_thread);
+ }
+ }
+ }
+
+ // Must force the bias of the passed object to be forcibly revoked
+ // as well to ensure guarantees to callers
+ revoke_bias(o, false, true, requesting_thread);
+ }
+
+ if (TraceBiasedLocking) {
+ tty->print_cr("* Ending bulk revocation");
+ }
+
+ BiasedLocking::Condition status_code = BiasedLocking::BIAS_REVOKED;
+
+ if (attempt_rebias_of_object &&
+ o->mark()->has_bias_pattern() &&
+ klass->prototype_header()->has_bias_pattern()) {
+ markOop new_mark = markOopDesc::encode(requesting_thread, o->mark()->age(),
+ klass->prototype_header()->bias_epoch());
+ o->set_mark(new_mark);
+ status_code = BiasedLocking::BIAS_REVOKED_AND_REBIASED;
+ if (TraceBiasedLocking) {
+ tty->print_cr(" Rebiased object toward thread " INTPTR_FORMAT, (intptr_t) requesting_thread);
+ }
+ }
+
+ assert(!o->mark()->has_bias_pattern() ||
+ (attempt_rebias_of_object && (o->mark()->biased_locker() == requesting_thread)),
+ "bug in bulk bias revocation");
+
+ return status_code;
+}
+
+
+static void clean_up_cached_monitor_info() {
+ // Walk the thread list clearing out the cached monitors
+ for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
+ thr->set_cached_monitor_info(NULL);
+ }
+}
+
+
+class VM_RevokeBias : public VM_Operation {
+protected:
+ Handle* _obj;
+ GrowableArray<Handle>* _objs;
+ JavaThread* _requesting_thread;
+ BiasedLocking::Condition _status_code;
+
+public:
+ VM_RevokeBias(Handle* obj, JavaThread* requesting_thread)
+ : _obj(obj)
+ , _objs(NULL)
+ , _requesting_thread(requesting_thread)
+ , _status_code(BiasedLocking::NOT_BIASED) {}
+
+ VM_RevokeBias(GrowableArray<Handle>* objs, JavaThread* requesting_thread)
+ : _obj(NULL)
+ , _objs(objs)
+ , _requesting_thread(requesting_thread)
+ , _status_code(BiasedLocking::NOT_BIASED) {}
+
+ virtual VMOp_Type type() const { return VMOp_RevokeBias; }
+
+ virtual bool doit_prologue() {
+ // Verify that there is actual work to do since the callers just
+ // give us locked object(s). If we don't find any biased objects
+ // there is nothing to do and we avoid a safepoint.
+ if (_obj != NULL) {
+ markOop mark = (*_obj)()->mark();
+ if (mark->has_bias_pattern()) {
+ return true;
+ }
+ } else {
+ for ( int i = 0 ; i < _objs->length(); i++ ) {
+ markOop mark = (_objs->at(i))()->mark();
+ if (mark->has_bias_pattern()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ virtual void doit() {
+ if (_obj != NULL) {
+ if (TraceBiasedLocking) {
+ tty->print_cr("Revoking bias with potentially per-thread safepoint:");
+ }
+ _status_code = revoke_bias((*_obj)(), false, false, _requesting_thread);
+ clean_up_cached_monitor_info();
+ return;
+ } else {
+ if (TraceBiasedLocking) {
+ tty->print_cr("Revoking bias with global safepoint:");
+ }
+ BiasedLocking::revoke_at_safepoint(_objs);
+ }
+ }
+
+ BiasedLocking::Condition status_code() const {
+ return _status_code;
+ }
+};
+
+
+class VM_BulkRevokeBias : public VM_RevokeBias {
+private:
+ bool _bulk_rebias;
+ bool _attempt_rebias_of_object;
+
+public:
+ VM_BulkRevokeBias(Handle* obj, JavaThread* requesting_thread,
+ bool bulk_rebias,
+ bool attempt_rebias_of_object)
+ : VM_RevokeBias(obj, requesting_thread)
+ , _bulk_rebias(bulk_rebias)
+ , _attempt_rebias_of_object(attempt_rebias_of_object) {}
+
+ virtual VMOp_Type type() const { return VMOp_BulkRevokeBias; }
+ virtual bool doit_prologue() { return true; }
+
+ virtual void doit() {
+ _status_code = bulk_revoke_or_rebias_at_safepoint((*_obj)(), _bulk_rebias, _attempt_rebias_of_object, _requesting_thread);
+ clean_up_cached_monitor_info();
+ }
+};
+
+
+BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
+ assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");
+
+ // We can revoke the biases of anonymously-biased objects
+ // efficiently enough that we should not cause these revocations to
+ // update the heuristics because doing so may cause unwanted bulk
+ // revocations (which are expensive) to occur.
+ markOop mark = obj->mark();
+ if (mark->is_biased_anonymously() && !attempt_rebias) {
+ // We are probably trying to revoke the bias of this object due to
+ // an identity hash code computation. Try to revoke the bias
+ // without a safepoint. This is possible if we can successfully
+ // compare-and-exchange an unbiased header into the mark word of
+ // the object, meaning that no other thread has raced to acquire
+ // the bias of the object.
+ markOop biased_value = mark;
+ markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
+ markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
+ if (res_mark == biased_value) {
+ return BIAS_REVOKED;
+ }
+ } else if (mark->has_bias_pattern()) {
+ Klass* k = Klass::cast(obj->klass());
+ markOop prototype_header = k->prototype_header();
+ if (!prototype_header->has_bias_pattern()) {
+ // This object has a stale bias from before the bulk revocation
+ // for this data type occurred. It's pointless to update the
+ // heuristics at this point so simply update the header with a
+ // CAS. If we fail this race, the object's bias has been revoked
+ // by another thread so we simply return and let the caller deal
+ // with it.
+ markOop biased_value = mark;
+ markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);
+ assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
+ return BIAS_REVOKED;
+ } else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
+ // The epoch of this biasing has expired indicating that the
+ // object is effectively unbiased. Depending on whether we need
+ // to rebias or revoke the bias of this object we can do it
+ // efficiently enough with a CAS that we shouldn't update the
+ // heuristics. This is normally done in the assembly code but we
+ // can reach this point due to various points in the runtime
+ // needing to revoke biases.
+ if (attempt_rebias) {
+ assert(THREAD->is_Java_thread(), "");
+ markOop biased_value = mark;
+ markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
+ markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
+ if (res_mark == biased_value) {
+ return BIAS_REVOKED_AND_REBIASED;
+ }
+ } else {
+ markOop biased_value = mark;
+ markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
+ markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
+ if (res_mark == biased_value) {
+ return BIAS_REVOKED;
+ }
+ }
+ }
+ }
+
+ HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
+ if (heuristics == HR_NOT_BIASED) {
+ return NOT_BIASED;
+ } else if (heuristics == HR_SINGLE_REVOKE) {
+ if (mark->biased_locker() == THREAD) {
+ // A thread is trying to revoke the bias of an object biased
+ // toward it, again likely due to an identity hash code
+ // computation. We can again avoid a safepoint in this case
+ // since we are only going to walk our own stack. There are no
+ // races with revocations occurring in other threads because we
+ // reach no safepoints in the revocation path.
+ ResourceMark rm;
+ if (TraceBiasedLocking) {
+ tty->print_cr("Revoking bias by walking my own stack:");
+ }
+ BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD);
+ ((JavaThread*) THREAD)->set_cached_monitor_info(NULL);
+ assert(cond == BIAS_REVOKED, "why not?");
+ return cond;
+ } else {
+ VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
+ VMThread::execute(&revoke);
+ return revoke.status_code();
+ }
+ }
+
+ assert((heuristics == HR_BULK_REVOKE) ||
+ (heuristics == HR_BULK_REBIAS), "?");
+ VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD,
+ (heuristics == HR_BULK_REBIAS),
+ attempt_rebias);
+ VMThread::execute(&bulk_revoke);
+ return bulk_revoke.status_code();
+}
+
+
+void BiasedLocking::revoke(GrowableArray<Handle>* objs) {
+ assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");
+ if (objs->length() == 0) {
+ return;
+ }
+ VM_RevokeBias revoke(objs, JavaThread::current());
+ VMThread::execute(&revoke);
+}
+
+
+void BiasedLocking::revoke_at_safepoint(Handle h_obj) {
+ assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint");
+ oop obj = h_obj();
+ HeuristicsResult heuristics = update_heuristics(obj, false);
+ if (heuristics == HR_SINGLE_REVOKE) {
+ revoke_bias(obj, false, false, NULL);
+ } else if ((heuristics == HR_BULK_REBIAS) ||
+ (heuristics == HR_BULK_REVOKE)) {
+ bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL);
+ }
+ clean_up_cached_monitor_info();
+}
+
+
+void BiasedLocking::revoke_at_safepoint(GrowableArray<Handle>* objs) {
+ assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint");
+ int len = objs->length();
+ for (int i = 0; i < len; i++) {
+ oop obj = (objs->at(i))();
+ HeuristicsResult heuristics = update_heuristics(obj, false);
+ if (heuristics == HR_SINGLE_REVOKE) {
+ revoke_bias(obj, false, false, NULL);
+ } else if ((heuristics == HR_BULK_REBIAS) ||
+ (heuristics == HR_BULK_REVOKE)) {
+ bulk_revoke_or_rebias_at_safepoint(obj, (heuristics == HR_BULK_REBIAS), false, NULL);
+ }
+ }
+ clean_up_cached_monitor_info();
+}
+
+
+void BiasedLocking::preserve_marks() {
+ if (!UseBiasedLocking)
+ return;
+
+ assert(SafepointSynchronize::is_at_safepoint(), "must only be called while at safepoint");
+
+ assert(_preserved_oop_stack == NULL, "double initialization");
+ assert(_preserved_mark_stack == NULL, "double initialization");
+
+ // In order to reduce the number of mark words preserved during GC
+ // due to the presence of biased locking, we reinitialize most mark
+ // words to the class's prototype during GC -- even those which have
+ // a currently valid bias owner. One important situation where we
+ // must not clobber a bias is when a biased object is currently
+ // locked. To handle this case we iterate over the currently-locked
+ // monitors in a prepass and, if they are biased, preserve their
+ // mark words here. This should be a relatively small set of objects
+ // especially compared to the number of objects in the heap.
+ _preserved_mark_stack = new (ResourceObj::C_HEAP) GrowableArray<markOop>(10, true);
+ _preserved_oop_stack = new (ResourceObj::C_HEAP) GrowableArray<Handle>(10, true);
+
+ ResourceMark rm;
+ Thread* cur = Thread::current();
+ for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
+ if (thread->has_last_Java_frame()) {
+ RegisterMap rm(thread);
+ for (javaVFrame* vf = thread->last_java_vframe(&rm); vf != NULL; vf = vf->java_sender()) {
+ GrowableArray<MonitorInfo*> *monitors = vf->monitors();
+ if (monitors != NULL) {
+ int len = monitors->length();
+ // Walk monitors youngest to oldest
+ for (int i = len - 1; i >= 0; i--) {
+ MonitorInfo* mon_info = monitors->at(i);
+ oop owner = mon_info->owner();
+ if (owner != NULL) {
+ markOop mark = owner->mark();
+ if (mark->has_bias_pattern()) {
+ _preserved_oop_stack->push(Handle(cur, owner));
+ _preserved_mark_stack->push(mark);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void BiasedLocking::restore_marks() {
+ if (!UseBiasedLocking)
+ return;
+
+ assert(_preserved_oop_stack != NULL, "double free");
+ assert(_preserved_mark_stack != NULL, "double free");
+
+ int len = _preserved_oop_stack->length();
+ for (int i = 0; i < len; i++) {
+ Handle owner = _preserved_oop_stack->at(i);
+ markOop mark = _preserved_mark_stack->at(i);
+ owner->set_mark(mark);
+ }
+
+ delete _preserved_oop_stack;
+ _preserved_oop_stack = NULL;
+ delete _preserved_mark_stack;
+ _preserved_mark_stack = NULL;
+}
+
+
+int* BiasedLocking::total_entry_count_addr() { return _counters.total_entry_count_addr(); }
+int* BiasedLocking::biased_lock_entry_count_addr() { return _counters.biased_lock_entry_count_addr(); }
+int* BiasedLocking::anonymously_biased_lock_entry_count_addr() { return _counters.anonymously_biased_lock_entry_count_addr(); }
+int* BiasedLocking::rebiased_lock_entry_count_addr() { return _counters.rebiased_lock_entry_count_addr(); }
+int* BiasedLocking::revoked_lock_entry_count_addr() { return _counters.revoked_lock_entry_count_addr(); }
+int* BiasedLocking::fast_path_entry_count_addr() { return _counters.fast_path_entry_count_addr(); }
+int* BiasedLocking::slow_path_entry_count_addr() { return _counters.slow_path_entry_count_addr(); }
+
+
+// BiasedLockingCounters
+
+int BiasedLockingCounters::slow_path_entry_count() {
+ if (_slow_path_entry_count != 0) {
+ return _slow_path_entry_count;
+ }
+ int sum = _biased_lock_entry_count + _anonymously_biased_lock_entry_count +
+ _rebiased_lock_entry_count + _revoked_lock_entry_count +
+ _fast_path_entry_count;
+
+ return _total_entry_count - sum;
+}
+
+void BiasedLockingCounters::print_on(outputStream* st) {
+ tty->print_cr("# total entries: %d", _total_entry_count);
+ tty->print_cr("# biased lock entries: %d", _biased_lock_entry_count);
+ tty->print_cr("# anonymously biased lock entries: %d", _anonymously_biased_lock_entry_count);
+ tty->print_cr("# rebiased lock entries: %d", _rebiased_lock_entry_count);
+ tty->print_cr("# revoked lock entries: %d", _revoked_lock_entry_count);
+ tty->print_cr("# fast path lock entries: %d", _fast_path_entry_count);
+ tty->print_cr("# slow path lock entries: %d", slow_path_entry_count());
+}