Skip to content

Commit 5b18558

Browse files
fiskreinrich
andcommitted
8255243: Reinforce escape barrier interactions with ZGC conc stack processing
Co-authored-by: Richard Reingruber <rrich@openjdk.org> Reviewed-by: rrich, sspitsyn
1 parent faf23de commit 5b18558

15 files changed

+196
-104
lines changed

src/hotspot/share/prims/jvmtiEnv.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,17 +1653,9 @@ JvmtiEnv::PopFrame(JavaThread* java_thread) {
16531653
// Eagerly reallocate scalar replaced objects.
16541654
JavaThread* current_thread = JavaThread::current();
16551655
EscapeBarrier eb(true, current_thread, java_thread);
1656-
if (eb.barrier_active()) {
1657-
if (java_thread->frames_to_pop_failed_realloc() > 0) {
1658-
// VM is in the process of popping the top frame because it has scalar replaced objects which
1659-
// could not be reallocated on the heap.
1660-
// Return JVMTI_ERROR_OUT_OF_MEMORY to avoid interfering with the VM.
1661-
return JVMTI_ERROR_OUT_OF_MEMORY;
1662-
}
1663-
if (!eb.deoptimize_objects(1)) {
1664-
// Reallocation of scalar replaced objects failed -> return with error
1665-
return JVMTI_ERROR_OUT_OF_MEMORY;
1666-
}
1656+
if (!eb.deoptimize_objects(1)) {
1657+
// Reallocation of scalar replaced objects failed -> return with error
1658+
return JVMTI_ERROR_OUT_OF_MEMORY;
16671659
}
16681660

16691661
MutexLocker mu(JvmtiThreadState_lock);

src/hotspot/share/prims/jvmtiEnvBase.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,17 +1378,9 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState
13781378
// Eagerly reallocate scalar replaced objects.
13791379
JavaThread* current_thread = JavaThread::current();
13801380
EscapeBarrier eb(true, current_thread, java_thread);
1381-
if (eb.barrier_active()) {
1382-
if (java_thread->frames_to_pop_failed_realloc() > 0) {
1383-
// VM is in the process of popping the top frame because it has scalar replaced objects
1384-
// which could not be reallocated on the heap.
1385-
// Return JVMTI_ERROR_OUT_OF_MEMORY to avoid interfering with the VM.
1386-
return JVMTI_ERROR_OUT_OF_MEMORY;
1387-
}
1388-
if (!eb.deoptimize_objects(0)) {
1389-
// Reallocation of scalar replaced objects failed -> return with error
1390-
return JVMTI_ERROR_OUT_OF_MEMORY;
1391-
}
1381+
if (!eb.deoptimize_objects(0)) {
1382+
// Reallocation of scalar replaced objects failed -> return with error
1383+
return JVMTI_ERROR_OUT_OF_MEMORY;
13921384
}
13931385

13941386
SetForceEarlyReturn op(state, value, tos);

src/hotspot/share/prims/jvmtiImpl.cpp

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -628,58 +628,12 @@ static bool can_be_deoptimized(vframe* vf) {
628628
return (vf->is_compiled_frame() && vf->fr().can_be_deoptimized());
629629
}
630630

631-
// Revert optimizations based on escape analysis if this is an access to a local object
632-
bool VM_GetOrSetLocal::deoptimize_objects(javaVFrame* jvf) {
633-
#if COMPILER2_OR_JVMCI
634-
assert(_type == T_OBJECT, "EscapeBarrier should not be active if _type != T_OBJECT");
635-
if (_depth < _thread->frames_to_pop_failed_realloc()) {
636-
// cannot access frame with failed reallocations
631+
bool VM_GetOrSetLocal::doit_prologue() {
632+
if (!_eb.deoptimize_objects(_depth, _depth)) {
633+
// The target frame is affected by a reallocation failure.
637634
_result = JVMTI_ERROR_OUT_OF_MEMORY;
638635
return false;
639636
}
640-
if (can_be_deoptimized(jvf)) {
641-
compiledVFrame* cf = compiledVFrame::cast(jvf);
642-
if (cf->has_ea_local_in_scope() && !_eb.deoptimize_objects(cf->fr().id())) {
643-
// reallocation of scalar replaced objects failed because heap is exhausted
644-
_result = JVMTI_ERROR_OUT_OF_MEMORY;
645-
return false;
646-
}
647-
}
648-
649-
// With this access the object could escape the thread changing its escape state from ArgEscape,
650-
// to GlobalEscape so we must deoptimize callers which could have optimized on the escape state.
651-
vframe* vf = jvf;
652-
do {
653-
// move to next physical frame
654-
while(!vf->is_top()) {
655-
vf = vf->sender();
656-
}
657-
vf = vf->sender();
658-
659-
if (vf != NULL && vf->is_compiled_frame()) {
660-
compiledVFrame* cvf = compiledVFrame::cast(vf);
661-
// Deoptimize objects if arg escape is being passed down the stack.
662-
// Note that deoptimizing the frame is not enough because objects need to be relocked
663-
if (cvf->arg_escape() && !_eb.deoptimize_objects(cvf->fr().id())) {
664-
// reallocation of scalar replaced objects failed because heap is exhausted
665-
_result = JVMTI_ERROR_OUT_OF_MEMORY;
666-
return false;
667-
}
668-
}
669-
} while(vf != NULL && !vf->is_entry_frame());
670-
#endif // COMPILER2_OR_JVMCI
671-
return true;
672-
}
673-
674-
bool VM_GetOrSetLocal::doit_prologue() {
675-
if (_eb.barrier_active()) {
676-
_jvf = get_java_vframe();
677-
NULL_CHECK(_jvf, false);
678-
679-
if (!deoptimize_objects(_jvf)) {
680-
return false;
681-
}
682-
}
683637

684638
return true;
685639
}

src/hotspot/share/prims/jvmtiImpl.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,6 @@ class VM_GetOrSetLocal : public VM_Operation {
334334
javaVFrame* get_java_vframe();
335335
bool check_slot_type_lvt(javaVFrame* vf);
336336
bool check_slot_type_no_lvt(javaVFrame* vf);
337-
bool deoptimize_objects(javaVFrame* vf);
338337

339338
public:
340339
// Constructor for non-object getter

src/hotspot/share/runtime/deoptimization.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,6 @@ static bool eliminate_allocations(JavaThread* thread, int exec_mode, CompiledMet
218218
Thread* THREAD = thread;
219219
// Clear pending OOM if reallocation fails and return true indicating allocation failure
220220
realloc_failures = Deoptimization::realloc_objects(thread, &deoptee, &map, objects, CHECK_AND_CLEAR_(true));
221-
// Make sure the deoptee frame gets processed after a potential safepoint during
222-
// object reallocation. This is necessary because (a) deoptee_thread can be
223-
// different from the current thread and (b) the deoptee frame does not need to be
224-
// the top frame.
225-
StackWatermarkSet::finish_processing(deoptee_thread, NULL /* context */, StackWatermarkKind::gc);
226221
deoptimized_objects = true;
227222
} else {
228223
JRT_BLOCK

src/hotspot/share/runtime/escapeBarrier.cpp

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "runtime/handles.hpp"
3434
#include "runtime/handshake.hpp"
3535
#include "runtime/interfaceSupport.inline.hpp"
36+
#include "runtime/keepStackGCProcessed.hpp"
3637
#include "runtime/mutexLocker.hpp"
3738
#include "runtime/registerMap.hpp"
3839
#include "runtime/stackValue.hpp"
@@ -62,29 +63,37 @@ bool EscapeBarrier::objs_are_deoptimized(JavaThread* thread, intptr_t* fr_id) {
6263
return result;
6364
}
6465

65-
// Object references of frames up to the given depth are about to be
66-
// accessed. Frames with optimizations based on escape state that is potentially
67-
// changed by the accesses need to be deoptimized and the referenced objects
68-
// need to be reallocated and relocked. Up to depth this is done for frames
69-
// with not escaping objects in scope. For deeper frames it is done only if
70-
// they pass not escaping objects as arguments because they potentially escape
71-
// from callee frames within the given depth.
72-
// The search for deeper frames is ended if an entry frame is found because
73-
// arguments to native methods are considered to escape globally.
74-
bool EscapeBarrier::deoptimize_objects(int depth) {
75-
if (barrier_active() && deoptee_thread()->has_last_Java_frame()) {
66+
// Deoptimize objects of frames of the target thread at depth >= d1 and depth <= d2.
67+
// Deoptimize objects of caller frames if they passed references to ArgEscape objects as arguments.
68+
// Return false in the case of a reallocation failure and true otherwise.
69+
bool EscapeBarrier::deoptimize_objects(int d1, int d2) {
70+
if (!barrier_active()) return true;
71+
if (d1 < deoptee_thread()->frames_to_pop_failed_realloc()) {
72+
// The deoptee thread has frames with reallocation failures on top of its stack.
73+
// These frames are about to be removed. We must not interfere with that and signal failure.
74+
return false;
75+
}
76+
if (deoptee_thread()->has_last_Java_frame()) {
7677
assert(calling_thread() == Thread::current(), "should be");
78+
KeepStackGCProcessedMark ksgcpm(deoptee_thread());
7779
ResourceMark rm(calling_thread());
7880
HandleMark hm(calling_thread());
7981
RegisterMap reg_map(deoptee_thread(), false /* update_map */, false /* process_frames */);
8082
vframe* vf = deoptee_thread()->last_java_vframe(&reg_map);
8183
int cur_depth = 0;
82-
while (vf != NULL && ((cur_depth <= depth) || !vf->is_entry_frame())) {
84+
85+
// Skip frames at depth < d1
86+
while (vf != NULL && cur_depth < d1) {
87+
cur_depth++;
88+
vf = vf->sender();
89+
}
90+
91+
while (vf != NULL && ((cur_depth <= d2) || !vf->is_entry_frame())) {
8392
if (vf->is_compiled_frame()) {
8493
compiledVFrame* cvf = compiledVFrame::cast(vf);
8594
// Deoptimize frame and local objects if any exist.
8695
// If cvf is deeper than depth, then we deoptimize iff local objects are passed as args.
87-
bool should_deopt = cur_depth <= depth ? cvf->has_ea_local_in_scope() : cvf->arg_escape();
96+
bool should_deopt = cur_depth <= d2 ? cvf->has_ea_local_in_scope() : cvf->arg_escape();
8897
if (should_deopt && !deoptimize_objects(cvf->fr().id())) {
8998
// reallocation of scalar replaced objects failed because heap is exhausted
9099
return false;
@@ -109,7 +118,13 @@ bool EscapeBarrier::deoptimize_objects_all_threads() {
109118
if (!barrier_active()) return true;
110119
ResourceMark rm(calling_thread());
111120
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
121+
if (jt->frames_to_pop_failed_realloc() > 0) {
122+
// The deoptee thread jt has frames with reallocation failures on top of its stack.
123+
// These frames are about to be removed. We must not interfere with that and signal failure.
124+
return false;
125+
}
112126
if (jt->has_last_Java_frame()) {
127+
KeepStackGCProcessedMark ksgcpm(jt);
113128
RegisterMap reg_map(jt, false /* update_map */, false /* process_frames */);
114129
vframe* vf = jt->last_java_vframe(&reg_map);
115130
assert(jt->frame_anchor()->walkable(),
@@ -297,7 +312,7 @@ static void set_objs_are_deoptimized(JavaThread* thread, intptr_t* fr_id) {
297312
// frame is replaced with interpreter frames. Returns false iff at least one
298313
// reallocation failed.
299314
bool EscapeBarrier::deoptimize_objects_internal(JavaThread* deoptee, intptr_t* fr_id) {
300-
if (!barrier_active()) return true;
315+
assert(barrier_active(), "should not call");
301316

302317
JavaThread* ct = calling_thread();
303318
bool realloc_failures = false;
@@ -307,11 +322,7 @@ bool EscapeBarrier::deoptimize_objects_internal(JavaThread* deoptee, intptr_t* f
307322
compiledVFrame* last_cvf;
308323
bool fr_is_deoptimized;
309324
do {
310-
if (!self_deopt()) {
311-
// Process stack of deoptee thread as we will access oops during object deoptimization.
312-
StackWatermarkSet::start_processing(deoptee, StackWatermarkKind::gc);
313-
}
314-
StackFrameStream fst(deoptee, true /* update */, true /* process_frames */);
325+
StackFrameStream fst(deoptee, true /* update */, false /* process_frames */);
315326
while (fst.current()->id() != fr_id && !fst.is_done()) {
316327
fst.next();
317328
}

src/hotspot/share/runtime/escapeBarrier.hpp

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ class EscapeBarrier : StackObj {
6161
// Deoptimize the given frame and deoptimize objects with optimizations based on escape analysis.
6262
bool deoptimize_objects_internal(JavaThread* deoptee, intptr_t* fr_id);
6363

64+
// Deoptimize objects, i.e. reallocate and relock them. The target frames are deoptimized.
65+
// The methods return false iff at least one reallocation failed.
66+
bool deoptimize_objects(intptr_t* fr_id) {
67+
return deoptimize_objects_internal(deoptee_thread(), fr_id);
68+
}
69+
6470
public:
6571
// Revert ea based optimizations for given deoptee thread
6672
EscapeBarrier(bool barrier_active, JavaThread* calling_thread, JavaThread* deoptee_thread)
@@ -89,13 +95,17 @@ class EscapeBarrier : StackObj {
8995
bool barrier_active() const { return false; }
9096
#endif // COMPILER2_OR_JVMCI
9197

92-
// Deoptimize objects, i.e. reallocate and relock them. The target frames are deoptimized.
93-
// The methods return false iff at least one reallocation failed.
94-
bool deoptimize_objects(intptr_t* fr_id) {
95-
return true COMPILER2_OR_JVMCI_PRESENT(&& deoptimize_objects_internal(deoptee_thread(), fr_id));
98+
// Deoptimize objects of frames of the target thread up to the given depth.
99+
// Deoptimize objects of caller frames if they passed references to ArgEscape objects as arguments.
100+
// Return false in the case of a reallocation failure and true otherwise.
101+
bool deoptimize_objects(int depth) {
102+
return deoptimize_objects(0, depth);
96103
}
97104

98-
bool deoptimize_objects(int depth) NOT_COMPILER2_OR_JVMCI_RETURN_(true);
105+
// Deoptimize objects of frames of the target thread at depth >= d1 and depth <= d2.
106+
// Deoptimize objects of caller frames if they passed references to ArgEscape objects as arguments.
107+
// Return false in the case of a reallocation failure and true otherwise.
108+
bool deoptimize_objects(int d1, int d2) NOT_COMPILER2_OR_JVMCI_RETURN_(true);
99109

100110
// Find and deoptimize non escaping objects and the holding frames on all stacks.
101111
bool deoptimize_objects_all_threads() NOT_COMPILER2_OR_JVMCI_RETURN_(true);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#include "precompiled.hpp"
26+
#include "runtime/safepoint.hpp"
27+
#include "runtime/stackWatermark.inline.hpp"
28+
#include "runtime/stackWatermarkSet.inline.hpp"
29+
#include "runtime/keepStackGCProcessed.hpp"
30+
31+
KeepStackGCProcessedMark::KeepStackGCProcessedMark(JavaThread* jt) :
32+
_active(true),
33+
_jt(jt) {
34+
finish_processing();
35+
if (!Thread::current()->is_Java_thread()) {
36+
assert(SafepointSynchronize::is_at_safepoint() && Thread::current()->is_VM_thread(),
37+
"must be either Java thread or VM thread in a safepoint");
38+
_active = false;
39+
return;
40+
}
41+
StackWatermark* our_watermark = StackWatermarkSet::get(JavaThread::current(), StackWatermarkKind::gc);
42+
if (our_watermark == NULL) {
43+
_active = false;
44+
return;
45+
}
46+
StackWatermark* their_watermark = StackWatermarkSet::get(jt, StackWatermarkKind::gc);
47+
our_watermark->link_watermark(their_watermark);
48+
}
49+
50+
KeepStackGCProcessedMark::~KeepStackGCProcessedMark() {
51+
if (!_active) {
52+
return;
53+
}
54+
StackWatermark* our_watermark = StackWatermarkSet::get(JavaThread::current(), StackWatermarkKind::gc);
55+
our_watermark->link_watermark(NULL);
56+
}
57+
58+
void KeepStackGCProcessedMark::finish_processing() {
59+
StackWatermarkSet::finish_processing(_jt, NULL /* context */, StackWatermarkKind::gc);
60+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#ifndef SHARE_RUNTIME_KEEPSTACKGCPROCESSED_HPP
26+
#define SHARE_RUNTIME_KEEPSTACKGCPROCESSED_HPP
27+
28+
#include "memory/allocation.hpp"
29+
#include "runtime/stackWatermark.hpp"
30+
#include "runtime/stackWatermarkKind.hpp"
31+
#include "runtime/stackWatermarkSet.hpp"
32+
33+
// Use this class to mark a remote thread you are currently interested
34+
// in examining the entire stack, without it slipping into an unprocessed
35+
// state at safepoint polls.
36+
class KeepStackGCProcessedMark : public StackObj {
37+
friend class StackWatermark;
38+
bool _active;
39+
JavaThread* _jt;
40+
41+
void finish_processing();
42+
43+
public:
44+
KeepStackGCProcessedMark(JavaThread* jt);
45+
~KeepStackGCProcessedMark();
46+
};
47+
48+
49+
#endif // SHARE_RUNTIME_KEEPSTACKGCPROCESSED_HPP

src/hotspot/share/runtime/safepointMechanism.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ void SafepointMechanism::process(JavaThread *thread) {
8383
SafepointSynchronize::block(thread); // Recursive
8484
}
8585

86-
// The call to start_processing fixes the thread's oops and the first few frames.
86+
// The call to on_safepoint fixes the thread's oops and the first few frames.
8787
//
8888
// The call has been carefully placed here to cater for a few situations:
8989
// 1) After we exit from block after a global poll
9090
// 2) After a thread races with the disarming of the global poll and transitions from native/blocked
9191
// 3) Before the handshake code is run
92-
StackWatermarkSet::start_processing(thread, StackWatermarkKind::gc);
92+
StackWatermarkSet::on_safepoint(thread);
9393

9494
if (thread->handshake_state()->should_process()) {
9595
thread->handshake_state()->process_by_self();

0 commit comments

Comments
 (0)