< prev index next >
src/hotspot/share/memory/metaspace/virtualSpaceList.cpp
Print this page
rev 57511 : [mq]: metaspace-improvement
*** 22,447 ****
*
*/
#include "precompiled.hpp"
- #include "logging/log.hpp"
- #include "logging/logStream.hpp"
#include "memory/metaspace.hpp"
#include "memory/metaspace/chunkManager.hpp"
! #include "memory/metaspace/metachunk.hpp"
! #include "memory/metaspace/metaspaceCommon.hpp"
#include "memory/metaspace/virtualSpaceList.hpp"
#include "memory/metaspace/virtualSpaceNode.hpp"
- #include "runtime/orderAccess.hpp"
#include "runtime/mutexLocker.hpp"
! #include "runtime/safepoint.hpp"
namespace metaspace {
VirtualSpaceList::~VirtualSpaceList() {
! VirtualSpaceListIterator iter(virtual_space_list());
! while (iter.repeat()) {
! VirtualSpaceNode* vsl = iter.get_next();
! delete vsl;
}
}
! void VirtualSpaceList::inc_reserved_words(size_t v) {
! assert_lock_strong(MetaspaceExpand_lock);
! _reserved_words = _reserved_words + v;
! }
! void VirtualSpaceList::dec_reserved_words(size_t v) {
! assert_lock_strong(MetaspaceExpand_lock);
! _reserved_words = _reserved_words - v;
! }
!
! #define assert_committed_below_limit() \
! assert(MetaspaceUtils::committed_bytes() <= MaxMetaspaceSize, \
! "Too much committed memory. Committed: " SIZE_FORMAT \
! " limit (MaxMetaspaceSize): " SIZE_FORMAT, \
! MetaspaceUtils::committed_bytes(), MaxMetaspaceSize);
!
! void VirtualSpaceList::inc_committed_words(size_t v) {
! assert_lock_strong(MetaspaceExpand_lock);
! _committed_words = _committed_words + v;
!
! assert_committed_below_limit();
! }
! void VirtualSpaceList::dec_committed_words(size_t v) {
! assert_lock_strong(MetaspaceExpand_lock);
! _committed_words = _committed_words - v;
!
! assert_committed_below_limit();
! }
!
! void VirtualSpaceList::inc_virtual_space_count() {
! assert_lock_strong(MetaspaceExpand_lock);
! _virtual_space_count++;
! }
!
! void VirtualSpaceList::dec_virtual_space_count() {
! assert_lock_strong(MetaspaceExpand_lock);
! _virtual_space_count--;
! }
!
! // Walk the list of VirtualSpaceNodes and delete
! // nodes with a 0 container_count. Remove Metachunks in
! // the node from their respective freelists.
! void VirtualSpaceList::purge(ChunkManager* chunk_manager) {
! assert_lock_strong(MetaspaceExpand_lock);
! // Don't use a VirtualSpaceListIterator because this
! // list is being changed and a straightforward use of an iterator is not safe.
! VirtualSpaceNode* prev_vsl = virtual_space_list();
! VirtualSpaceNode* next_vsl = prev_vsl;
! int num_purged_nodes = 0;
! while (next_vsl != NULL) {
! VirtualSpaceNode* vsl = next_vsl;
! DEBUG_ONLY(vsl->verify(false);)
! next_vsl = vsl->next();
! // Don't free the current virtual space since it will likely
! // be needed soon.
! if (vsl->container_count() == 0 && vsl != current_virtual_space()) {
! log_trace(gc, metaspace, freelist)("Purging VirtualSpaceNode " PTR_FORMAT " (capacity: " SIZE_FORMAT
! ", used: " SIZE_FORMAT ").", p2i(vsl), vsl->capacity_words_in_vs(), vsl->used_words_in_vs());
! DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_purged));
! // Unlink it from the list
! if (prev_vsl == vsl) {
! // This is the case of the current node being the first node.
! assert(vsl == virtual_space_list(), "Expected to be the first node");
! set_virtual_space_list(vsl->next());
! } else {
! prev_vsl->set_next(vsl->next());
! }
! vsl->purge(chunk_manager);
! dec_reserved_words(vsl->reserved_words());
! dec_committed_words(vsl->committed_words());
! dec_virtual_space_count();
! delete vsl;
! num_purged_nodes ++;
} else {
! prev_vsl = vsl;
}
}
! // Verify list
! #ifdef ASSERT
! if (num_purged_nodes > 0) {
! verify(false);
! }
! #endif
! }
! // This function looks at the mmap regions in the metaspace without locking.
! // The chunks are added with store ordering and not deleted except for at
! // unloading time during a safepoint.
! VirtualSpaceNode* VirtualSpaceList::find_enclosing_space(const void* ptr) {
! // List should be stable enough to use an iterator here because removing virtual
! // space nodes is only allowed at a safepoint.
! if (is_within_envelope((address)ptr)) {
! VirtualSpaceListIterator iter(virtual_space_list());
! while (iter.repeat()) {
! VirtualSpaceNode* vsn = iter.get_next();
! if (vsn->contains(ptr)) {
! return vsn;
! }
! }
! }
! return NULL;
! }
!
! void VirtualSpaceList::retire_current_virtual_space() {
! assert_lock_strong(MetaspaceExpand_lock);
- VirtualSpaceNode* vsn = current_virtual_space();
-
- ChunkManager* cm = is_class() ? Metaspace::chunk_manager_class() :
- Metaspace::chunk_manager_metadata();
-
- vsn->retire(cm);
}
! VirtualSpaceList::VirtualSpaceList(size_t word_size) :
! _virtual_space_list(NULL),
! _current_virtual_space(NULL),
! _is_class(false),
! _reserved_words(0),
! _committed_words(0),
! _virtual_space_count(0),
! _envelope_lo((address)max_uintx),
! _envelope_hi(NULL) {
! MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag);
! create_new_virtual_space(word_size);
! }
!
! VirtualSpaceList::VirtualSpaceList(ReservedSpace rs) :
! _virtual_space_list(NULL),
! _current_virtual_space(NULL),
! _is_class(true),
! _reserved_words(0),
! _committed_words(0),
! _virtual_space_count(0),
! _envelope_lo((address)max_uintx),
! _envelope_hi(NULL) {
! MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag);
! VirtualSpaceNode* class_entry = new VirtualSpaceNode(is_class(), rs);
! bool succeeded = class_entry->initialize();
! if (succeeded) {
! expand_envelope_to_include_node(class_entry);
! // ensure lock-free iteration sees fully initialized node
! OrderAccess::storestore();
! link_vs(class_entry);
! }
! }
!
! size_t VirtualSpaceList::free_bytes() {
! return current_virtual_space()->free_words_in_vs() * BytesPerWord;
! }
!
! // Allocate another meta virtual space and add it to the list.
! bool VirtualSpaceList::create_new_virtual_space(size_t vs_word_size) {
! assert_lock_strong(MetaspaceExpand_lock);
!
! if (is_class()) {
! assert(false, "We currently don't support more than one VirtualSpace for"
! " the compressed class space. The initialization of the"
! " CCS uses another code path and should not hit this path.");
! return false;
! }
!
! if (vs_word_size == 0) {
! assert(false, "vs_word_size should always be at least _reserve_alignment large.");
! return false;
! }
!
! // Reserve the space
! size_t vs_byte_size = vs_word_size * BytesPerWord;
! assert_is_aligned(vs_byte_size, Metaspace::reserve_alignment());
!
! // Allocate the meta virtual space and initialize it.
! VirtualSpaceNode* new_entry = new VirtualSpaceNode(is_class(), vs_byte_size);
! if (!new_entry->initialize()) {
! delete new_entry;
! return false;
! } else {
! assert(new_entry->reserved_words() == vs_word_size,
! "Reserved memory size differs from requested memory size");
! expand_envelope_to_include_node(new_entry);
! // ensure lock-free iteration sees fully initialized node
! OrderAccess::storestore();
! link_vs(new_entry);
! DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_vsnodes_created));
! return true;
}
-
- DEBUG_ONLY(verify(false);)
-
}
- void VirtualSpaceList::link_vs(VirtualSpaceNode* new_entry) {
- if (virtual_space_list() == NULL) {
- set_virtual_space_list(new_entry);
- } else {
- current_virtual_space()->set_next(new_entry);
- }
- set_current_virtual_space(new_entry);
- inc_reserved_words(new_entry->reserved_words());
- inc_committed_words(new_entry->committed_words());
- inc_virtual_space_count();
#ifdef ASSERT
! new_entry->mangle();
! #endif
! LogTarget(Trace, gc, metaspace) lt;
! if (lt.is_enabled()) {
! LogStream ls(lt);
! VirtualSpaceNode* vsl = current_virtual_space();
! ResourceMark rm;
! vsl->print_on(&ls);
! }
! }
!
! bool VirtualSpaceList::expand_node_by(VirtualSpaceNode* node,
! size_t min_words,
! size_t preferred_words) {
! size_t before = node->committed_words();
!
! bool result = node->expand_by(min_words, preferred_words);
!
! size_t after = node->committed_words();
!
! // after and before can be the same if the memory was pre-committed.
! assert(after >= before, "Inconsistency");
! inc_committed_words(after - before);
!
! return result;
! }
! bool VirtualSpaceList::expand_by(size_t min_words, size_t preferred_words) {
! assert_is_aligned(min_words, Metaspace::commit_alignment_words());
! assert_is_aligned(preferred_words, Metaspace::commit_alignment_words());
! assert(min_words <= preferred_words, "Invalid arguments");
!
! const char* const class_or_not = (is_class() ? "class" : "non-class");
!
! if (!MetaspaceGC::can_expand(min_words, this->is_class())) {
! log_trace(gc, metaspace, freelist)("Cannot expand %s virtual space list.",
! class_or_not);
! return false;
}
! size_t allowed_expansion_words = MetaspaceGC::allowed_expansion();
! if (allowed_expansion_words < min_words) {
! log_trace(gc, metaspace, freelist)("Cannot expand %s virtual space list (must try gc first).",
! class_or_not);
! return false;
! }
!
! size_t max_expansion_words = MIN2(preferred_words, allowed_expansion_words);
!
! // Commit more memory from the the current virtual space.
! bool vs_expanded = expand_node_by(current_virtual_space(),
! min_words,
! max_expansion_words);
! if (vs_expanded) {
! log_trace(gc, metaspace, freelist)("Expanded %s virtual space list.",
! class_or_not);
! return true;
! }
! log_trace(gc, metaspace, freelist)("%s virtual space list: retire current node.",
! class_or_not);
! retire_current_virtual_space();
!
! // Get another virtual space.
! size_t grow_vs_words = MAX2((size_t)VirtualSpaceSize, preferred_words);
! grow_vs_words = align_up(grow_vs_words, Metaspace::reserve_alignment_words());
!
! if (create_new_virtual_space(grow_vs_words)) {
! if (current_virtual_space()->is_pre_committed()) {
! // The memory was pre-committed, so we are done here.
! assert(min_words <= current_virtual_space()->committed_words(),
! "The new VirtualSpace was pre-committed, so it"
! "should be large enough to fit the alloc request.");
! return true;
! }
- return expand_node_by(current_virtual_space(),
- min_words,
- max_expansion_words);
- }
-
- return false;
- }
-
- // Given a chunk, calculate the largest possible padding space which
- // could be required when allocating it.
- static size_t largest_possible_padding_size_for_chunk(size_t chunk_word_size, bool is_class) {
- const ChunkIndex chunk_type = get_chunk_type_by_size(chunk_word_size, is_class);
- if (chunk_type != HumongousIndex) {
- // Normal, non-humongous chunks are allocated at chunk size
- // boundaries, so the largest padding space required would be that
- // minus the smallest chunk size.
- const size_t smallest_chunk_size = is_class ? ClassSpecializedChunk : SpecializedChunk;
- return chunk_word_size - smallest_chunk_size;
- } else {
- // Humongous chunks are allocated at smallest-chunksize
- // boundaries, so there is no padding required.
- return 0;
- }
- }
-
-
- Metachunk* VirtualSpaceList::get_new_chunk(size_t chunk_word_size, size_t suggested_commit_granularity) {
-
- // Allocate a chunk out of the current virtual space.
- Metachunk* next = current_virtual_space()->get_chunk_vs(chunk_word_size);
-
- if (next != NULL) {
- return next;
- }
-
- // The expand amount is currently only determined by the requested sizes
- // and not how much committed memory is left in the current virtual space.
-
- // We must have enough space for the requested size and any
- // additional reqired padding chunks.
- const size_t size_for_padding = largest_possible_padding_size_for_chunk(chunk_word_size, this->is_class());
-
- size_t min_word_size = align_up(chunk_word_size + size_for_padding, Metaspace::commit_alignment_words());
- size_t preferred_word_size = align_up(suggested_commit_granularity, Metaspace::commit_alignment_words());
- if (min_word_size >= preferred_word_size) {
- // Can happen when humongous chunks are allocated.
- preferred_word_size = min_word_size;
- }
-
- bool expanded = expand_by(min_word_size, preferred_word_size);
- if (expanded) {
- next = current_virtual_space()->get_chunk_vs(chunk_word_size);
- assert(next != NULL, "The allocation was expected to succeed after the expansion");
- }
-
- return next;
}
! void VirtualSpaceList::print_on(outputStream* st, size_t scale) const {
! st->print_cr(SIZE_FORMAT " nodes, current node: " PTR_FORMAT,
! _virtual_space_count, p2i(_current_virtual_space));
! VirtualSpaceListIterator iter(virtual_space_list());
! while (iter.repeat()) {
! st->cr();
! VirtualSpaceNode* node = iter.get_next();
! node->print_on(st, scale);
}
}
! void VirtualSpaceList::print_map(outputStream* st) const {
! VirtualSpaceNode* list = virtual_space_list();
! VirtualSpaceListIterator iter(list);
! unsigned i = 0;
! while (iter.repeat()) {
! st->print_cr("Node %u:", i);
! VirtualSpaceNode* node = iter.get_next();
! node->print_map(st, this->is_class());
! i ++;
! }
! }
! // Given a node, expand range such that it includes the node.
! void VirtualSpaceList::expand_envelope_to_include_node(const VirtualSpaceNode* node) {
! _envelope_lo = MIN2(_envelope_lo, (address)node->low_boundary());
! _envelope_hi = MAX2(_envelope_hi, (address)node->high_boundary());
}
!
! #ifdef ASSERT
! void VirtualSpaceList::verify(bool slow) {
! VirtualSpaceNode* list = virtual_space_list();
! VirtualSpaceListIterator iter(list);
! size_t reserved = 0;
! size_t committed = 0;
! size_t node_count = 0;
! while (iter.repeat()) {
! VirtualSpaceNode* node = iter.get_next();
! if (slow) {
! node->verify(true);
! }
! // Check that the node resides fully within our envelope.
! assert((address)node->low_boundary() >= _envelope_lo && (address)node->high_boundary() <= _envelope_hi,
! "Node " SIZE_FORMAT " [" PTR_FORMAT ", " PTR_FORMAT ") outside envelope [" PTR_FORMAT ", " PTR_FORMAT ").",
! node_count, p2i(node->low_boundary()), p2i(node->high_boundary()), p2i(_envelope_lo), p2i(_envelope_hi));
! reserved += node->reserved_words();
! committed += node->committed_words();
! node_count ++;
! }
! assert(reserved == reserved_words() && committed == committed_words() && node_count == _virtual_space_count,
! "Mismatch: reserved real: " SIZE_FORMAT " expected: " SIZE_FORMAT
! ", committed real: " SIZE_FORMAT " expected: " SIZE_FORMAT
! ", node count real: " SIZE_FORMAT " expected: " SIZE_FORMAT ".",
! reserved, reserved_words(), committed, committed_words(),
! node_count, _virtual_space_count);
}
- #endif // ASSERT
} // namespace metaspace
--- 22,183 ----
*
*/
#include "precompiled.hpp"
#include "memory/metaspace.hpp"
#include "memory/metaspace/chunkManager.hpp"
! #include "memory/metaspace/counter.hpp"
! #include "memory/metaspace/commitLimiter.hpp"
! #include "memory/metaspace/counter.hpp"
#include "memory/metaspace/virtualSpaceList.hpp"
#include "memory/metaspace/virtualSpaceNode.hpp"
#include "runtime/mutexLocker.hpp"
!
namespace metaspace {
+ // Create a new, empty, expandable list.
+ VirtualSpaceList::VirtualSpaceList(const char* name, CommitLimiter* commit_limiter)
+ : _first_node(NULL),
+ _current_node(NULL),
+ _can_expand(true),
+ _name(name),
+ _commit_limiter(commit_limiter),
+ _reserved_words_counter(),
+ _committed_words_counter()
+ {
+ // Create the first node right now. Nothing gets committed yet though.
+ create_new_node();
+ }
+
+ // Create a new list. The list will contain one node only, which uses the given ReservedSpace.
+ // It will be not expandable beyond that first node.
+ VirtualSpaceList::VirtualSpaceList(const char* name, ReservedSpace rs, CommitLimiter* commit_limiter)
+ : _first_node(NULL),
+ _current_node(NULL),
+ _can_expand(false),
+ _name(name),
+ _commit_limiter(commit_limiter),
+ _reserved_words_counter(),
+ _committed_words_counter()
+ {
+ // Create the first node spanning the existing ReservedSpace. This will be the only node created
+ // for this list since we cannot expand.
+ VirtualSpaceNode* vsn = VirtualSpaceNode::create_node(rs, _commit_limiter,
+ &_reserved_words_counter, &_committed_words_counter);
+ assert(vsn != NULL, "node creation failed");
+ _first_node = _current_node = vsn;
+ _current_node->set_next(NULL);
+ }
VirtualSpaceList::~VirtualSpaceList() {
! // Note: normally, there is no reason ever to delete a vslist since they are
! // global objects, but for gtests it makes sense to allow this.
! VirtualSpaceNode* vsn = _first_node;
! VirtualSpaceNode* vsn2 = vsn;
! while (vsn != NULL) {
! vsn2 = vsn->next();
! delete vsn;
! vsn = vsn2;
}
}
! // Create a new node and append it to the list. After
! // this function, _current_node shall point to a new empty node.
! // List must be expandable for this to work.
! void VirtualSpaceList::create_new_node() {
! assert(_can_expand, "List is not expandable");
! VirtualSpaceNode* vsn = VirtualSpaceNode::create_node(constants::virtual_space_node_default_size,
! _commit_limiter,
! &_reserved_words_counter, &_committed_words_counter);
! assert(vsn != NULL, "node creation failed");
! vsn->set_next(_first_node);
! _first_node = _current_node = vsn;
! }
!
! // Allocate a root chunk from this list.
! // Note: this just returns a chunk whose memory is reserved; no memory is committed yet.
! // Hence, before using this chunk, it must be committed.
! // Also, no limits are checked, since no committing takes place.
! Metachunk* VirtualSpaceList::allocate_root_chunk() {
!
! assert(_current_node != NULL, "Sanity");
!
! Metachunk* c = _current_node->allocate_root_chunk();
!
! if (c == NULL) {
!
! // The current node is fully used up.
!
! // Since all allocations from a VirtualSpaceNode happen in root-chunk-size units,
! // we should never have som
! assert(_current_node->used_words() == _current_node->word_size(), "Sanity");
! if (_can_expand) {
! create_new_node();
} else {
! return NULL; // We cannot expand this list.
}
}
! Metachunk* c = _current_node->allocate_root_chunk();
+ assert(c != NULL, "This should have worked");
! return c;
}
! // Print all nodes in this space list.
! void VirtualSpaceList::print_on(outputStream* st, size_t scale) const {
! const VirtualSpaceNode* vsn = _first_node;
! while (vsn != NULL) {
! vsn->print_on(st, scale);
! st->cr();
! vsn = vsn->next();
}
}
#ifdef ASSERT
! void VirtualSpaceList::verify(bool slow) const {
! assert(_current_node != NULL && _first_node != NULL && _name != NULL, "Sanity");
! size_t total_reserved_words = 0;
! size_t total_committed_words = 0;
! const VirtualSpaceNode* vsn = _first_node;
! while (vsn != NULL) {
! vsn->verify(slow);
! total_reserved_words += vsn->word_size();
! total_committed_words += vsn->committed_words();
! vsn = vsn->next();
}
! _reserved_words_counter.check(total_reserved_words);
! _committed_words_counter.check(total_committed_words);
}
+ #endif
! // Returns true if this pointer is contained in one of our nodes.
! bool VirtualSpaceList::contains(const MetaWord* p) const {
! const VirtualSpaceNode* vsn = _first_node;
! while (vsn != NULL && !vsn->contains(p)) {
! vsn = vsn->next();
}
}
! VirtualSpaceList* VirtualSpaceList::_vslist_class = NULL;
! VirtualSpaceList* VirtualSpaceList::_vslist_nonclass = NULL;
! void VirtualSpaceList::set_vslist_class(VirtualSpaceList* vsl) {
! assert(_vslist_class == NULL, "Sanity");
! _vslist_class = vsl;
}
! void VirtualSpaceList::set_vslist_nonclass(VirtualSpaceList* vsl) {
! assert(_vslist_nonclass == NULL, "Sanity");
! _vslist_nonclass = vsl;
}
} // namespace metaspace
< prev index next >