Skip to content

Commit 4440bda

Browse files
committedSep 24, 2020
8219586: CodeHeap State Analytics processes dead nmethods
Reviewed-by: thartmann, eosterlund
1 parent 154b8cf commit 4440bda

File tree

8 files changed

+213
-155
lines changed

8 files changed

+213
-155
lines changed
 

‎src/hotspot/share/code/codeBlob.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la
9999
// probably wrong for tiered
100100
assert(_frame_size >= -1, "must use frame size or -1 for runtime stubs");
101101
#endif // COMPILER1
102+
S390_ONLY(_ctable_offset = 0;) // avoid uninitialized fields
102103
}
103104

104105
CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& layout, CodeBuffer* cb, int frame_complete_offset, int frame_size, OopMapSet* oop_maps, bool caller_must_gc_arguments) :
@@ -128,6 +129,7 @@ CodeBlob::CodeBlob(const char* name, CompilerType type, const CodeBlobLayout& la
128129
// probably wrong for tiered
129130
assert(_frame_size >= -1, "must use frame size or -1 for runtime stubs");
130131
#endif // COMPILER1
132+
S390_ONLY(_ctable_offset = 0;) // avoid uninitialized fields
131133
}
132134

133135

‎src/hotspot/share/code/codeHeapState.cpp

+137-107
Large diffs are not rendered by default.

