Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

8335409: Can't allocate and retain memory from resource area in frame::oops_interpreted_do oop closure after 8329665 #20012

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 16 additions & 21 deletions src/hotspot/share/interpreter/oopMapCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ class OopMapCacheEntry: private InterpreterOopMap {
public:
OopMapCacheEntry() : InterpreterOopMap() {
_next = nullptr;
#ifdef ASSERT
_resource_allocate_bit_mask = false;
#endif
}
};

Expand Down Expand Up @@ -178,10 +175,18 @@ class VerifyClosure : public OffsetClosure {
InterpreterOopMap::InterpreterOopMap() {
initialize();
#ifdef ASSERT
_resource_allocate_bit_mask = true;
_used = false;
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit pre-existing: use of DEBUG_ONLY would be more consistent with later setting of _used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

}

InterpreterOopMap::~InterpreterOopMap() {
if (mask_size() > small_mask_limit) {
assert(!Thread::current()->resource_area()->contains((void*)_bit_mask[0]),
"The bit mask should be allocated from the C heap");
Copy link
Member

@tstuefe tstuefe Jul 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguably, this assert is not needed. In debug builds, we have NMT enabled, and that does a check on os::free.

However, an assert that _bit_mask[0] != 0 would make sense, since the free quielty swallows null pointers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. We could had such case if the mask was never filled due to invalid bci, so I also improved the conditional.

FREE_C_HEAP_ARRAY(uintptr_t, _bit_mask[0]);
}
}

bool InterpreterOopMap::is_empty() const {
bool result = _method == nullptr;
assert(_method != nullptr || (_bci == 0 &&
Expand Down Expand Up @@ -400,10 +405,10 @@ void OopMapCacheEntry::deallocate(OopMapCacheEntry* const entry) {
// Implementation of OopMapCache

void InterpreterOopMap::resource_copy(OopMapCacheEntry* from) {
assert(_resource_allocate_bit_mask,
"Should not resource allocate the _bit_mask");
assert(from->has_valid_mask(),
"Cannot copy entry with an invalid mask");
// The expectation is that this InterpreterOopMap is a recently created
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/is a recently/is recently/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// and empty. It is used to get a copy of a cached entry.
assert(!_used, "InterpreterOopMap object can only be filled once");
assert(from->has_valid_mask(), "Cannot copy entry with an invalid mask");

set_method(from->method());
set_bci(from->bci());
Expand All @@ -416,21 +421,11 @@ void InterpreterOopMap::resource_copy(OopMapCacheEntry* from) {
memcpy((void *)_bit_mask, (void *)from->_bit_mask,
mask_word_size() * BytesPerWord);
} else {
// The expectation is that this InterpreterOopMap is a recently created
// and empty. It is used to get a copy of a cached entry.
// If the bit mask has a value, it should be in the
// resource area.
assert(_bit_mask[0] == 0 ||
Thread::current()->resource_area()->contains((void*)_bit_mask[0]),
"The bit mask should have been allocated from a resource area");
// Allocate the bit_mask from a Resource area for performance. Allocating
// from the C heap as is done for OopMapCache has a significant
// performance impact.
_bit_mask[0] = (uintptr_t) NEW_RESOURCE_ARRAY(uintptr_t, mask_word_size());
_bit_mask[0] = (uintptr_t) NEW_C_HEAP_ARRAY(uintptr_t, mask_word_size(), mtClass);
assert(_bit_mask[0] != 0, "bit mask was not allocated");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assert can be removed, no? NEW_C_HEAP_ARRAY does a null check by default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, removed.

memcpy((void*) _bit_mask[0], (void*) from->_bit_mask[0],
mask_word_size() * BytesPerWord);
memcpy((void*) _bit_mask[0], (void*) from->_bit_mask[0], mask_word_size() * BytesPerWord);
}
DEBUG_ONLY(_used = true);
}

inline unsigned int OopMapCache::hash_value_for(const methodHandle& method, int bci) const {
Expand Down
12 changes: 7 additions & 5 deletions src/hotspot/share/interpreter/oopMapCache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@
// OopMapCache's are allocated lazily per InstanceKlass.

// The oopMap (InterpreterOopMap) is stored as a bit mask. If the
// bit_mask can fit into two words it is stored in
// bit_mask can fit into four words it is stored in
// the _bit_mask array, otherwise it is allocated on the heap.
// For OopMapCacheEntry the bit_mask is allocated in the C heap
// because these entries persist between garbage collections.
// For InterpreterOopMap the bit_mask is allocated in
// a resource area for better performance. InterpreterOopMap
// For InterpreterOopMap the bit_mask is allocated in the C heap
// to avoid issues with allocations from the resource area that have
// to live accross the oop closure (see 8335409). InterpreterOopMap
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't usually put bug numbers in the code and after this change nobody will want to move this back to resource area, so putting the bug number as a caution shouldn't be needed. If one wants to know the details, they can git blame this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

// should only be created and deleted during same garbage collection.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add 'the' to "during the same garbage collection."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

//
// If ENABBLE_ZAP_DEAD_LOCALS is defined, two bits are used
Expand Down Expand Up @@ -89,7 +90,7 @@ class InterpreterOopMap: ResourceObj {

protected:
#ifdef ASSERT
bool _resource_allocate_bit_mask;
bool _used;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you make this a DEBUG_ONLY() too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

#endif
int _num_oops;
intptr_t _bit_mask[N]; // the bit mask if
Expand Down Expand Up @@ -128,11 +129,12 @@ class InterpreterOopMap: ResourceObj {

public:
InterpreterOopMap();
~InterpreterOopMap();

// Copy the OopMapCacheEntry in parameter "from" into this
// InterpreterOopMap. If the _bit_mask[0] in "from" points to
// allocated space (i.e., the bit mask was to large to hold
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit pre-existing: s/to/too/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// in-line), allocate the space from a Resource area.
// in-line), allocate the space from the C heap.
void resource_copy(OopMapCacheEntry* from);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name resource_copy seems somewhat of a misnomer given it may be C heap. Is it worth changing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this should probably be copy_from, and rename the parameter src. Or something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also thought about renaming it but ended up leaving it as is in v1. I changed it to Coleen's suggestion.


void iterate_oop(OffsetClosure* oop_closure) const;
Expand Down
1 change: 0 additions & 1 deletion src/hotspot/share/runtime/frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,6 @@ void frame::oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool quer
InterpreterFrameClosure blk(this, max_locals, m->max_stack(), f);

// process locals & expression stack
ResourceMark rm(thread);
InterpreterOopMap mask;
if (query_oop_map_cache) {
m->mask_for(m, bci, &mask);
Expand Down