mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-18 01:54:47 +02:00
8227745: Enable Escape Analysis for Better Performance in the Presence of JVMTI Agents
8233915: JVMTI FollowReferences: Java Heap Leak not found because of C2 Scalar Replacement Reviewed-by: mdoerr, goetz, sspitsyn, kvn
This commit is contained in:
parent
f167a71f1d
commit
40f847e2fb
53 changed files with 5744 additions and 218 deletions
|
@ -48,18 +48,21 @@
|
|||
#include "oops/fieldStreams.inline.hpp"
|
||||
#include "oops/typeArrayOop.inline.hpp"
|
||||
#include "oops/verifyOopClosure.hpp"
|
||||
#include "prims/jvmtiDeferredUpdates.hpp"
|
||||
#include "prims/jvmtiThreadState.hpp"
|
||||
#include "prims/vectorSupport.hpp"
|
||||
#include "prims/methodHandles.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/biasedLocking.hpp"
|
||||
#include "runtime/deoptimization.hpp"
|
||||
#include "runtime/escapeBarrier.hpp"
|
||||
#include "runtime/fieldDescriptor.hpp"
|
||||
#include "runtime/fieldDescriptor.inline.hpp"
|
||||
#include "runtime/frame.inline.hpp"
|
||||
#include "runtime/handles.inline.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/jniHandles.inline.hpp"
|
||||
#include "runtime/objectMonitor.inline.hpp"
|
||||
#include "runtime/safepointVerifiers.hpp"
|
||||
#include "runtime/sharedRuntime.hpp"
|
||||
#include "runtime/signature.hpp"
|
||||
|
@ -175,10 +178,15 @@ JRT_END
|
|||
|
||||
#if COMPILER2_OR_JVMCI
|
||||
static bool eliminate_allocations(JavaThread* thread, int exec_mode, CompiledMethod* compiled_method,
|
||||
frame& deoptee, RegisterMap& map, GrowableArray<compiledVFrame*>* chunk) {
|
||||
frame& deoptee, RegisterMap& map, GrowableArray<compiledVFrame*>* chunk,
|
||||
bool& deoptimized_objects) {
|
||||
bool realloc_failures = false;
|
||||
assert (chunk->at(0)->scope() != NULL,"expect only compiled java frames");
|
||||
|
||||
JavaThread* deoptee_thread = chunk->at(0)->thread();
|
||||
assert(exec_mode == Deoptimization::Unpack_none || (deoptee_thread == thread),
|
||||
"a frame can only be deoptimized by the owner thread");
|
||||
|
||||
GrowableArray<ScopeValue*>* objects = chunk->at(0)->scope()->objects();
|
||||
|
||||
// The flag return_oop() indicates call sites which return oop
|
||||
|
@ -205,15 +213,28 @@ static bool eliminate_allocations(JavaThread* thread, int exec_mode, CompiledMet
|
|||
}
|
||||
}
|
||||
if (objects != NULL) {
|
||||
JRT_BLOCK
|
||||
if (exec_mode == Deoptimization::Unpack_none) {
|
||||
assert(thread->thread_state() == _thread_in_vm, "assumption");
|
||||
Thread* THREAD = thread;
|
||||
// Clear pending OOM if reallocation fails and return true indicating allocation failure
|
||||
realloc_failures = Deoptimization::realloc_objects(thread, &deoptee, &map, objects, CHECK_AND_CLEAR_(true));
|
||||
// Make sure the deoptee frame gets processed after a potential safepoint during
|
||||
// object reallocation. This is necessary because (a) deoptee_thread can be
|
||||
// different from the current thread and (b) the deoptee frame does not need to be
|
||||
// the top frame.
|
||||
StackWatermarkSet::finish_processing(deoptee_thread, NULL /* context */, StackWatermarkKind::gc);
|
||||
deoptimized_objects = true;
|
||||
} else {
|
||||
JRT_BLOCK
|
||||
realloc_failures = Deoptimization::realloc_objects(thread, &deoptee, &map, objects, THREAD);
|
||||
JRT_END
|
||||
JRT_END
|
||||
}
|
||||
bool skip_internal = (compiled_method != NULL) && !compiled_method->is_compiled_by_jvmci();
|
||||
Deoptimization::reassign_fields(&deoptee, &map, objects, realloc_failures, skip_internal);
|
||||
#ifndef PRODUCT
|
||||
if (TraceDeoptimization) {
|
||||
ttyLocker ttyl;
|
||||
tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, p2i(thread));
|
||||
tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, p2i(deoptee_thread));
|
||||
Deoptimization::print_objects(objects, realloc_failures);
|
||||
}
|
||||
#endif
|
||||
|
@ -225,7 +246,10 @@ static bool eliminate_allocations(JavaThread* thread, int exec_mode, CompiledMet
|
|||
return realloc_failures;
|
||||
}
|
||||
|
||||
static void eliminate_locks(JavaThread* thread, GrowableArray<compiledVFrame*>* chunk, bool realloc_failures) {
|
||||
static void eliminate_locks(JavaThread* thread, GrowableArray<compiledVFrame*>* chunk, bool realloc_failures,
|
||||
frame& deoptee, int exec_mode, bool& deoptimized_objects) {
|
||||
JavaThread* deoptee_thread = chunk->at(0)->thread();
|
||||
assert(!EscapeBarrier::objs_are_deoptimized(deoptee_thread, deoptee.id()), "must relock just once");
|
||||
assert(thread == Thread::current(), "should be");
|
||||
HandleMark hm(thread);
|
||||
#ifndef PRODUCT
|
||||
|
@ -236,7 +260,9 @@ static void eliminate_locks(JavaThread* thread, GrowableArray<compiledVFrame*>*
|
|||
assert (cvf->scope() != NULL,"expect only compiled java frames");
|
||||
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
|
||||
if (monitors->is_nonempty()) {
|
||||
Deoptimization::relock_objects(monitors, thread, realloc_failures);
|
||||
bool relocked = Deoptimization::relock_objects(thread, monitors, deoptee_thread, deoptee,
|
||||
exec_mode, realloc_failures);
|
||||
deoptimized_objects = deoptimized_objects || relocked;
|
||||
#ifndef PRODUCT
|
||||
if (PrintDeoptimizationDetails) {
|
||||
ttyLocker ttyl;
|
||||
|
@ -247,6 +273,13 @@ static void eliminate_locks(JavaThread* thread, GrowableArray<compiledVFrame*>*
|
|||
first = false;
|
||||
tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, p2i(thread));
|
||||
}
|
||||
if (exec_mode == Deoptimization::Unpack_none) {
|
||||
ObjectMonitor* monitor = deoptee_thread->current_waiting_monitor();
|
||||
if (monitor != NULL && (oop)monitor->object() == mi->owner()) {
|
||||
tty->print_cr(" object <" INTPTR_FORMAT "> DEFERRED relocking after wait", p2i(mi->owner()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (mi->owner_is_scalar_replaced()) {
|
||||
Klass* k = java_lang_Class::as_Klass(mi->owner_klass());
|
||||
tty->print_cr(" failed reallocation for klass %s", k->external_name());
|
||||
|
@ -260,6 +293,36 @@ static void eliminate_locks(JavaThread* thread, GrowableArray<compiledVFrame*>*
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deoptimize objects, that is reallocate and relock them, just before they escape through JVMTI.
|
||||
// The given vframes cover one physical frame.
|
||||
bool Deoptimization::deoptimize_objects_internal(JavaThread* thread, GrowableArray<compiledVFrame*>* chunk,
|
||||
bool& realloc_failures) {
|
||||
frame deoptee = chunk->at(0)->fr();
|
||||
JavaThread* deoptee_thread = chunk->at(0)->thread();
|
||||
CompiledMethod* cm = deoptee.cb()->as_compiled_method_or_null();
|
||||
RegisterMap map(chunk->at(0)->register_map());
|
||||
bool deoptimized_objects = false;
|
||||
|
||||
bool const jvmci_enabled = JVMCI_ONLY(UseJVMCICompiler) NOT_JVMCI(false);
|
||||
|
||||
// Reallocate the non-escaping objects and restore their fields.
|
||||
if (jvmci_enabled COMPILER2_PRESENT(|| (DoEscapeAnalysis && EliminateAllocations))) {
|
||||
realloc_failures = eliminate_allocations(thread, Unpack_none, cm, deoptee, map, chunk, deoptimized_objects);
|
||||
}
|
||||
|
||||
// Revoke biases of objects with eliminated locks in the given frame.
|
||||
Deoptimization::revoke_for_object_deoptimization(deoptee_thread, deoptee, &map, thread);
|
||||
|
||||
// MonitorInfo structures used in eliminate_locks are not GC safe.
|
||||
NoSafepointVerifier no_safepoint;
|
||||
|
||||
// Now relock objects if synchronization on them was eliminated.
|
||||
if (jvmci_enabled COMPILER2_PRESENT(|| ((DoEscapeAnalysis || EliminateNestedLocks) && EliminateLocks))) {
|
||||
eliminate_locks(thread, chunk, realloc_failures, deoptee, Unpack_none, deoptimized_objects);
|
||||
}
|
||||
return deoptimized_objects;
|
||||
}
|
||||
#endif // COMPILER2_OR_JVMCI
|
||||
|
||||
// This is factored, since it is both called from a JRT_LEAF (deoptimization) and a JRT_ENTRY (uncommon_trap)
|
||||
|
@ -318,7 +381,8 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
|||
// Reallocate the non-escaping objects and restore their fields. Then
|
||||
// relock objects if synchronization on them was eliminated.
|
||||
if (jvmci_enabled COMPILER2_PRESENT( || (DoEscapeAnalysis && EliminateAllocations) )) {
|
||||
realloc_failures = eliminate_allocations(thread, exec_mode, cm, deoptee, map, chunk);
|
||||
bool unused;
|
||||
realloc_failures = eliminate_allocations(thread, exec_mode, cm, deoptee, map, chunk, unused);
|
||||
}
|
||||
#endif // COMPILER2_OR_JVMCI
|
||||
|
||||
|
@ -333,8 +397,10 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
|||
NoSafepointVerifier no_safepoint;
|
||||
|
||||
#if COMPILER2_OR_JVMCI
|
||||
if (jvmci_enabled COMPILER2_PRESENT( || ((DoEscapeAnalysis || EliminateNestedLocks) && EliminateLocks) )) {
|
||||
eliminate_locks(thread, chunk, realloc_failures);
|
||||
if ((jvmci_enabled COMPILER2_PRESENT( || ((DoEscapeAnalysis || EliminateNestedLocks) && EliminateLocks) ))
|
||||
&& !EscapeBarrier::objs_are_deoptimized(thread, deoptee.id())) {
|
||||
bool unused;
|
||||
eliminate_locks(thread, chunk, realloc_failures, deoptee, exec_mode, unused);
|
||||
}
|
||||
#endif // COMPILER2_OR_JVMCI
|
||||
|
||||
|
@ -365,28 +431,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
|||
// added by jvmti then we can free up that structure as the data is now in the
|
||||
// vframeArray
|
||||
|
||||
if (thread->deferred_locals() != NULL) {
|
||||
GrowableArray<jvmtiDeferredLocalVariableSet*>* list = thread->deferred_locals();
|
||||
int i = 0;
|
||||
do {
|
||||
// Because of inlining we could have multiple vframes for a single frame
|
||||
// and several of the vframes could have deferred writes. Find them all.
|
||||
if (list->at(i)->id() == array->original().id()) {
|
||||
jvmtiDeferredLocalVariableSet* dlv = list->at(i);
|
||||
list->remove_at(i);
|
||||
// individual jvmtiDeferredLocalVariableSet are CHeapObj's
|
||||
delete dlv;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
} while ( i < list->length() );
|
||||
if (list->length() == 0) {
|
||||
thread->set_deferred_locals(NULL);
|
||||
// free the list and elements back to C heap.
|
||||
delete list;
|
||||
}
|
||||
|
||||
}
|
||||
JvmtiDeferredUpdates::delete_updates_for_frame(thread, array->original().id());
|
||||
|
||||
// Compute the caller frame based on the sender sp of stub_frame and stored frame sizes info.
|
||||
CodeBlob* cb = stub_frame.cb();
|
||||
|
@ -1380,11 +1425,14 @@ void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableAr
|
|||
|
||||
|
||||
// relock objects for which synchronization was eliminated
|
||||
void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread, bool realloc_failures) {
|
||||
bool Deoptimization::relock_objects(JavaThread* thread, GrowableArray<MonitorInfo*>* monitors,
|
||||
JavaThread* deoptee_thread, frame& fr, int exec_mode, bool realloc_failures) {
|
||||
bool relocked_objects = false;
|
||||
for (int i = 0; i < monitors->length(); i++) {
|
||||
MonitorInfo* mon_info = monitors->at(i);
|
||||
if (mon_info->eliminated()) {
|
||||
assert(!mon_info->owner_is_scalar_replaced() || realloc_failures, "reallocation was missed");
|
||||
relocked_objects = true;
|
||||
if (!mon_info->owner_is_scalar_replaced()) {
|
||||
Handle obj(thread, mon_info->owner());
|
||||
markWord mark = obj->mark();
|
||||
|
@ -1393,17 +1441,37 @@ void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaT
|
|||
// Also the deoptimized method may called methods with synchronization
|
||||
// where the thread-local object is bias locked to the current thread.
|
||||
assert(mark.is_biased_anonymously() ||
|
||||
mark.biased_locker() == thread, "should be locked to current thread");
|
||||
mark.biased_locker() == deoptee_thread, "should be locked to current thread");
|
||||
// Reset mark word to unbiased prototype.
|
||||
markWord unbiased_prototype = markWord::prototype().set_age(mark.age());
|
||||
obj->set_mark(unbiased_prototype);
|
||||
} else if (exec_mode == Unpack_none) {
|
||||
if (mark.has_locker() && fr.sp() > (intptr_t*)mark.locker()) {
|
||||
// With exec_mode == Unpack_none obj may be thread local and locked in
|
||||
// a callee frame. In this case the bias was revoked before in revoke_for_object_deoptimization().
|
||||
// Make the lock in the callee a recursive lock and restore the displaced header.
|
||||
markWord dmw = mark.displaced_mark_helper();
|
||||
mark.locker()->set_displaced_header(markWord::encode((BasicLock*) NULL));
|
||||
obj->set_mark(dmw);
|
||||
}
|
||||
if (mark.has_monitor()) {
|
||||
// defer relocking if the deoptee thread is currently waiting for obj
|
||||
ObjectMonitor* waiting_monitor = deoptee_thread->current_waiting_monitor();
|
||||
if (waiting_monitor != NULL && (oop)waiting_monitor->object() == obj()) {
|
||||
assert(fr.is_deoptimized_frame(), "frame must be scheduled for deoptimization");
|
||||
mon_info->lock()->set_displaced_header(markWord::unused_mark());
|
||||
JvmtiDeferredUpdates::inc_relock_count_after_wait(deoptee_thread);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
BasicLock* lock = mon_info->lock();
|
||||
ObjectSynchronizer::enter(obj, lock, thread);
|
||||
ObjectSynchronizer::enter(obj, lock, deoptee_thread);
|
||||
assert(mon_info->owner()->is_locked(), "object must be locked now");
|
||||
}
|
||||
}
|
||||
}
|
||||
return relocked_objects;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1521,18 +1589,22 @@ void Deoptimization::pop_frames_failed_reallocs(JavaThread* thread, vframeArray*
|
|||
}
|
||||
#endif
|
||||
|
||||
static void collect_monitors(compiledVFrame* cvf, GrowableArray<Handle>* objects_to_revoke) {
|
||||
static void collect_monitors(compiledVFrame* cvf, GrowableArray<Handle>* objects_to_revoke,
|
||||
bool only_eliminated) {
|
||||
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
|
||||
Thread* thread = Thread::current();
|
||||
for (int i = 0; i < monitors->length(); i++) {
|
||||
MonitorInfo* mon_info = monitors->at(i);
|
||||
if (!mon_info->eliminated() && mon_info->owner() != NULL) {
|
||||
if (mon_info->eliminated() == only_eliminated &&
|
||||
!mon_info->owner_is_scalar_replaced() &&
|
||||
mon_info->owner() != NULL) {
|
||||
objects_to_revoke->append(Handle(thread, mon_info->owner()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_monitors_from_stack(GrowableArray<Handle>* objects_to_revoke, JavaThread* thread, frame fr, RegisterMap* map) {
|
||||
static void get_monitors_from_stack(GrowableArray<Handle>* objects_to_revoke, JavaThread* thread,
|
||||
frame fr, RegisterMap* map, bool only_eliminated) {
|
||||
// Unfortunately we don't have a RegisterMap available in most of
|
||||
// the places we want to call this routine so we need to walk the
|
||||
// stack again to update the register map.
|
||||
|
@ -1552,10 +1624,10 @@ static void get_monitors_from_stack(GrowableArray<Handle>* objects_to_revoke, Ja
|
|||
compiledVFrame* cvf = compiledVFrame::cast(vf);
|
||||
// Revoke monitors' biases in all scopes
|
||||
while (!cvf->is_top()) {
|
||||
collect_monitors(cvf, objects_to_revoke);
|
||||
collect_monitors(cvf, objects_to_revoke, only_eliminated);
|
||||
cvf = compiledVFrame::cast(cvf->sender());
|
||||
}
|
||||
collect_monitors(cvf, objects_to_revoke);
|
||||
collect_monitors(cvf, objects_to_revoke, only_eliminated);
|
||||
}
|
||||
|
||||
void Deoptimization::revoke_from_deopt_handler(JavaThread* thread, frame fr, RegisterMap* map) {
|
||||
|
@ -1566,7 +1638,7 @@ void Deoptimization::revoke_from_deopt_handler(JavaThread* thread, frame fr, Reg
|
|||
ResourceMark rm(thread);
|
||||
HandleMark hm(thread);
|
||||
GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
|
||||
get_monitors_from_stack(objects_to_revoke, thread, fr, map);
|
||||
get_monitors_from_stack(objects_to_revoke, thread, fr, map, false);
|
||||
|
||||
int len = objects_to_revoke->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
@ -1576,6 +1648,41 @@ void Deoptimization::revoke_from_deopt_handler(JavaThread* thread, frame fr, Reg
|
|||
}
|
||||
}
|
||||
|
||||
// Revoke the bias of objects with eliminated locking to prepare subsequent relocking.
|
||||
void Deoptimization::revoke_for_object_deoptimization(JavaThread* deoptee_thread, frame fr,
|
||||
RegisterMap* map, JavaThread* thread) {
|
||||
if (!UseBiasedLocking) {
|
||||
return;
|
||||
}
|
||||
GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
|
||||
if (deoptee_thread != thread) {
|
||||
// Process stack of deoptee thread as we will access oops during object deoptimization.
|
||||
StackWatermarkSet::start_processing(deoptee_thread, StackWatermarkKind::gc);
|
||||
}
|
||||
// Collect monitors but only those with eliminated locking.
|
||||
get_monitors_from_stack(objects_to_revoke, deoptee_thread, fr, map, true);
|
||||
|
||||
int len = objects_to_revoke->length();
|
||||
for (int i = 0; i < len; i++) {
|
||||
oop obj = (objects_to_revoke->at(i))();
|
||||
markWord mark = obj->mark();
|
||||
if (!mark.has_bias_pattern() ||
|
||||
mark.is_biased_anonymously() || // eliminated locking does not bias an object if it wasn't before
|
||||
!obj->klass()->prototype_header().has_bias_pattern() || // bulk revoke ignores eliminated monitors
|
||||
(obj->klass()->prototype_header().bias_epoch() != mark.bias_epoch())) { // bulk rebias ignores eliminated monitors
|
||||
// We reach here regularly if there's just eliminated locking on obj.
|
||||
// We must not call BiasedLocking::revoke_own_lock() in this case, as we
|
||||
// would hit assertions because it is a prerequisite that there has to be
|
||||
// non-eliminated locking on obj by deoptee_thread.
|
||||
// Luckily we don't have to revoke here because obj has to be a
|
||||
// non-escaping obj and can be relocked without revoking the bias. See
|
||||
// Deoptimization::relock_objects().
|
||||
continue;
|
||||
}
|
||||
BiasedLocking::revoke(objects_to_revoke->at(i), thread);
|
||||
assert(!objects_to_revoke->at(i)->mark().has_bias_pattern(), "biases should be revoked by now");
|
||||
}
|
||||
}
|
||||
|
||||
void Deoptimization::deoptimize_single_frame(JavaThread* thread, frame fr, Deoptimization::DeoptReason reason) {
|
||||
assert(fr.can_be_deoptimized(), "checking frame type");
|
||||
|
@ -2622,6 +2729,7 @@ void Deoptimization::print_statistics() {
|
|||
if (xtty != NULL) xtty->tail("statistics");
|
||||
}
|
||||
}
|
||||
|
||||
#else // COMPILER2_OR_JVMCI
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue