8024638: Count and expose the amount of committed memory in the metaspaces
Reviewed-by: brutisso, ehelin
--- a/hotspot/src/share/vm/memory/metaspace.cpp Thu Sep 12 10:15:30 2013 +0200
+++ b/hotspot/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
--- a/hotspot/src/share/vm/memory/metaspace.hpp Thu Sep 12 10:15:30 2013 +0200
+++ b/hotspot/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;
--- a/hotspot/src/share/vm/prims/jni.cpp Thu Sep 12 10:15:30 2013 +0200
+++ b/hotspot/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());
--- a/hotspot/src/share/vm/runtime/virtualspace.cpp Thu Sep 12 10:15:30 2013 +0200
+++ b/hotspot/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
--- a/hotspot/src/share/vm/runtime/virtualspace.hpp Thu Sep 12 10:15:30 2013 +0200
+++ b/hotspot/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