‎src/hotspot/share/code/codeHeapState.hpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class CodeHeapState : public CHeapObj<mtCode> {
4747
// The nMethod_* values correspond to the CompiledMethod enum values.
4848
// We can't use the CompiledMethod values 1:1 because we depend on noType == 0.
4949
nMethod_inconstruction, // under construction. Very soon, the type will transition to "in_use".
50+
// can't be observed while holding Compile_lock and CodeCache_lock simultaneously.
51+
// left in here for completeness (and to document we spent a thought).
5052
nMethod_inuse, // executable. This is the "normal" state for a nmethod.
5153
nMethod_notused, // assumed inactive, marked not entrant. Could be revived if necessary.
5254
nMethod_notentrant, // no new activations allowed, marked for deoptimization. Old activations may still exist.
@@ -95,7 +97,9 @@ class CodeHeapState : public CHeapObj<mtCode> {
9597
static void print_line_delim(outputStream* out, bufferedStream *sst, char* low_bound, unsigned int ix, unsigned int gpl);
9698
static void print_line_delim(outputStream* out, outputStream *sst, char* low_bound, unsigned int ix, unsigned int gpl);
9799
static blobType get_cbType(CodeBlob* cb);
98-
static bool blob_access_is_safe(CodeBlob* this_blob, CodeBlob* prev_blob);
100+
static bool blob_access_is_safe(CodeBlob* this_blob);
101+
static bool nmethod_access_is_safe(nmethod* nm);
102+
static bool holding_required_locks();
99103

100104
public:
101105
static void discard(outputStream* out, CodeHeap* heap);
@@ -164,12 +168,16 @@ struct FreeBlk : public CHeapObj<mtCode> {
164168
// know about those largest blocks.
165169
// All TopSizeBlks of a heap segment are stored in the related TopSizeArray.
166170
struct TopSizeBlk : public CHeapObj<mtCode> {
167-
HeapBlock* start; // address of block
171+
HeapBlock* start; // address of block
172+
const char* blob_name; // name of blob (mostly: name_and_sig of nmethod)
168173
unsigned int len; // length of block, in _segment_size units. Will never overflow int.
169174

170175
unsigned int index; // ordering index, 0 is largest block
171176
// contains array index of next smaller block
172177
// -1 indicates end of list
178+
179+
unsigned int nm_size; // nmeethod total size (if nmethod, 0 otherwise)
180+
int temperature; // nmethod temperature (if nmethod, 0 otherwise)
173181
CompLevel level; // optimization level (see globalDefinitions.hpp)
174182
u2 compiler; // compiler which generated this blob
175183
u2 type; // blob type
@@ -216,7 +224,6 @@ struct CodeHeapStat {
216224
unsigned int nBlocks_t2;
217225
unsigned int nBlocks_alive;
218226
unsigned int nBlocks_dead;
219-
unsigned int nBlocks_inconstr;
220227
unsigned int nBlocks_unloaded;
221228
unsigned int nBlocks_stub;
222229
// FreeBlk data

‎src/hotspot/share/code/compiledMethod.cpp

+7-15
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ CompiledMethod::CompiledMethod(Method* method, const char* name, CompilerType ty
6969
}
7070

7171
void CompiledMethod::init_defaults() {
72+
{ // avoid uninitialized fields, even for short time periods
73+
_is_far_code = false;
74+
_scopes_data_begin = NULL;
75+
_deopt_handler_begin = NULL;
76+
_deopt_mh_handler_begin = NULL;
77+
_exception_cache = NULL;
78+
}
7279
_has_unsafe_access = 0;
7380
_has_method_handle_invokes = 0;
7481
_lazy_critical_native = 0;
@@ -692,21 +699,6 @@ bool CompiledMethod::cleanup_inline_caches_impl(bool unloading_occurred, bool cl
692699
return true;
693700
}
694701

695-
// Iterating over all nmethods, e.g. with the help of CodeCache::nmethods_do(fun) was found
696-
// to not be inherently safe. There is a chance that fields are seen which are not properly
697-
// initialized. This happens despite the fact that nmethods_do() asserts the CodeCache_lock
698-
// to be held.
699-
// To bundle knowledge about necessary checks in one place, this function was introduced.
700-
// It is not claimed that these checks are sufficient, but they were found to be necessary.
701-
bool CompiledMethod::nmethod_access_is_safe(nmethod* nm) {
702-
Method* method = (nm == NULL) ? NULL : nm->method(); // nm->method() may be uninitialized, i.e. != NULL, but invalid
703-
return (nm != NULL) && (method != NULL) && (method->signature() != NULL) &&
704-
!nm->is_zombie() && !nm->is_not_installed() &&
705-
os::is_readable_pointer(method) &&
706-
os::is_readable_pointer(method->constants()) &&
707-
os::is_readable_pointer(method->signature());
708-
}
709-
710702
address CompiledMethod::continuation_for_implicit_exception(address pc, bool for_div0_check) {
711703
// Exception happened outside inline-cache check code => we are inside
712704
// an active nmethod => use cpc to determine a return address

‎src/hotspot/share/code/compiledMethod.hpp

-2
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,6 @@ class CompiledMethod : public CodeBlob {
254254
return _mark_for_deoptimization_status != deoptimize_noupdate;
255255
}
256256

257-
static bool nmethod_access_is_safe(nmethod* nm);
258-
259257
// tells whether frames described by this nmethod can be deoptimized
260258
// note: native wrappers cannot be deoptimized.
261259
bool can_be_deoptimized() const { return is_java_method(); }

‎src/hotspot/share/compiler/compileBroker.cpp

+47-27
Original file line numberDiff line numberDiff line change
@@ -2764,42 +2764,59 @@ void CompileBroker::print_heapinfo(outputStream* out, const char* function, size
27642764
}
27652765

27662766
// We hold the CodeHeapStateAnalytics_lock all the time, from here until we leave this function.
2767-
// That prevents another thread from destroying our view on the CodeHeap.
2767+
// That prevents other threads from destroying (making inconsistent) our view on the CodeHeap.
27682768
// When we request individual parts of the analysis via the jcmd interface, it is possible
27692769
// that in between another thread (another jcmd user or the vm running into CodeCache OOM)
2770-
// updated the aggregated data. That's a tolerable tradeoff because we can't hold a lock
2771-
// across user interaction.
2772-
// Acquire this lock before acquiring the CodeCache_lock.
2773-
// CodeHeapStateAnalytics_lock could be held by a concurrent thread for a long time,
2774-
// leading to an unnecessarily long hold time of the CodeCache_lock.
2770+
// updated the aggregated data. We will then see a modified, but again consistent, view
2771+
// on the CodeHeap. That's a tolerable tradeoff we have to accept because we can't hold
2772+
// a lock across user interaction.
2773+
2774+
// We should definitely acquire this lock before acquiring Compile_lock and CodeCache_lock.
2775+
// CodeHeapStateAnalytics_lock may be held by a concurrent thread for a long time,
2776+
// leading to an unnecessarily long hold time of the other locks we acquired before.
27752777
ts.update(); // record starting point
2776-
MutexLocker mu1(CodeHeapStateAnalytics_lock, Mutex::_no_safepoint_check_flag);
2778+
MutexLocker mu0(CodeHeapStateAnalytics_lock, Mutex::_safepoint_check_flag);
27772779
out->print_cr("\n__ CodeHeapStateAnalytics lock wait took %10.3f seconds _________\n", ts.seconds());
27782780

2779-
// If we serve an "allFun" call, it is beneficial to hold the CodeCache_lock
2780-
// for the entire duration of aggregation and printing. That makes sure
2781-
// we see a consistent picture and do not run into issues caused by
2782-
// the CodeHeap being altered concurrently.
2783-
Mutex* global_lock = allFun ? CodeCache_lock : NULL;
2784-
Mutex* function_lock = allFun ? NULL : CodeCache_lock;
2781+
// Holding the CodeCache_lock protects from concurrent alterations of the CodeCache.
2782+
// Unfortunately, such protection is not sufficient:
2783+
// When a new nmethod is created via ciEnv::register_method(), the
2784+
// Compile_lock is taken first. After some initializations,
2785+
// nmethod::new_nmethod() takes over, grabbing the CodeCache_lock
2786+
// immediately (after finalizing the oop references). To lock out concurrent
2787+
// modifiers, we have to grab both locks as well in the described sequence.
2788+
//
2789+
// If we serve an "allFun" call, it is beneficial to hold CodeCache_lock and Compile_lock
2790+
// for the entire duration of aggregation and printing. That makes sure we see
2791+
// a consistent picture and do not run into issues caused by concurrent alterations.
2792+
bool should_take_Compile_lock = !SafepointSynchronize::is_at_safepoint() &&
2793+
!Compile_lock->owned_by_self();
2794+
bool should_take_CodeCache_lock = !SafepointSynchronize::is_at_safepoint() &&
2795+
!CodeCache_lock->owned_by_self();
2796+
Mutex* global_lock_1 = allFun ? (should_take_Compile_lock ? Compile_lock : NULL) : NULL;
2797+
Monitor* global_lock_2 = allFun ? (should_take_CodeCache_lock ? CodeCache_lock : NULL) : NULL;
2798+
Mutex* function_lock_1 = allFun ? NULL : (should_take_Compile_lock ? Compile_lock : NULL);
2799+
Monitor* function_lock_2 = allFun ? NULL : (should_take_CodeCache_lock ? CodeCache_lock : NULL);
27852800
ts_global.update(); // record starting point
2786-
MutexLocker mu2(global_lock, Mutex::_no_safepoint_check_flag);
2787-
if (global_lock != NULL) {
2788-
out->print_cr("\n__ CodeCache (global) lock wait took %10.3f seconds _________\n", ts_global.seconds());
2801+
MutexLocker mu1(global_lock_1, Mutex::_safepoint_check_flag);
2802+
MutexLocker mu2(global_lock_2, Mutex::_no_safepoint_check_flag);
2803+
if ((global_lock_1 != NULL) || (global_lock_2 != NULL)) {
2804+
out->print_cr("\n__ Compile & CodeCache (global) lock wait took %10.3f seconds _________\n", ts_global.seconds());
27892805
ts_global.update(); // record starting point
27902806
}
27912807

27922808
if (aggregate) {
27932809
ts.update(); // record starting point
2794-
MutexLocker mu3(function_lock, Mutex::_no_safepoint_check_flag);
2795-
if (function_lock != NULL) {
2796-
out->print_cr("\n__ CodeCache (function) lock wait took %10.3f seconds _________\n", ts.seconds());
2810+
MutexLocker mu11(function_lock_1, Mutex::_safepoint_check_flag);
2811+
MutexLocker mu22(function_lock_2, Mutex::_no_safepoint_check_flag);
2812+
if ((function_lock_1 != NULL) || (function_lock_1 != NULL)) {
2813+
out->print_cr("\n__ Compile & CodeCache (function) lock wait took %10.3f seconds _________\n", ts.seconds());
27972814
}
27982815

27992816
ts.update(); // record starting point
28002817
CodeCache::aggregate(out, granularity);
2801-
if (function_lock != NULL) {
2802-
out->print_cr("\n__ CodeCache (function) lock hold took %10.3f seconds _________\n", ts.seconds());
2818+
if ((function_lock_1 != NULL) || (function_lock_1 != NULL)) {
2819+
out->print_cr("\n__ Compile & CodeCache (function) lock hold took %10.3f seconds _________\n", ts.seconds());
28032820
}
28042821
}
28052822

@@ -2809,15 +2826,18 @@ void CompileBroker::print_heapinfo(outputStream* out, const char* function, size
28092826
if (methodSpace) CodeCache::print_space(out);
28102827
if (methodAge) CodeCache::print_age(out);
28112828
if (methodNames) {
2812-
// print_names() has shown to be sensitive to concurrent CodeHeap modifications.
2813-
// Therefore, request the CodeCache_lock before calling...
2814-
MutexLocker mu3(function_lock, Mutex::_no_safepoint_check_flag);
2815-
CodeCache::print_names(out);
2829+
if (allFun) {
2830+
// print_names() can only be used safely if the locks have been continuously held
2831+
// since aggregation begin. That is true only for function "all".
2832+
CodeCache::print_names(out);
2833+
} else {
2834+
out->print_cr("\nCodeHeapStateAnalytics: Function 'MethodNames' is only available as part of function 'all'");
2835+
}
28162836
}
28172837
if (discard) CodeCache::discard(out);
28182838

2819-
if (global_lock != NULL) {
2820-
out->print_cr("\n__ CodeCache (global) lock hold took %10.3f seconds _________\n", ts_global.seconds());
2839+
if ((global_lock_1 != NULL) || (global_lock_2 != NULL)) {
2840+
out->print_cr("\n__ Compile & CodeCache (global) lock hold took %10.3f seconds _________\n", ts_global.seconds());
28212841
}
28222842
out->print_cr("\n__ CodeHeapStateAnalytics total duration %10.3f seconds _________\n", ts_total.seconds());
28232843
}

‎src/hotspot/share/memory/heap.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_s
205205
assert(rs.size() >= committed_size, "reserved < committed");
206206
assert(segment_size >= sizeof(FreeBlock), "segment size is too small");
207207
assert(is_power_of_2(segment_size), "segment_size must be a power of 2");
208+
assert_locked_or_safepoint(CodeCache_lock);
208209

209210
_segment_size = segment_size;
210211
_log2_segment_size = exact_log2(segment_size);
@@ -253,6 +254,8 @@ bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_s
253254

254255

255256
bool CodeHeap::expand_by(size_t size) {
257+
assert_locked_or_safepoint(CodeCache_lock);
258+
256259
// expand _memory space
257260
size_t dm = align_to_page_size(_memory.committed_size() + size) - _memory.committed_size();
258261
if (dm > 0) {
@@ -283,6 +286,7 @@ bool CodeHeap::expand_by(size_t size) {
283286
void* CodeHeap::allocate(size_t instance_size) {
284287
size_t number_of_segments = size_to_segments(instance_size + header_size());
285288
assert(segments_to_size(number_of_segments) >= sizeof(FreeBlock), "not enough room for FreeList");
289+
assert_locked_or_safepoint(CodeCache_lock);
286290

287291
// First check if we can satisfy request from freelist
288292
NOT_PRODUCT(verify());
@@ -347,6 +351,8 @@ HeapBlock* CodeHeap::split_block(HeapBlock *b, size_t split_at) {
347351

348352
void CodeHeap::deallocate_tail(void* p, size_t used_size) {
349353
assert(p == find_start(p), "illegal deallocation");
354+
assert_locked_or_safepoint(CodeCache_lock);
355+
350356
// Find start of HeapBlock
351357
HeapBlock* b = (((HeapBlock *)p) - 1);
352358
assert(b->allocated_space() == p, "sanity check");
@@ -363,6 +369,8 @@ void CodeHeap::deallocate_tail(void* p, size_t used_size) {
363369

364370
void CodeHeap::deallocate(void* p) {
365371
assert(p == find_start(p), "illegal deallocation");
372+
assert_locked_or_safepoint(CodeCache_lock);
373+
366374
// Find start of HeapBlock
367375
HeapBlock* b = (((HeapBlock *)p) - 1);
368376
assert(b->allocated_space() == p, "sanity check");
@@ -790,6 +798,7 @@ void CodeHeap::print() {
790798

791799
void CodeHeap::verify() {
792800
if (VerifyCodeCache) {
801+
assert_locked_or_safepoint(CodeCache_lock);
793802
size_t len = 0;
794803
int count = 0;
795804
for(FreeBlock* b = _freelist; b != NULL; b = b->link()) {

‎src/hotspot/share/runtime/mutexLocker.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ void mutex_init() {
332332
def(UnsafeJlong_lock , PaddedMutex , special, false, _safepoint_check_never);
333333
#endif
334334

335-
def(CodeHeapStateAnalytics_lock , PaddedMutex , leaf, true, _safepoint_check_never);
335+
def(CodeHeapStateAnalytics_lock , PaddedMutex , nonleaf+6, false, _safepoint_check_always);
336336
def(NMethodSweeperStats_lock , PaddedMutex , special, true, _safepoint_check_never);
337337
def(ThreadsSMRDelete_lock , PaddedMonitor, special, true, _safepoint_check_never);
338338
def(ThreadIdTableCreate_lock , PaddedMutex , leaf, false, _safepoint_check_always);

0 commit comments

Comments
 (0)
Please sign in to comment.