| File: | jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp |
| Warning: | line 91, column 22 Value stored to 'orig_level' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 2 | * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. |
| 3 | * Copyright (c) 2018, 2021 SAP SE. All rights reserved. |
| 4 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 5 | * |
| 6 | * This code is free software; you can redistribute it and/or modify it |
| 7 | * under the terms of the GNU General Public License version 2 only, as |
| 8 | * published by the Free Software Foundation. |
| 9 | * |
| 10 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 13 | * version 2 for more details (a copy is included in the LICENSE file that |
| 14 | * accompanied this code). |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License version |
| 17 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 18 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 19 | * |
| 20 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 21 | * or visit www.oracle.com if you need additional information or have any |
| 22 | * questions. |
| 23 | * |
| 24 | */ |
| 25 | |
| 26 | #include "precompiled.hpp" |
| 27 | #include "logging/log.hpp" |
| 28 | #include "logging/logStream.hpp" |
| 29 | #include "memory/metaspace/chunkManager.hpp" |
| 30 | #include "memory/metaspace/internalStats.hpp" |
| 31 | #include "memory/metaspace/metachunk.hpp" |
| 32 | #include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp" |
| 33 | #include "memory/metaspace/metaspaceCommon.hpp" |
| 34 | #include "memory/metaspace/metaspaceContext.hpp" |
| 35 | #include "memory/metaspace/metaspaceSettings.hpp" |
| 36 | #include "memory/metaspace/metaspaceStatistics.hpp" |
| 37 | #include "memory/metaspace/virtualSpaceList.hpp" |
| 38 | #include "memory/metaspace/virtualSpaceNode.hpp" |
| 39 | #include "runtime/mutexLocker.hpp" |
| 40 | #include "utilities/debug.hpp" |
| 41 | #include "utilities/globalDefinitions.hpp" |
| 42 | |
| 43 | namespace metaspace { |
| 44 | |
| 45 | #define LOGFMT"ChkMgr @" "0x%016" "l" "x" " (%s)" "ChkMgr @" PTR_FORMAT"0x%016" "l" "x" " (%s)" |
| 46 | #define LOGFMT_ARGSp2i(this), this->_name p2i(this), this->_name |
| 47 | |
| 48 | // Return a single chunk to the freelist and adjust accounting. No merge is attempted. |
| 49 | void ChunkManager::return_chunk_simple_locked(Metachunk* c) { |
| 50 | assert_lock_strong(Metaspace_lock); |
| 51 | DEBUG_ONLY(c->verify())c->verify(); |
| 52 | _chunks.add(c); |
| 53 | c->reset_used_words(); |
| 54 | // Tracing |
| 55 | log_debug(metaspace)(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChunkManager %s: returned chunk " METACHUNK_FORMAT"@" "0x%016" "l" "x" ", %c, base " "0x%016" "l" "x" ", level " "lv%.2d" ".", |
| 56 | _name, METACHUNK_FORMAT_ARGS(c)p2i(c), c->get_state_char(), p2i(c->base()), c->level ()); |
| 57 | } |
| 58 | |
| 59 | // Creates a chunk manager with a given name (which is for debug purposes only) |
| 60 | // and an associated space list which will be used to request new chunks from |
| 61 | // (see get_chunk()) |
| 62 | ChunkManager::ChunkManager(const char* name, VirtualSpaceList* space_list) : |
| 63 | _vslist(space_list), |
| 64 | _name(name), |
| 65 | _chunks() |
| 66 | { |
| 67 | } |
| 68 | |
| 69 | // Given a chunk, split it into a target chunk of a smaller size (higher target level) |
| 70 | // and at least one, possible several splinter chunks. |
| 71 | // The original chunk must be outside of the freelist and its state must be free. |
| 72 | // The splinter chunks are added to the freelist. |
| 73 | // The resulting target chunk will be located at the same address as the original |
| 74 | // chunk, but it will of course be smaller (of a higher level). |
| 75 | // The committed areas within the original chunk carry over to the resulting |
| 76 | // chunks. |
| 77 | void ChunkManager::split_chunk_and_add_splinters(Metachunk* c, chunklevel_t target_level) { |
| 78 | assert_lock_strong(Metaspace_lock); |
| 79 | assert(c->is_free(), "chunk to be split must be free.")do { if (!(c->is_free())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 79, "assert(" "c->is_free()" ") failed", "chunk to be split must be free." ); ::breakpoint(); } } while (0); |
| 80 | assert(c->level() < target_level, "Target level must be higher than current level.")do { if (!(c->level() < target_level)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 80, "assert(" "c->level() < target_level" ") failed", "Target level must be higher than current level."); ::breakpoint (); } } while (0); |
| 81 | assert(c->prev() == NULL && c->next() == NULL, "Chunk must be outside of any list.")do { if (!(c->prev() == __null && c->next() == __null )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 81, "assert(" "c->prev() == __null && c->next() == __null" ") failed", "Chunk must be outside of any list."); ::breakpoint (); } } while (0); |
| 82 | |
| 83 | DEBUG_ONLY(chunklevel::check_valid_level(target_level);)chunklevel::check_valid_level(target_level); |
| 84 | DEBUG_ONLY(c->verify();)c->verify(); |
| 85 | |
| 86 | UL2(debug, "splitting chunk " METACHUNK_FORMAT " to " CHKLVL_FORMAT ".",(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "splitting chunk " "@" "0x%016" "l" "x" ", %c, base " "0x%016" "l" "x" ", level " "lv%.2d" " to " "lv%.2d" ".", p2i (this), this->_name, p2i(c), c->get_state_char(), p2i(c ->base()), c->level(), target_level); |
| 87 | METACHUNK_FORMAT_ARGS(c), target_level)(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "splitting chunk " "@" "0x%016" "l" "x" ", %c, base " "0x%016" "l" "x" ", level " "lv%.2d" " to " "lv%.2d" ".", p2i (this), this->_name, p2i(c), c->get_state_char(), p2i(c ->base()), c->level(), target_level);; |
| 88 | |
| 89 | DEBUG_ONLY(size_t committed_words_before = c->committed_words();)size_t committed_words_before = c->committed_words(); |
| 90 | |
| 91 | const chunklevel_t orig_level = c->level(); |
Value stored to 'orig_level' during its initialization is never read | |
| 92 | c->vsnode()->split(target_level, c, &_chunks); |
| 93 | |
| 94 | // Splitting should never fail. |
| 95 | assert(c->level() == target_level, "Sanity")do { if (!(c->level() == target_level)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 95, "assert(" "c->level() == target_level" ") failed", "Sanity" ); ::breakpoint(); } } while (0); |
| 96 | |
| 97 | // The size of the committed portion should not change (subject to the reduced chunk size of course) |
| 98 | #ifdef ASSERT1 |
| 99 | if (committed_words_before > c->word_size()) { |
| 100 | assert(c->is_fully_committed(), "Sanity")do { if (!(c->is_fully_committed())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 100, "assert(" "c->is_fully_committed()" ") failed", "Sanity" ); ::breakpoint(); } } while (0); |
| 101 | } else { |
| 102 | assert(c->committed_words() == committed_words_before, "Sanity")do { if (!(c->committed_words() == committed_words_before) ) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 102, "assert(" "c->committed_words() == committed_words_before" ") failed", "Sanity"); ::breakpoint(); } } while (0); |
| 103 | } |
| 104 | c->verify(); |
| 105 | verify_locked(); |
| 106 | SOMETIMES(c->vsnode()->verify_locked();){ static int counter_ = 0; if (VerifyMetaspaceInterval > 0 ) { counter_++; if (counter_ >= VerifyMetaspaceInterval) { counter_ = 0; { c->vsnode()->verify_locked(); } } } } |
| 107 | #endif |
| 108 | InternalStats::inc_num_chunk_splits(); |
| 109 | } |
| 110 | |
| 111 | // On success, returns a chunk of level of <preferred_level>, but at most <max_level>. |
| 112 | // The first first <min_committed_words> of the chunk are guaranteed to be committed. |
| 113 | // On error, will return NULL. |
| 114 | // |
| 115 | // This function may fail for two reasons: |
| 116 | // - Either we are unable to reserve space for a new chunk (if the underlying VirtualSpaceList |
| 117 | // is non-expandable but needs expanding - aka out of compressed class space). |
| 118 | // - Or, if the necessary space cannot be committed because we hit a commit limit. |
| 119 | // This may be either the GC threshold or MaxMetaspaceSize. |
| 120 | Metachunk* ChunkManager::get_chunk(chunklevel_t preferred_level, chunklevel_t max_level, size_t min_committed_words) { |
| 121 | assert(preferred_level <= max_level, "Sanity")do { if (!(preferred_level <= max_level)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 121, "assert(" "preferred_level <= max_level" ") failed" , "Sanity"); ::breakpoint(); } } while (0); |
| 122 | assert(chunklevel::level_fitting_word_size(min_committed_words) >= max_level, "Sanity")do { if (!(chunklevel::level_fitting_word_size(min_committed_words ) >= max_level)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 122, "assert(" "chunklevel::level_fitting_word_size(min_committed_words) >= max_level" ") failed", "Sanity"); ::breakpoint(); } } while (0); |
| 123 | |
| 124 | MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); |
| 125 | |
| 126 | DEBUG_ONLY(verify_locked();)verify_locked(); |
| 127 | DEBUG_ONLY(chunklevel::check_valid_level(max_level);)chunklevel::check_valid_level(max_level); |
| 128 | DEBUG_ONLY(chunklevel::check_valid_level(preferred_level);)chunklevel::check_valid_level(preferred_level); |
| 129 | |
| 130 | UL2(debug, "requested chunk: pref_level: " CHKLVL_FORMAT(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "requested chunk: pref_level: " "lv%.2d" ", max_level: " "lv%.2d" ", min committed size: " "%" "l" "u" ".", p2i(this) , this->_name, preferred_level, max_level, min_committed_words ); |
| 131 | ", max_level: " CHKLVL_FORMAT ", min committed size: " SIZE_FORMAT ".",(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "requested chunk: pref_level: " "lv%.2d" ", max_level: " "lv%.2d" ", min committed size: " "%" "l" "u" ".", p2i(this) , this->_name, preferred_level, max_level, min_committed_words ); |
| 132 | preferred_level, max_level, min_committed_words)(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "requested chunk: pref_level: " "lv%.2d" ", max_level: " "lv%.2d" ", min committed size: " "%" "l" "u" ".", p2i(this) , this->_name, preferred_level, max_level, min_committed_words );; |
| 133 | |
| 134 | // First, optimistically look for a chunk which is already committed far enough to hold min_word_size. |
| 135 | |
| 136 | // 1) Search best or smaller committed chunks (first attempt): |
| 137 | // Start at the preferred chunk size and work your way down (level up). |
| 138 | // But for now, only consider chunks larger than a certain threshold - |
| 139 | // this is to prevent large loaders (eg boot) from unnecessarily gobbling up |
| 140 | // all the tiny splinter chunks lambdas leave around. |
| 141 | Metachunk* c = NULL__null; |
| 142 | c = _chunks.search_chunk_ascending(preferred_level, MIN2((chunklevel_t)(preferred_level + 2), max_level), min_committed_words); |
| 143 | |
| 144 | // 2) Search larger committed chunks: |
| 145 | // If that did not yield anything, look at larger chunks, which may be committed. We would have to split |
| 146 | // them first, of course. |
| 147 | if (c == NULL__null) { |
| 148 | c = _chunks.search_chunk_descending(preferred_level, min_committed_words); |
| 149 | } |
| 150 | // 3) Search best or smaller committed chunks (second attempt): |
| 151 | // Repeat (1) but now consider even the tiniest chunks as long as they are large enough to hold the |
| 152 | // committed min size. |
| 153 | if (c == NULL__null) { |
| 154 | c = _chunks.search_chunk_ascending(preferred_level, max_level, min_committed_words); |
| 155 | } |
| 156 | // if we did not get anything yet, there are no free chunks commmitted enough. Repeat search but look for uncommitted chunks too: |
| 157 | // 4) Search best or smaller chunks, can be uncommitted: |
| 158 | if (c == NULL__null) { |
| 159 | c = _chunks.search_chunk_ascending(preferred_level, max_level, 0); |
| 160 | } |
| 161 | // 5) Search a larger uncommitted chunk: |
| 162 | if (c == NULL__null) { |
| 163 | c = _chunks.search_chunk_descending(preferred_level, 0); |
| 164 | } |
| 165 | |
| 166 | if (c != NULL__null) { |
| 167 | UL(trace, "taken from freelist.")(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Trace))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Trace>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "taken from freelist.", p2i(this), this->_name );; |
| 168 | } |
| 169 | |
| 170 | // Failing all that, allocate a new root chunk from the connected virtual space. |
| 171 | // This may fail if the underlying vslist cannot be expanded (e.g. compressed class space) |
| 172 | if (c == NULL__null) { |
| 173 | c = _vslist->allocate_root_chunk(); |
| 174 | if (c == NULL__null) { |
| 175 | UL(info, "failed to get new root chunk.")(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Info))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Info>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "failed to get new root chunk.", p2i(this), this ->_name);; |
| 176 | } else { |
| 177 | assert(c->level() == chunklevel::ROOT_CHUNK_LEVEL, "root chunk expected")do { if (!(c->level() == chunklevel::ROOT_CHUNK_LEVEL)) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 177, "assert(" "c->level() == chunklevel::ROOT_CHUNK_LEVEL" ") failed", "root chunk expected"); ::breakpoint(); } } while (0); |
| 178 | UL(debug, "allocated new root chunk.")(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "allocated new root chunk.", p2i(this), this-> _name);; |
| 179 | } |
| 180 | } |
| 181 | if (c == NULL__null) { |
| 182 | // If we end up here, we found no match in the freelists and were unable to get a new |
| 183 | // root chunk (so we used up all address space, e.g. out of CompressedClassSpace). |
| 184 | UL2(info, "failed to get chunk (preferred level: " CHKLVL_FORMAT(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Info))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Info>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "failed to get chunk (preferred level: " "lv%.2d" ", max level " "lv%.2d" ".", p2i(this), this->_name, preferred_level , max_level); |
| 185 | ", max level " CHKLVL_FORMAT ".", preferred_level, max_level)(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Info))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Info>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "failed to get chunk (preferred level: " "lv%.2d" ", max level " "lv%.2d" ".", p2i(this), this->_name, preferred_level , max_level);; |
| 186 | c = NULL__null; |
| 187 | } |
| 188 | if (c != NULL__null) { |
| 189 | // Now we have a chunk. |
| 190 | // It may be larger than what the caller wanted, so we may want to split it. This should |
| 191 | // always work. |
| 192 | if (c->level() < preferred_level) { |
| 193 | split_chunk_and_add_splinters(c, preferred_level); |
| 194 | assert(c->level() == preferred_level, "split failed?")do { if (!(c->level() == preferred_level)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 194, "assert(" "c->level() == preferred_level" ") failed" , "split failed?"); ::breakpoint(); } } while (0); |
| 195 | } |
| 196 | // Attempt to commit the chunk (depending on settings, we either fully commit it or just |
| 197 | // commit enough to get the caller going). That may fail if we hit a commit limit. In |
| 198 | // that case put the chunk back to the freelist (re-merging it with its neighbors if we |
| 199 | // did split it) and return NULL. |
| 200 | const size_t to_commit = Settings::new_chunks_are_fully_committed() ? c->word_size() : min_committed_words; |
| 201 | if (c->committed_words() < to_commit) { |
| 202 | if (c->ensure_committed_locked(to_commit) == false) { |
| 203 | UL2(info, "failed to commit " SIZE_FORMAT " words on chunk " METACHUNK_FORMAT ".",(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Info))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Info>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "failed to commit " "%" "l" "u" " words on chunk " "@" "0x%016" "l" "x" ", %c, base " "0x%016" "l" "x" ", level " "lv%.2d" ".", p2i(this), this->_name, to_commit, p2i(c), c ->get_state_char(), p2i(c->base()), c->level()); |
| 204 | to_commit, METACHUNK_FORMAT_ARGS(c))(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Info))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Info>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "failed to commit " "%" "l" "u" " words on chunk " "@" "0x%016" "l" "x" ", %c, base " "0x%016" "l" "x" ", level " "lv%.2d" ".", p2i(this), this->_name, to_commit, p2i(c), c ->get_state_char(), p2i(c->base()), c->level());; |
| 205 | return_chunk_locked(c); |
| 206 | c = NULL__null; |
| 207 | } |
| 208 | } |
| 209 | if (c != NULL__null) { |
| 210 | // Still here? We have now a good chunk, all is well. |
| 211 | assert(c->committed_words() >= min_committed_words, "Sanity")do { if (!(c->committed_words() >= min_committed_words) ) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 211, "assert(" "c->committed_words() >= min_committed_words" ") failed", "Sanity"); ::breakpoint(); } } while (0); |
| 212 | |
| 213 | // Any chunk returned from ChunkManager shall be marked as in use. |
| 214 | c->set_in_use(); |
| 215 | |
| 216 | UL2(debug, "handing out chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c))(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "handing out chunk " "@" "0x%016" "l" "x" ", %c, base " "0x%016" "l" "x" ", level " "lv%.2d" ".", p2i(this), this-> _name, p2i(c), c->get_state_char(), p2i(c->base()), c-> level());; |
| 217 | |
| 218 | InternalStats::inc_num_chunks_taken_from_freelist(); |
| 219 | |
| 220 | SOMETIMES(c->vsnode()->verify_locked();){ static int counter_ = 0; if (VerifyMetaspaceInterval > 0 ) { counter_++; if (counter_ >= VerifyMetaspaceInterval) { counter_ = 0; { c->vsnode()->verify_locked(); } } } } |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | DEBUG_ONLY(verify_locked();)verify_locked(); |
| 225 | return c; |
| 226 | } |
| 227 | |
| 228 | // Return a single chunk to the ChunkManager and adjust accounting. May merge chunk |
| 229 | // with neighbors. |
| 230 | // As a side effect this removes the chunk from whatever list it has been in previously. |
| 231 | // Happens after a Classloader was unloaded and releases its metaspace chunks. |
| 232 | // !! Note: this may invalidate the chunk. Do not access the chunk after |
| 233 | // this function returns !! |
| 234 | void ChunkManager::return_chunk(Metachunk* c) { |
| 235 | MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); |
| 236 | return_chunk_locked(c); |
| 237 | } |
| 238 | |
| 239 | // See return_chunk(). |
| 240 | void ChunkManager::return_chunk_locked(Metachunk* c) { |
| 241 | assert_lock_strong(Metaspace_lock); |
| 242 | UL2(debug, ": returning chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(c))(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " ": returning chunk " "@" "0x%016" "l" "x" ", %c, base " "0x%016" "l" "x" ", level " "lv%.2d" ".", p2i(this), this-> _name, p2i(c), c->get_state_char(), p2i(c->base()), c-> level());; |
| 243 | DEBUG_ONLY(c->verify();)c->verify(); |
| 244 | assert(contains_chunk(c) == false, "A chunk to be added to the freelist must not be in the freelist already.")do { if (!(contains_chunk(c) == false)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 244, "assert(" "contains_chunk(c) == false" ") failed", "A chunk to be added to the freelist must not be in the freelist already." ); ::breakpoint(); } } while (0); |
| 245 | assert(c->is_in_use() || c->is_free(), "Unexpected chunk state")do { if (!(c->is_in_use() || c->is_free())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 245, "assert(" "c->is_in_use() || c->is_free()" ") failed" , "Unexpected chunk state"); ::breakpoint(); } } while (0); |
| 246 | assert(!c->in_list(), "Remove from list first")do { if (!(!c->in_list())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 246, "assert(" "!c->in_list()" ") failed", "Remove from list first" ); ::breakpoint(); } } while (0); |
| 247 | |
| 248 | c->set_free(); |
| 249 | c->reset_used_words(); |
| 250 | const chunklevel_t orig_lvl = c->level(); |
| 251 | |
| 252 | Metachunk* merged = NULL__null; |
| 253 | if (!c->is_root_chunk()) { |
| 254 | // Only attempt merging if we are not of the lowest level already. |
| 255 | merged = c->vsnode()->merge(c, &_chunks); |
| 256 | } |
| 257 | |
| 258 | if (merged != NULL__null) { |
| 259 | InternalStats::inc_num_chunk_merges(); |
| 260 | DEBUG_ONLY(merged->verify())merged->verify(); |
| 261 | // We did merge chunks and now have a bigger chunk. |
| 262 | assert(merged->level() < orig_lvl, "Sanity")do { if (!(merged->level() < orig_lvl)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 262, "assert(" "merged->level() < orig_lvl" ") failed" , "Sanity"); ::breakpoint(); } } while (0); |
| 263 | UL2(debug, "merged into chunk " METACHUNK_FORMAT ".", METACHUNK_FORMAT_ARGS(merged))(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Debug))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Debug>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "merged into chunk " "@" "0x%016" "l" "x" ", %c, base " "0x%016" "l" "x" ", level " "lv%.2d" ".", p2i(this), this-> _name, p2i(merged), merged->get_state_char(), p2i(merged-> base()), merged->level());; |
| 264 | c = merged; |
| 265 | } |
| 266 | |
| 267 | return_chunk_simple_locked(c); |
| 268 | DEBUG_ONLY(verify_locked();)verify_locked(); |
| 269 | SOMETIMES(c->vsnode()->verify_locked();){ static int counter_ = 0; if (VerifyMetaspaceInterval > 0 ) { counter_++; if (counter_ >= VerifyMetaspaceInterval) { counter_ = 0; { c->vsnode()->verify_locked(); } } } } |
| 270 | InternalStats::inc_num_chunks_returned_to_freelist(); |
| 271 | } |
| 272 | |
| 273 | // Given a chunk c, whose state must be "in-use" and must not be a root chunk, attempt to |
| 274 | // enlarge it in place by claiming its trailing buddy. |
| 275 | // |
| 276 | // This will only work if c is the leader of the buddy pair and the trailing buddy is free. |
| 277 | // |
| 278 | // If successful, the follower chunk will be removed from the freelists, the leader chunk c will |
| 279 | // double in size (level decreased by one). |
| 280 | // |
| 281 | // On success, true is returned, false otherwise. |
| 282 | bool ChunkManager::attempt_enlarge_chunk(Metachunk* c) { |
| 283 | MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); |
| 284 | return c->vsnode()->attempt_enlarge_chunk(c, &_chunks); |
| 285 | } |
| 286 | |
| 287 | static void print_word_size_delta(outputStream* st, size_t word_size_1, size_t word_size_2) { |
| 288 | if (word_size_1 == word_size_2) { |
| 289 | print_scaled_words(st, word_size_1); |
| 290 | st->print (" (no change)"); |
| 291 | } else { |
| 292 | print_scaled_words(st, word_size_1); |
| 293 | st->print("->"); |
| 294 | print_scaled_words(st, word_size_2); |
| 295 | st->print(" ("); |
| 296 | if (word_size_2 <= word_size_1) { |
| 297 | st->print("-"); |
| 298 | print_scaled_words(st, word_size_1 - word_size_2); |
| 299 | } else { |
| 300 | st->print("+"); |
| 301 | print_scaled_words(st, word_size_2 - word_size_1); |
| 302 | } |
| 303 | st->print(")"); |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | void ChunkManager::purge() { |
| 308 | MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); |
| 309 | UL(info, ": reclaiming memory...")(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Info))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Info>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " ": reclaiming memory...", p2i(this), this->_name );; |
| 310 | |
| 311 | const size_t reserved_before = _vslist->reserved_words(); |
| 312 | const size_t committed_before = _vslist->committed_words(); |
| 313 | |
| 314 | // We return unused memory to the Operating System: we iterate over all |
| 315 | // free chunks and uncommit the backing memory of those large enough to |
| 316 | // contain one or multiple commit granules (chunks larger than a granule |
| 317 | // always cover a whole number of granules and start at a granule boundary). |
| 318 | if (Settings::uncommit_free_chunks()) { |
| 319 | const chunklevel_t max_level = |
| 320 | chunklevel::level_fitting_word_size(Settings::commit_granule_words()); |
| 321 | for (chunklevel_t l = chunklevel::LOWEST_CHUNK_LEVEL; |
| 322 | l <= max_level; |
| 323 | l++) { |
| 324 | // Since we uncommit all chunks at this level, we do not break the "committed chunks are |
| 325 | // at the front of the list" condition. |
| 326 | for (Metachunk* c = _chunks.first_at_level(l); c != NULL__null; c = c->next()) { |
| 327 | c->uncommit_locked(); |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | const size_t reserved_after = _vslist->reserved_words(); |
| 333 | const size_t committed_after = _vslist->committed_words(); |
| 334 | |
| 335 | // Print a nice report. |
| 336 | if (reserved_after == reserved_before && committed_after == committed_before) { |
| 337 | UL(info, "nothing reclaimed.")(!(LogImpl<(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG)>::is_level(LogLevel::Info))) ? (void)0 : LogImpl <(LogTag::_metaspace), (LogTag::__NO_TAG), (LogTag::__NO_TAG ), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG) >::write<LogLevel::Info>("ChkMgr @" "0x%016" "l" "x" " (%s)" ": " "nothing reclaimed.", p2i(this), this->_name );; |
| 338 | } else { |
| 339 | LogTarget(Info, metaspace)LogTargetImpl<LogLevel::Info, (LogTag::_metaspace), (LogTag ::__NO_TAG), (LogTag::__NO_TAG), (LogTag::__NO_TAG), (LogTag:: __NO_TAG), (LogTag::__NO_TAG)> lt; |
| 340 | if (lt.is_enabled()) { |
| 341 | LogStream ls(lt); |
| 342 | ls.print_cr(LOGFMT"ChkMgr @" "0x%016" "l" "x" " (%s)" ": finished reclaiming memory: ", LOGFMT_ARGSp2i(this), this->_name); |
| 343 | ls.print("reserved: "); |
| 344 | print_word_size_delta(&ls, reserved_before, reserved_after); |
| 345 | ls.cr(); |
| 346 | ls.print("committed: "); |
| 347 | print_word_size_delta(&ls, committed_before, committed_after); |
| 348 | ls.cr(); |
| 349 | } |
| 350 | } |
| 351 | DEBUG_ONLY(_vslist->verify_locked())_vslist->verify_locked(); |
| 352 | DEBUG_ONLY(verify_locked())verify_locked(); |
| 353 | } |
| 354 | |
| 355 | // Convenience methods to return the global class-space chunkmanager |
| 356 | // and non-class chunkmanager, respectively. |
| 357 | ChunkManager* ChunkManager::chunkmanager_class() { |
| 358 | return MetaspaceContext::context_class() == NULL__null ? NULL__null : MetaspaceContext::context_class()->cm(); |
| 359 | } |
| 360 | |
| 361 | ChunkManager* ChunkManager::chunkmanager_nonclass() { |
| 362 | return MetaspaceContext::context_nonclass() == NULL__null ? NULL__null : MetaspaceContext::context_nonclass()->cm(); |
| 363 | } |
| 364 | |
| 365 | // Calculates the total number of committed words over all chunks. Walks chunks. |
| 366 | size_t ChunkManager::calc_committed_word_size() const { |
| 367 | MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); |
| 368 | return calc_committed_word_size_locked(); |
| 369 | } |
| 370 | |
| 371 | size_t ChunkManager::calc_committed_word_size_locked() const { |
| 372 | assert_lock_strong(Metaspace_lock); |
| 373 | return _chunks.calc_committed_word_size(); |
| 374 | } |
| 375 | |
| 376 | // Update statistics. |
| 377 | void ChunkManager::add_to_statistics(ChunkManagerStats* out) const { |
| 378 | MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); |
| 379 | for (chunklevel_t l = chunklevel::ROOT_CHUNK_LEVEL; l <= chunklevel::HIGHEST_CHUNK_LEVEL; l++) { |
| 380 | out->_num_chunks[l] += _chunks.num_chunks_at_level(l); |
| 381 | out->_committed_word_size[l] += _chunks.calc_committed_word_size_at_level(l); |
| 382 | } |
| 383 | DEBUG_ONLY(out->verify();)out->verify(); |
| 384 | } |
| 385 | |
| 386 | #ifdef ASSERT1 |
| 387 | |
| 388 | void ChunkManager::verify() const { |
| 389 | MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); |
| 390 | verify_locked(); |
| 391 | } |
| 392 | |
| 393 | void ChunkManager::verify_locked() const { |
| 394 | assert_lock_strong(Metaspace_lock); |
| 395 | assert(_vslist != NULL, "No vslist")do { if (!(_vslist != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/memory/metaspace/chunkManager.cpp" , 395, "assert(" "_vslist != __null" ") failed", "No vslist") ; ::breakpoint(); } } while (0); |
| 396 | _chunks.verify(); |
| 397 | } |
| 398 | |
| 399 | bool ChunkManager::contains_chunk(Metachunk* c) const { |
| 400 | return _chunks.contains(c); |
| 401 | } |
| 402 | |
| 403 | #endif // ASSERT |
| 404 | |
| 405 | void ChunkManager::print_on(outputStream* st) const { |
| 406 | MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag); |
| 407 | print_on_locked(st); |
| 408 | } |
| 409 | |
| 410 | void ChunkManager::print_on_locked(outputStream* st) const { |
| 411 | assert_lock_strong(Metaspace_lock); |
| 412 | st->print_cr("cm %s: %d chunks, total word size: " SIZE_FORMAT"%" "l" "u" ".", _name, |
| 413 | total_num_chunks(), total_word_size()); |
| 414 | _chunks.print_on(st); |
| 415 | } |
| 416 | |
| 417 | } // namespace metaspace |