# HG changeset patch # User stefank # Date 1378973754 -7200 # Node ID c4c768305a8f9a02c8a306dec2b20d81097458f3 # Parent d6c2669993457f5f71a7a41ec21d73fff86d5145 8024638: Count and expose the amount of committed memory in the metaspaces Reviewed-by: brutisso, ehelin diff -r d6c266999345 -r c4c768305a8f src/share/vm/memory/metaspace.cpp --- a/src/share/vm/memory/metaspace.cpp Thu Sep 12 10:15:30 2013 +0200 +++ b/src/share/vm/memory/metaspace.cpp Thu Sep 12 10:15:54 2013 +0200 @@ -291,6 +291,10 @@ MetaWord* bottom() const { return (MetaWord*) _virtual_space.low(); } MetaWord* end() const { return (MetaWord*) _virtual_space.high(); } + size_t reserved_words() const { return _virtual_space.reserved_size() / BytesPerWord; } + size_t expanded_words() const { return _virtual_space.committed_size() / BytesPerWord; } + size_t committed_words() const { return _virtual_space.actual_committed_size() / BytesPerWord; } + // address of next available space in _virtual_space; // Accessors VirtualSpaceNode* next() { return _next; } @@ -327,12 +331,10 @@ // Allocate a chunk from the virtual space and return it. Metachunk* get_chunk_vs(size_t chunk_word_size); - Metachunk* get_chunk_vs_with_expand(size_t chunk_word_size); // Expands/shrinks the committed space in a virtual space. Delegates // to Virtualspace bool expand_by(size_t words, bool pre_touch = false); - bool shrink_by(size_t words); // In preparation for deleting this node, remove all the chunks // in the node from any freelist. @@ -340,8 +342,6 @@ #ifdef ASSERT // Debug support - static void verify_virtual_space_total(); - static void verify_virtual_space_count(); void mangle(); #endif @@ -429,8 +429,11 @@ bool _is_class; bool can_grow() const { return !is_class() || !UseCompressedClassPointers; } - // Sum of space in all virtual spaces and number of virtual spaces - size_t _virtual_space_total; + // Sum of reserved and committed memory in the virtual spaces + size_t _reserved_words; + size_t _committed_words; + + // Number of virtual spaces size_t _virtual_space_count; ~VirtualSpaceList(); @@ -444,7 +447,7 @@ _current_virtual_space = v; } - void link_vs(VirtualSpaceNode* new_entry, size_t vs_word_size); + void link_vs(VirtualSpaceNode* new_entry); // Get another virtual space and add it to the list. This // is typically prompted by a failed attempt to allocate a chunk @@ -461,6 +464,8 @@ size_t grow_chunks_by_words, size_t medium_chunk_bunch); + bool expand_by(VirtualSpaceNode* node, size_t word_size, bool pre_touch = false); + // Get the first chunk for a Metaspace. Used for // special cases such as the boot class loader, reflection // class loader and anonymous class loader. @@ -476,10 +481,15 @@ // Allocate the first virtualspace. void initialize(size_t word_size); - size_t virtual_space_total() { return _virtual_space_total; } - - void inc_virtual_space_total(size_t v); - void dec_virtual_space_total(size_t v); + size_t reserved_words() { return _reserved_words; } + size_t reserved_bytes() { return reserved_words() * BytesPerWord; } + size_t committed_words() { return _committed_words; } + size_t committed_bytes() { return committed_words() * BytesPerWord; } + + void inc_reserved_words(size_t v); + void dec_reserved_words(size_t v); + void inc_committed_words(size_t v); + void dec_committed_words(size_t v); void inc_virtual_space_count(); void dec_virtual_space_count(); @@ -901,15 +911,6 @@ return result; } -// Shrink the virtual space (commit more of the reserved space) -bool VirtualSpaceNode::shrink_by(size_t words) { - size_t bytes = words * BytesPerWord; - virtual_space()->shrink_by(bytes); - return true; -} - -// Add another chunk to the chunk list. - Metachunk* VirtualSpaceNode::get_chunk_vs(size_t chunk_word_size) { assert_lock_strong(SpaceManager::expand_lock()); Metachunk* result = take_from_committed(chunk_word_size); @@ -919,23 +920,6 @@ return result; } -Metachunk* VirtualSpaceNode::get_chunk_vs_with_expand(size_t chunk_word_size) { - assert_lock_strong(SpaceManager::expand_lock()); - - Metachunk* new_chunk = get_chunk_vs(chunk_word_size); - - if (new_chunk == NULL) { - // Only a small part of the virtualspace is committed when first - // allocated so committing more here can be expected. - size_t page_size_words = os::vm_page_size() / BytesPerWord; - size_t aligned_expand_vs_by_words = align_size_up(chunk_word_size, - page_size_words); - expand_by(aligned_expand_vs_by_words, false); - new_chunk = get_chunk_vs(chunk_word_size); - } - return new_chunk; -} - bool VirtualSpaceNode::initialize() { if (!_rs.is_reserved()) { @@ -995,13 +979,22 @@ } } -void VirtualSpaceList::inc_virtual_space_total(size_t v) { +void VirtualSpaceList::inc_reserved_words(size_t v) { assert_lock_strong(SpaceManager::expand_lock()); - _virtual_space_total = _virtual_space_total + v; + _reserved_words = _reserved_words + v; +} +void VirtualSpaceList::dec_reserved_words(size_t v) { + assert_lock_strong(SpaceManager::expand_lock()); + _reserved_words = _reserved_words - v; } -void VirtualSpaceList::dec_virtual_space_total(size_t v) { + +void VirtualSpaceList::inc_committed_words(size_t v) { assert_lock_strong(SpaceManager::expand_lock()); - _virtual_space_total = _virtual_space_total - v; + _committed_words = _committed_words + v; +} +void VirtualSpaceList::dec_committed_words(size_t v) { + assert_lock_strong(SpaceManager::expand_lock()); + _committed_words = _committed_words - v; } void VirtualSpaceList::inc_virtual_space_count() { @@ -1052,7 +1045,8 @@ } vsl->purge(chunk_manager()); - dec_virtual_space_total(vsl->reserved()->word_size()); + dec_reserved_words(vsl->reserved_words()); + dec_committed_words(vsl->committed_words()); dec_virtual_space_count(); purged_vsl = vsl; delete vsl; @@ -1106,7 +1100,8 @@ _is_class(false), _virtual_space_list(NULL), _current_virtual_space(NULL), - _virtual_space_total(0), + _reserved_words(0), + _committed_words(0), _virtual_space_count(0) { MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag); @@ -1123,7 +1118,8 @@ _is_class(true), _virtual_space_list(NULL), _current_virtual_space(NULL), - _virtual_space_total(0), + _reserved_words(0), + _committed_words(0), _virtual_space_count(0) { MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag); @@ -1133,7 +1129,7 @@ _chunk_manager.free_chunks(SmallIndex)->set_size(ClassSmallChunk); _chunk_manager.free_chunks(MediumIndex)->set_size(ClassMediumChunk); assert(succeeded, " VirtualSpaceList initialization should not fail"); - link_vs(class_entry, rs.size()/BytesPerWord); + link_vs(class_entry); } size_t VirtualSpaceList::free_bytes() { @@ -1156,21 +1152,23 @@ delete new_entry; return false; } else { + assert(new_entry->reserved_words() == vs_word_size, "Must be"); // ensure lock-free iteration sees fully initialized node OrderAccess::storestore(); - link_vs(new_entry, vs_word_size); + link_vs(new_entry); return true; } } -void VirtualSpaceList::link_vs(VirtualSpaceNode* new_entry, size_t vs_word_size) { +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_virtual_space_total(vs_word_size); + inc_reserved_words(new_entry->reserved_words()); + inc_committed_words(new_entry->committed_words()); inc_virtual_space_count(); #ifdef ASSERT new_entry->mangle(); @@ -1181,6 +1179,20 @@ } } +bool VirtualSpaceList::expand_by(VirtualSpaceNode* node, size_t word_size, bool pre_touch) { + size_t before = node->committed_words(); + + bool result = node->expand_by(word_size, pre_touch); + + size_t after = node->committed_words(); + + // after and before can be the same if the memory was pre-committed. + assert(after >= before, "Must be"); + inc_committed_words(after - before); + + return result; +} + Metachunk* VirtualSpaceList::get_new_chunk(size_t word_size, size_t grow_chunks_by_words, size_t medium_chunk_bunch) { @@ -1204,7 +1216,7 @@ size_t aligned_expand_vs_by_words = align_size_up(expand_vs_by_words, page_size_words); bool vs_expanded = - current_virtual_space()->expand_by(aligned_expand_vs_by_words, false); + expand_by(current_virtual_space(), aligned_expand_vs_by_words); if (!vs_expanded) { // Should the capacity of the metaspaces be expanded for // this allocation? If it's the virtual space for classes and is @@ -1215,7 +1227,14 @@ MAX2((size_t)VirtualSpaceSize, aligned_expand_vs_by_words); if (grow_vs(grow_vs_words)) { // Got it. It's on the list now. Get a chunk from it. - next = current_virtual_space()->get_chunk_vs_with_expand(grow_chunks_by_words); + assert(current_virtual_space()->expanded_words() == 0, + "New virtuals space nodes should not have expanded"); + + size_t grow_chunks_by_words_aligned = align_size_up(grow_chunks_by_words, + page_size_words); + // We probably want to expand by aligned_expand_vs_by_words here. + expand_by(current_virtual_space(), grow_chunks_by_words_aligned); + next = current_virtual_space()->get_chunk_vs(grow_chunks_by_words); } } else { // Allocation will fail and induce a GC @@ -1325,7 +1344,7 @@ // reserved space, because this is a larger space prereserved for compressed // class pointers. if (!FLAG_IS_DEFAULT(MaxMetaspaceSize)) { - size_t real_allocated = Metaspace::space_list()->virtual_space_total() + + size_t real_allocated = Metaspace::space_list()->reserved_words() + MetaspaceAux::allocated_capacity_bytes(Metaspace::ClassType); if (real_allocated >= MaxMetaspaceSize) { return false; @@ -2615,7 +2634,12 @@ size_t MetaspaceAux::reserved_bytes(Metaspace::MetadataType mdtype) { VirtualSpaceList* list = Metaspace::get_space_list(mdtype); - return list == NULL ? 0 : list->virtual_space_total() * BytesPerWord; + return list == NULL ? 0 : list->reserved_bytes(); +} + +size_t MetaspaceAux::committed_bytes(Metaspace::MetadataType mdtype) { + VirtualSpaceList* list = Metaspace::get_space_list(mdtype); + return list == NULL ? 0 : list->committed_bytes(); } size_t MetaspaceAux::min_chunk_size_words() { return Metaspace::first_chunk_word_size(); } @@ -3357,3 +3381,59 @@ class_vsm()->dump(out); } } + +/////////////// Unit tests /////////////// + +#ifndef PRODUCT + +class MetaspaceAuxTest : AllStatic { + public: + static void test_reserved() { + size_t reserved = MetaspaceAux::reserved_bytes(); + + assert(reserved > 0, "assert"); + + size_t committed = MetaspaceAux::committed_bytes(); + assert(committed <= reserved, "assert"); + + size_t reserved_metadata = MetaspaceAux::reserved_bytes(Metaspace::NonClassType); + assert(reserved_metadata > 0, "assert"); + assert(reserved_metadata <= reserved, "assert"); + + if (UseCompressedClassPointers) { + size_t reserved_class = MetaspaceAux::reserved_bytes(Metaspace::ClassType); + assert(reserved_class > 0, "assert"); + assert(reserved_class < reserved, "assert"); + } + } + + static void test_committed() { + size_t committed = MetaspaceAux::committed_bytes(); + + assert(committed > 0, "assert"); + + size_t reserved = MetaspaceAux::reserved_bytes(); + assert(committed <= reserved, "assert"); + + size_t committed_metadata = MetaspaceAux::committed_bytes(Metaspace::NonClassType); + assert(committed_metadata > 0, "assert"); + assert(committed_metadata <= committed, "assert"); + + if (UseCompressedClassPointers) { + size_t committed_class = MetaspaceAux::committed_bytes(Metaspace::ClassType); + assert(committed_class > 0, "assert"); + assert(committed_class < committed, "assert"); + } + } + + static void test() { + test_reserved(); + test_committed(); + } +}; + +void MetaspaceAux_test() { + MetaspaceAuxTest::test(); +} + +#endif diff -r d6c266999345 -r c4c768305a8f src/share/vm/memory/metaspace.hpp --- a/src/share/vm/memory/metaspace.hpp Thu Sep 12 10:15:30 2013 +0200 +++ b/src/share/vm/memory/metaspace.hpp Thu Sep 12 10:15:54 2013 +0200 @@ -292,13 +292,18 @@ static size_t free_bytes(); static size_t free_bytes(Metaspace::MetadataType mdtype); - // Total capacity in all Metaspaces static size_t reserved_bytes(Metaspace::MetadataType mdtype); static size_t reserved_bytes() { return reserved_bytes(Metaspace::ClassType) + reserved_bytes(Metaspace::NonClassType); } + static size_t committed_bytes(Metaspace::MetadataType mdtype); + static size_t committed_bytes() { + return committed_bytes(Metaspace::ClassType) + + committed_bytes(Metaspace::NonClassType); + } + static size_t min_chunk_size_words(); static size_t min_chunk_size_bytes() { return min_chunk_size_words() * BytesPerWord; diff -r d6c266999345 -r c4c768305a8f src/share/vm/prims/jni.cpp --- a/src/share/vm/prims/jni.cpp Thu Sep 12 10:15:30 2013 +0200 +++ b/src/share/vm/prims/jni.cpp Thu Sep 12 10:15:54 2013 +0200 @@ -5048,12 +5048,16 @@ // Forward declaration void TestReservedSpace_test(); void TestReserveMemorySpecial_test(); +void TestVirtualSpace_test(); +void MetaspaceAux_test(); void execute_internal_vm_tests() { if (ExecuteInternalVMTests) { tty->print_cr("Running internal VM tests"); run_unit_test(TestReservedSpace_test()); run_unit_test(TestReserveMemorySpecial_test()); + run_unit_test(TestVirtualSpace_test()); + run_unit_test(MetaspaceAux_test()); run_unit_test(GlobalDefinitions::test_globals()); run_unit_test(GCTimerAllTest::all()); run_unit_test(arrayOopDesc::test_max_array_length()); diff -r d6c266999345 -r c4c768305a8f src/share/vm/runtime/virtualspace.cpp --- a/src/share/vm/runtime/virtualspace.cpp Thu Sep 12 10:15:30 2013 +0200 +++ b/src/share/vm/runtime/virtualspace.cpp Thu Sep 12 10:15:54 2013 +0200 @@ -453,6 +453,42 @@ return reserved_size() - committed_size(); } +size_t VirtualSpace::actual_committed_size() const { + // Special VirtualSpaces commit all reserved space up front. + if (special()) { + return reserved_size(); + } + + size_t committed_low = pointer_delta(_lower_high, _low_boundary, sizeof(char)); + size_t committed_middle = pointer_delta(_middle_high, _lower_high_boundary, sizeof(char)); + size_t committed_high = pointer_delta(_upper_high, _middle_high_boundary, sizeof(char)); + +#ifdef ASSERT + size_t lower = pointer_delta(_lower_high_boundary, _low_boundary, sizeof(char)); + size_t middle = pointer_delta(_middle_high_boundary, _lower_high_boundary, sizeof(char)); + size_t upper = pointer_delta(_upper_high_boundary, _middle_high_boundary, sizeof(char)); + + if (committed_high > 0) { + assert(committed_low == lower, "Must be"); + assert(committed_middle == middle, "Must be"); + } + + if (committed_middle > 0) { + assert(committed_low == lower, "Must be"); + } + if (committed_middle < middle) { + assert(committed_high == 0, "Must be"); + } + + if (committed_low < lower) { + assert(committed_high == 0, "Must be"); + assert(committed_middle == 0, "Must be"); + } +#endif + + return committed_low + committed_middle + committed_high; +} + bool VirtualSpace::contains(const void* p) const { return low() <= (const char*) p && (const char*) p < high(); @@ -910,6 +946,109 @@ TestReservedSpace::test_reserved_space(); } +#define assert_equals(actual, expected) \ + assert(actual == expected, \ + err_msg("Got " SIZE_FORMAT " expected " \ + SIZE_FORMAT, actual, expected)); + +#define assert_ge(value1, value2) \ + assert(value1 >= value2, \ + err_msg("'" #value1 "': " SIZE_FORMAT " '" \ + #value2 "': " SIZE_FORMAT, value1, value2)); + +#define assert_lt(value1, value2) \ + assert(value1 < value2, \ + err_msg("'" #value1 "': " SIZE_FORMAT " '" \ + #value2 "': " SIZE_FORMAT, value1, value2)); + + +class TestVirtualSpace : AllStatic { + public: + static void test_virtual_space_actual_committed_space(size_t reserve_size, size_t commit_size) { + size_t granularity = os::vm_allocation_granularity(); + size_t reserve_size_aligned = align_size_up(reserve_size, granularity); + + ReservedSpace reserved(reserve_size_aligned); + + assert(reserved.is_reserved(), "Must be"); + + VirtualSpace vs; + bool initialized = vs.initialize(reserved, 0); + assert(initialized, "Failed to initialize VirtualSpace"); + + vs.expand_by(commit_size, false); + + if (vs.special()) { + assert_equals(vs.actual_committed_size(), reserve_size_aligned); + } else { + assert_ge(vs.actual_committed_size(), commit_size); + // Approximate the commit granularity. + size_t commit_granularity = UseLargePages ? os::large_page_size() : os::vm_page_size(); + assert_lt(vs.actual_committed_size(), commit_size + commit_granularity); + } + + reserved.release(); + } + + static void test_virtual_space_actual_committed_space_one_large_page() { + if (!UseLargePages) { + return; + } + + size_t large_page_size = os::large_page_size(); + + ReservedSpace reserved(large_page_size, large_page_size, true, false); + + assert(reserved.is_reserved(), "Must be"); + + VirtualSpace vs; + bool initialized = vs.initialize(reserved, 0); + assert(initialized, "Failed to initialize VirtualSpace"); + + vs.expand_by(large_page_size, false); + + assert_equals(vs.actual_committed_size(), large_page_size); + + reserved.release(); + } + + static void test_virtual_space_actual_committed_space() { + test_virtual_space_actual_committed_space(4 * K, 0); + test_virtual_space_actual_committed_space(4 * K, 4 * K); + test_virtual_space_actual_committed_space(8 * K, 0); + test_virtual_space_actual_committed_space(8 * K, 4 * K); + test_virtual_space_actual_committed_space(8 * K, 8 * K); + test_virtual_space_actual_committed_space(12 * K, 0); + test_virtual_space_actual_committed_space(12 * K, 4 * K); + test_virtual_space_actual_committed_space(12 * K, 8 * K); + test_virtual_space_actual_committed_space(12 * K, 12 * K); + test_virtual_space_actual_committed_space(64 * K, 0); + test_virtual_space_actual_committed_space(64 * K, 32 * K); + test_virtual_space_actual_committed_space(64 * K, 64 * K); + test_virtual_space_actual_committed_space(2 * M, 0); + test_virtual_space_actual_committed_space(2 * M, 4 * K); + test_virtual_space_actual_committed_space(2 * M, 64 * K); + test_virtual_space_actual_committed_space(2 * M, 1 * M); + test_virtual_space_actual_committed_space(2 * M, 2 * M); + test_virtual_space_actual_committed_space(10 * M, 0); + test_virtual_space_actual_committed_space(10 * M, 4 * K); + test_virtual_space_actual_committed_space(10 * M, 8 * K); + test_virtual_space_actual_committed_space(10 * M, 1 * M); + test_virtual_space_actual_committed_space(10 * M, 2 * M); + test_virtual_space_actual_committed_space(10 * M, 5 * M); + test_virtual_space_actual_committed_space(10 * M, 10 * M); + } + + static void test_virtual_space() { + test_virtual_space_actual_committed_space(); + test_virtual_space_actual_committed_space_one_large_page(); + } +}; + +void TestVirtualSpace_test() { + TestVirtualSpace::test_virtual_space(); +} + #endif // PRODUCT #endif diff -r d6c266999345 -r c4c768305a8f src/share/vm/runtime/virtualspace.hpp --- a/src/share/vm/runtime/virtualspace.hpp Thu Sep 12 10:15:30 2013 +0200 +++ b/src/share/vm/runtime/virtualspace.hpp Thu Sep 12 10:15:54 2013 +0200 @@ -183,11 +183,16 @@ // Destruction ~VirtualSpace(); - // Testers (all sizes are byte sizes) - size_t committed_size() const; - size_t reserved_size() const; + // Reserved memory + size_t reserved_size() const; + // Actually committed OS memory + size_t actual_committed_size() const; + // Memory used/expanded in this virtual space + size_t committed_size() const; + // Memory left to use/expand in this virtual space size_t uncommitted_size() const; - bool contains(const void* p) const; + + bool contains(const void* p) const; // Operations // returns true on success, false otherwise