From c22b626da6781cf8037b54ac36c9d78a594d7582 Mon Sep 17 00:00:00 2001 From: mchinnathamb Date: Mon, 11 Dec 2017 02:39:24 -0800 Subject: [PATCH] 8170395: Metaspace initialization queries the wrong chunk freelist Reviewed-by: stuefe, stefank --- src/share/vm/memory/metaspace.cpp | 271 +++++++++++++++++++----------- src/share/vm/memory/metaspace.hpp | 15 +- src/share/vm/prims/jni.cpp | 4 +- src/share/vm/utilities/debug.cpp | 10 +- src/share/vm/utilities/debug.hpp | 3 +- 5 files changed, 196 insertions(+), 107 deletions(-) diff --git a/src/share/vm/memory/metaspace.cpp b/src/share/vm/memory/metaspace.cpp index bb45f62d0..207ddcc05 100644 --- a/src/share/vm/memory/metaspace.cpp +++ b/src/share/vm/memory/metaspace.cpp @@ -531,9 +531,8 @@ class VirtualSpaceList : public CHeapObj { size_t free_bytes(); - Metachunk* get_new_chunk(size_t word_size, - size_t grow_chunks_by_words, - size_t medium_chunk_bunch); + Metachunk* get_new_chunk(size_t chunk_word_size, + size_t suggested_commit_granularity); bool expand_node_by(VirtualSpaceNode* node, size_t min_words, @@ -687,15 +686,22 @@ class SpaceManager : public CHeapObj { MediumChunkMultiple = 4 }; - bool is_class() { return _mdtype == Metaspace::ClassType; } + static size_t specialized_chunk_size(bool is_class) { return is_class ? ClassSpecializedChunk : SpecializedChunk; } + static size_t small_chunk_size(bool is_class) { return is_class ? ClassSmallChunk : SmallChunk; } + static size_t medium_chunk_size(bool is_class) { return is_class ? ClassMediumChunk : MediumChunk; } + + static size_t smallest_chunk_size(bool is_class) { return specialized_chunk_size(is_class); } // Accessors - size_t specialized_chunk_size() { return (size_t) is_class() ? ClassSpecializedChunk : SpecializedChunk; } - size_t small_chunk_size() { return (size_t) is_class() ? ClassSmallChunk : SmallChunk; } - size_t medium_chunk_size() { return (size_t) is_class() ? ClassMediumChunk : MediumChunk; } - size_t medium_chunk_bunch() { return medium_chunk_size() * MediumChunkMultiple; } + bool is_class() const { return _mdtype == Metaspace::ClassType; } + + size_t specialized_chunk_size() const { return specialized_chunk_size(is_class()); } + size_t small_chunk_size() const { return small_chunk_size(is_class()); } + size_t medium_chunk_size() const { return medium_chunk_size(is_class()); } - size_t smallest_chunk_size() { return specialized_chunk_size(); } + size_t smallest_chunk_size() const { return smallest_chunk_size(is_class()); } + + size_t medium_chunk_bunch() const { return medium_chunk_size() * MediumChunkMultiple; } size_t allocated_blocks_words() const { return _allocated_blocks_words; } size_t allocated_blocks_bytes() const { return _allocated_blocks_words * BytesPerWord; } @@ -718,10 +724,13 @@ class SpaceManager : public CHeapObj { // decremented for all the Metachunks in-use by this SpaceManager. void dec_total_from_size_metrics(); - // Set the sizes for the initial chunks. - void get_initial_chunk_sizes(Metaspace::MetaspaceType type, - size_t* chunk_word_size, - size_t* class_chunk_word_size); + // Adjust the initial chunk size to match one of the fixed chunk list sizes, + // or return the unadjusted size if the requested size is humongous. + static size_t adjust_initial_chunk_size(size_t requested, bool is_class_space); + size_t adjust_initial_chunk_size(size_t requested) const; + + // Get the initial chunks size for this metaspace type. + size_t get_initial_chunk_size(Metaspace::MetaspaceType type) const; size_t sum_capacity_in_chunks_in_use() const; size_t sum_used_in_chunks_in_use() const; @@ -732,7 +741,7 @@ class SpaceManager : public CHeapObj { size_t sum_count_in_chunks_in_use(); size_t sum_count_in_chunks_in_use(ChunkIndex i); - Metachunk* get_new_chunk(size_t word_size, size_t grow_chunks_by_words); + Metachunk* get_new_chunk(size_t chunk_word_size); // Block allocation and deallocation. // Allocates a block from the current chunk @@ -1319,12 +1328,10 @@ bool VirtualSpaceList::expand_by(size_t min_words, size_t preferred_words) { return false; } -Metachunk* VirtualSpaceList::get_new_chunk(size_t word_size, - size_t grow_chunks_by_words, - size_t medium_chunk_bunch) { +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(grow_chunks_by_words); + Metachunk* next = current_virtual_space()->get_chunk_vs(chunk_word_size); if (next != NULL) { return next; @@ -1333,8 +1340,8 @@ Metachunk* VirtualSpaceList::get_new_chunk(size_t word_size, // The expand amount is currently only determined by the requested sizes // and not how much committed memory is left in the current virtual space. - size_t min_word_size = align_size_up(grow_chunks_by_words, Metaspace::commit_alignment_words()); - size_t preferred_word_size = align_size_up(medium_chunk_bunch, Metaspace::commit_alignment_words()); + size_t min_word_size = align_size_up(chunk_word_size, Metaspace::commit_alignment_words()); + size_t preferred_word_size = align_size_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; @@ -1342,7 +1349,7 @@ Metachunk* VirtualSpaceList::get_new_chunk(size_t word_size, bool expanded = expand_by(min_word_size, preferred_word_size); if (expanded) { - next = current_virtual_space()->get_chunk_vs(grow_chunks_by_words); + next = current_virtual_space()->get_chunk_vs(chunk_word_size); assert(next != NULL, "The allocation was expected to succeed after the expansion"); } @@ -1883,36 +1890,58 @@ void ChunkManager::print_on(outputStream* out) const { // SpaceManager methods -void SpaceManager::get_initial_chunk_sizes(Metaspace::MetaspaceType type, - size_t* chunk_word_size, - size_t* class_chunk_word_size) { - switch (type) { - case Metaspace::BootMetaspaceType: - *chunk_word_size = Metaspace::first_chunk_word_size(); - *class_chunk_word_size = Metaspace::first_class_chunk_word_size(); - break; - case Metaspace::ROMetaspaceType: - *chunk_word_size = SharedReadOnlySize / wordSize; - *class_chunk_word_size = ClassSpecializedChunk; - break; - case Metaspace::ReadWriteMetaspaceType: - *chunk_word_size = SharedReadWriteSize / wordSize; - *class_chunk_word_size = ClassSpecializedChunk; - break; - case Metaspace::AnonymousMetaspaceType: - case Metaspace::ReflectionMetaspaceType: - *chunk_word_size = SpecializedChunk; - *class_chunk_word_size = ClassSpecializedChunk; - break; - default: - *chunk_word_size = SmallChunk; - *class_chunk_word_size = ClassSmallChunk; - break; - } - assert(*chunk_word_size != 0 && *class_chunk_word_size != 0, - err_msg("Initial chunks sizes bad: data " SIZE_FORMAT - " class " SIZE_FORMAT, - *chunk_word_size, *class_chunk_word_size)); +size_t SpaceManager::adjust_initial_chunk_size(size_t requested, bool is_class_space) { + size_t chunk_sizes[] = { + specialized_chunk_size(is_class_space), + small_chunk_size(is_class_space), + medium_chunk_size(is_class_space) + }; + + // Adjust up to one of the fixed chunk sizes ... + for (size_t i = 0; i < ARRAY_SIZE(chunk_sizes); i++) { + if (requested <= chunk_sizes[i]) { + return chunk_sizes[i]; + } + } + + // ... or return the size as a humongous chunk. + return requested; +} + +size_t SpaceManager::adjust_initial_chunk_size(size_t requested) const { + return adjust_initial_chunk_size(requested, is_class()); +} + +size_t SpaceManager::get_initial_chunk_size(Metaspace::MetaspaceType type) const { + size_t requested; + + if (is_class()) { + switch (type) { + case Metaspace::BootMetaspaceType: requested = Metaspace::first_class_chunk_word_size(); break; + case Metaspace::ROMetaspaceType: requested = ClassSpecializedChunk; break; + case Metaspace::ReadWriteMetaspaceType: requested = ClassSpecializedChunk; break; + case Metaspace::AnonymousMetaspaceType: requested = ClassSpecializedChunk; break; + case Metaspace::ReflectionMetaspaceType: requested = ClassSpecializedChunk; break; + default: requested = ClassSmallChunk; break; + } + } else { + switch (type) { + case Metaspace::BootMetaspaceType: requested = Metaspace::first_chunk_word_size(); break; + case Metaspace::ROMetaspaceType: requested = SharedReadOnlySize / wordSize; break; + case Metaspace::ReadWriteMetaspaceType: requested = SharedReadWriteSize / wordSize; break; + case Metaspace::AnonymousMetaspaceType: requested = SpecializedChunk; break; + case Metaspace::ReflectionMetaspaceType: requested = SpecializedChunk; break; + default: requested = SmallChunk; break; + } + } + + // Adjust to one of the fixed chunk sizes (unless humongous) + const size_t adjusted = adjust_initial_chunk_size(requested); + + assert(adjusted != 0, err_msg("Incorrect initial chunk size. Requested: " + SIZE_FORMAT " adjusted: " SIZE_FORMAT, requested, adjusted)); + + return adjusted; } size_t SpaceManager::sum_free_in_chunks_in_use() const { @@ -2102,8 +2131,8 @@ MetaWord* SpaceManager::grow_and_allocate(size_t word_size) { } // Get another chunk out of the virtual space - size_t grow_chunks_by_words = calc_chunk_size(word_size); - Metachunk* next = get_new_chunk(word_size, grow_chunks_by_words); + size_t chunk_word_size = calc_chunk_size(word_size); + Metachunk* next = get_new_chunk(chunk_word_size); MetaWord* mem = NULL; @@ -2412,14 +2441,12 @@ void SpaceManager::retire_current_chunk() { } } -Metachunk* SpaceManager::get_new_chunk(size_t word_size, - size_t grow_chunks_by_words) { +Metachunk* SpaceManager::get_new_chunk(size_t chunk_word_size) { // Get a chunk from the chunk freelist - Metachunk* next = chunk_manager()->chunk_freelist_allocate(grow_chunks_by_words); + Metachunk* next = chunk_manager()->chunk_freelist_allocate(chunk_word_size); if (next == NULL) { - next = vs_list()->get_new_chunk(word_size, - grow_chunks_by_words, + next = vs_list()->get_new_chunk(chunk_word_size, medium_chunk_bunch()); } @@ -3085,7 +3112,7 @@ void Metaspace::initialize_class_space(ReservedSpace rs) { err_msg(SIZE_FORMAT " != " UINTX_FORMAT, rs.size(), CompressedClassSpaceSize)); assert(using_class_space(), "Must be using class space"); _class_space_list = new VirtualSpaceList(rs); - _chunk_manager_class = new ChunkManager(SpecializedChunk, ClassSmallChunk, ClassMediumChunk); + _chunk_manager_class = new ChunkManager(ClassSpecializedChunk, ClassSmallChunk, ClassMediumChunk); if (!_class_space_list->initialization_succeeded()) { vm_exit_during_initialization("Failed to setup compressed class space virtual space list."); @@ -3286,66 +3313,62 @@ void Metaspace::post_initialize() { MetaspaceGC::post_initialize(); } -Metachunk* Metaspace::get_initialization_chunk(MetadataType mdtype, - size_t chunk_word_size, - size_t chunk_bunch) { +void Metaspace::initialize_first_chunk(MetaspaceType type, MetadataType mdtype) { + Metachunk* chunk = get_initialization_chunk(type, mdtype); + if (chunk != NULL) { + // Add to this manager's list of chunks in use and current_chunk(). + get_space_manager(mdtype)->add_chunk(chunk, true); + } +} + +Metachunk* Metaspace::get_initialization_chunk(MetaspaceType type, MetadataType mdtype) { + size_t chunk_word_size = get_space_manager(mdtype)->get_initial_chunk_size(type); + // Get a chunk from the chunk freelist Metachunk* chunk = get_chunk_manager(mdtype)->chunk_freelist_allocate(chunk_word_size); - if (chunk != NULL) { - return chunk; + + if (chunk == NULL) { + chunk = get_space_list(mdtype)->get_new_chunk(chunk_word_size, + get_space_manager(mdtype)->medium_chunk_bunch()); } - return get_space_list(mdtype)->get_new_chunk(chunk_word_size, chunk_word_size, chunk_bunch); + // For dumping shared archive, report error if allocation has failed. + if (DumpSharedSpaces && chunk == NULL) { + report_insufficient_metaspace(MetaspaceAux::committed_bytes() + chunk_word_size * BytesPerWord); + } + + return chunk; } -void Metaspace::initialize(Mutex* lock, MetaspaceType type) { +void Metaspace::verify_global_initialization() { + assert(space_list() != NULL, "Metadata VirtualSpaceList has not been initialized"); + assert(chunk_manager_metadata() != NULL, "Metadata ChunkManager has not been initialized"); + + if (using_class_space()) { + assert(class_space_list() != NULL, "Class VirtualSpaceList has not been initialized"); + assert(chunk_manager_class() != NULL, "Class ChunkManager has not been initialized"); + } +} - assert(space_list() != NULL, - "Metadata VirtualSpaceList has not been initialized"); - assert(chunk_manager_metadata() != NULL, - "Metadata ChunkManager has not been initialized"); +void Metaspace::initialize(Mutex* lock, MetaspaceType type) { + verify_global_initialization(); + // Allocate SpaceManager for metadata objects. _vsm = new SpaceManager(NonClassType, lock); - if (_vsm == NULL) { - return; - } - size_t word_size; - size_t class_word_size; - vsm()->get_initial_chunk_sizes(type, &word_size, &class_word_size); if (using_class_space()) { - assert(class_space_list() != NULL, - "Class VirtualSpaceList has not been initialized"); - assert(chunk_manager_class() != NULL, - "Class ChunkManager has not been initialized"); - // Allocate SpaceManager for classes. _class_vsm = new SpaceManager(ClassType, lock); - if (_class_vsm == NULL) { - return; - } } MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag); // Allocate chunk for metadata objects - Metachunk* new_chunk = get_initialization_chunk(NonClassType, - word_size, - vsm()->medium_chunk_bunch()); - assert(!DumpSharedSpaces || new_chunk != NULL, "should have enough space for both chunks"); - if (new_chunk != NULL) { - // Add to this manager's list of chunks in use and current_chunk(). - vsm()->add_chunk(new_chunk, true); - } + initialize_first_chunk(type, NonClassType); // Allocate chunk for class metadata objects if (using_class_space()) { - Metachunk* class_chunk = get_initialization_chunk(ClassType, - class_word_size, - class_vsm()->medium_chunk_bunch()); - if (class_chunk != NULL) { - class_vsm()->add_chunk(class_chunk, true); - } + initialize_first_chunk(type, ClassType); } _alloc_record_head = NULL; @@ -3772,7 +3795,7 @@ class TestMetaspaceAuxTest : AllStatic { // vm_allocation_granularity aligned on Windows. size_t large_size = (size_t)(2*256*K + (os::vm_page_size()/BytesPerWord)); large_size += (os::vm_page_size()/BytesPerWord); - vs_list->get_new_chunk(large_size, large_size, 0); + vs_list->get_new_chunk(large_size, 0); } static void test() { @@ -3947,4 +3970,54 @@ void TestVirtualSpaceNode_test() { TestVirtualSpaceNodeTest::test(); TestVirtualSpaceNodeTest::test_is_available(); } + +// The following test is placed here instead of a gtest / unittest file +// because the ChunkManager class is only available in this file. +class SpaceManagerTest : AllStatic { + friend void SpaceManager_test_adjust_initial_chunk_size(); + + static void test_adjust_initial_chunk_size(bool is_class) { + const size_t smallest = SpaceManager::smallest_chunk_size(is_class); + const size_t normal = SpaceManager::small_chunk_size(is_class); + const size_t medium = SpaceManager::medium_chunk_size(is_class); + +#define test_adjust_initial_chunk_size(value, expected, is_class_value) \ + do { \ + size_t v = value; \ + size_t e = expected; \ + assert(SpaceManager::adjust_initial_chunk_size(v, (is_class_value)) == e, \ + err_msg("Expected: " SIZE_FORMAT " got: " SIZE_FORMAT, e, v)); \ + } while (0) + + // Smallest (specialized) + test_adjust_initial_chunk_size(1, smallest, is_class); + test_adjust_initial_chunk_size(smallest - 1, smallest, is_class); + test_adjust_initial_chunk_size(smallest, smallest, is_class); + + // Small + test_adjust_initial_chunk_size(smallest + 1, normal, is_class); + test_adjust_initial_chunk_size(normal - 1, normal, is_class); + test_adjust_initial_chunk_size(normal, normal, is_class); + + // Medium + test_adjust_initial_chunk_size(normal + 1, medium, is_class); + test_adjust_initial_chunk_size(medium - 1, medium, is_class); + test_adjust_initial_chunk_size(medium, medium, is_class); + + // Humongous + test_adjust_initial_chunk_size(medium + 1, medium + 1, is_class); + +#undef test_adjust_initial_chunk_size + } + + static void test_adjust_initial_chunk_size() { + test_adjust_initial_chunk_size(false); + test_adjust_initial_chunk_size(true); + } +}; + +void SpaceManager_test_adjust_initial_chunk_size() { + SpaceManagerTest::test_adjust_initial_chunk_size(); +} + #endif diff --git a/src/share/vm/memory/metaspace.hpp b/src/share/vm/memory/metaspace.hpp index 2026420aa..60c49df10 100644 --- a/src/share/vm/memory/metaspace.hpp +++ b/src/share/vm/memory/metaspace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -104,14 +104,15 @@ class Metaspace : public CHeapObj { }; private: + static void verify_global_initialization(); + void initialize(Mutex* lock, MetaspaceType type); - // Get the first chunk for a Metaspace. Used for + // Initialize the first chunk for a Metaspace. Used for // special cases such as the boot class loader, reflection // class loader and anonymous class loader. - Metachunk* get_initialization_chunk(MetadataType mdtype, - size_t chunk_word_size, - size_t chunk_bunch); + void initialize_first_chunk(MetaspaceType type, MetadataType mdtype); + Metachunk* get_initialization_chunk(MetaspaceType type, MetadataType mdtype); // Align up the word size to the allocation word size static size_t align_word_size_up(size_t); @@ -138,6 +139,10 @@ class Metaspace : public CHeapObj { SpaceManager* _class_vsm; SpaceManager* class_vsm() const { return _class_vsm; } + SpaceManager* get_space_manager(MetadataType mdtype) { + assert(mdtype != MetadataTypeCount, "MetadaTypeCount can't be used as mdtype"); + return mdtype == ClassType ? class_vsm() : vsm(); + } // Allocate space for metadata of type mdtype. This is space // within a Metachunk and is used by diff --git a/src/share/vm/prims/jni.cpp b/src/share/vm/prims/jni.cpp index 95fd4d226..074cc4a64 100644 --- a/src/share/vm/prims/jni.cpp +++ b/src/share/vm/prims/jni.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -5093,6 +5093,7 @@ void TestReservedSpace_test(); void TestReserveMemorySpecial_test(); void TestVirtualSpace_test(); void TestMetaspaceAux_test(); +void SpaceManager_test_adjust_initial_chunk_size(); void TestMetachunk_test(); void TestVirtualSpaceNode_test(); void TestNewSize_test(); @@ -5137,6 +5138,7 @@ void execute_internal_vm_tests() { run_unit_test(TestOldFreeSpaceCalculation_test()); run_unit_test(TestG1BiasedArray_test()); run_unit_test(HeapRegionRemSet::test_prt()); + run_unit_test(SpaceManager_test_adjust_initial_chunk_size()); run_unit_test(TestBufferingOopClosure_test()); run_unit_test(TestCodeCacheRemSet_test()); if (UseG1GC) { diff --git a/src/share/vm/utilities/debug.cpp b/src/share/vm/utilities/debug.cpp index 232f35016..4f7cbddcd 100644 --- a/src/share/vm/utilities/debug.cpp +++ b/src/share/vm/utilities/debug.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -315,6 +315,14 @@ void report_java_out_of_memory(const char* message) { } } +void report_insufficient_metaspace(size_t required_size) { + warning("\nThe MaxMetaspaceSize of " SIZE_FORMAT " bytes is not large enough.\n" + "Either don't specify the -XX:MaxMetaspaceSize=\n" + "or increase the size to at least " SIZE_FORMAT ".\n", + MaxMetaspaceSize, required_size); + exit(2); +} + static bool error_reported = false; // call this when the VM is dying--it might loosen some asserts diff --git a/src/share/vm/utilities/debug.hpp b/src/share/vm/utilities/debug.hpp index 5b47e6142..86fc6b18d 100644 --- a/src/share/vm/utilities/debug.hpp +++ b/src/share/vm/utilities/debug.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -222,6 +222,7 @@ void report_should_not_call(const char* file, int line); void report_should_not_reach_here(const char* file, int line); void report_unimplemented(const char* file, int line); void report_untested(const char* file, int line, const char* message); +void report_insufficient_metaspace(size_t required_size); void warning(const char* format, ...) ATTRIBUTE_PRINTF(1, 2); -- GitLab