| 1 | /* |
| 2 | * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | * accompanied this code). |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License version |
| 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | * |
| 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | * or visit www.oracle.com if you need additional information or have any |
| 21 | * questions. |
| 22 | * |
| 23 | */ |
| 24 | #include "precompiled.hpp" |
| 25 | |
| 26 | #include "logging/log.hpp" |
| 27 | #include "logging/logStream.hpp" |
| 28 | #include "memory/metaspace/chunkManager.hpp" |
| 29 | #include "memory/metaspace/metachunk.hpp" |
| 30 | #include "memory/metaspace/metaDebug.hpp" |
| 31 | #include "memory/metaspace/metaspaceCommon.hpp" |
| 32 | #include "memory/metaspace/spaceManager.hpp" |
| 33 | #include "memory/metaspace/virtualSpaceList.hpp" |
| 34 | #include "runtime/atomic.hpp" |
| 35 | #include "runtime/init.hpp" |
| 36 | #include "services/memoryService.hpp" |
| 37 | #include "utilities/debug.hpp" |
| 38 | #include "utilities/globalDefinitions.hpp" |
| 39 | |
| 40 | namespace metaspace { |
| 41 | |
| 42 | #define assert_counter(expected_value, real_value, msg) \ |
| 43 | assert( (expected_value) == (real_value), \ |
| 44 | "Counter mismatch (%s): expected " SIZE_FORMAT \ |
| 45 | ", but got: " SIZE_FORMAT ".", msg, expected_value, \ |
| 46 | real_value); |
| 47 | |
| 48 | // SpaceManager methods |
| 49 | |
| 50 | size_t SpaceManager::adjust_initial_chunk_size(size_t requested, bool is_class_space) { |
| 51 | size_t chunk_sizes[] = { |
| 52 | specialized_chunk_size(is_class_space), |
| 53 | small_chunk_size(is_class_space), |
| 54 | medium_chunk_size(is_class_space) |
| 55 | }; |
| 56 | |
| 57 | // Adjust up to one of the fixed chunk sizes ... |
| 58 | for (size_t i = 0; i < ARRAY_SIZE(chunk_sizes); i++) { |
| 59 | if (requested <= chunk_sizes[i]) { |
| 60 | return chunk_sizes[i]; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | // ... or return the size as a humongous chunk. |
| 65 | return requested; |
| 66 | } |
| 67 | |
| 68 | size_t SpaceManager::adjust_initial_chunk_size(size_t requested) const { |
| 69 | return adjust_initial_chunk_size(requested, is_class()); |
| 70 | } |
| 71 | |
| 72 | size_t SpaceManager::get_initial_chunk_size(Metaspace::MetaspaceType type) const { |
| 73 | size_t requested; |
| 74 | |
| 75 | if (is_class()) { |
| 76 | switch (type) { |
| 77 | case Metaspace::BootMetaspaceType: requested = Metaspace::first_class_chunk_word_size(); break; |
| 78 | case Metaspace::UnsafeAnonymousMetaspaceType: requested = ClassSpecializedChunk; break; |
| 79 | case Metaspace::ReflectionMetaspaceType: requested = ClassSpecializedChunk; break; |
| 80 | default: requested = ClassSmallChunk; break; |
| 81 | } |
| 82 | } else { |
| 83 | switch (type) { |
| 84 | case Metaspace::BootMetaspaceType: requested = Metaspace::first_chunk_word_size(); break; |
| 85 | case Metaspace::UnsafeAnonymousMetaspaceType: requested = SpecializedChunk; break; |
| 86 | case Metaspace::ReflectionMetaspaceType: requested = SpecializedChunk; break; |
| 87 | default: requested = SmallChunk; break; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | // Adjust to one of the fixed chunk sizes (unless humongous) |
| 92 | const size_t adjusted = adjust_initial_chunk_size(requested); |
| 93 | |
| 94 | assert(adjusted != 0, "Incorrect initial chunk size. Requested: " |
| 95 | SIZE_FORMAT " adjusted: " SIZE_FORMAT, requested, adjusted); |
| 96 | |
| 97 | return adjusted; |
| 98 | } |
| 99 | |
| 100 | void SpaceManager::locked_print_chunks_in_use_on(outputStream* st) const { |
| 101 | |
| 102 | for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { |
| 103 | st->print("SpaceManager: " UINTX_FORMAT " %s chunks." , |
| 104 | num_chunks_by_type(i), chunk_size_name(i)); |
| 105 | } |
| 106 | |
| 107 | chunk_manager()->locked_print_free_chunks(st); |
| 108 | } |
| 109 | |
| 110 | size_t SpaceManager::calc_chunk_size(size_t word_size) { |
| 111 | |
| 112 | // Decide between a small chunk and a medium chunk. Up to |
| 113 | // _small_chunk_limit small chunks can be allocated. |
| 114 | // After that a medium chunk is preferred. |
| 115 | size_t chunk_word_size; |
| 116 | |
| 117 | // Special case for unsafe anonymous metadata space. |
| 118 | // UnsafeAnonymous metadata space is usually small since it is used for |
| 119 | // class loader data's whose life cycle is governed by one class such as an |
| 120 | // unsafe anonymous class. The majority within 1K - 2K range and |
| 121 | // rarely about 4K (64-bits JVM). |
| 122 | // Instead of jumping to SmallChunk after initial chunk exhausted, keeping allocation |
| 123 | // from SpecializeChunk up to _anon_or_delegating_metadata_specialize_chunk_limit (4) |
| 124 | // reduces space waste from 60+% to around 30%. |
| 125 | if ((_space_type == Metaspace::UnsafeAnonymousMetaspaceType || _space_type == Metaspace::ReflectionMetaspaceType) && |
| 126 | _mdtype == Metaspace::NonClassType && |
| 127 | num_chunks_by_type(SpecializedIndex) < anon_and_delegating_metadata_specialize_chunk_limit && |
| 128 | word_size + Metachunk::overhead() <= SpecializedChunk) { |
| 129 | return SpecializedChunk; |
| 130 | } |
| 131 | |
| 132 | if (num_chunks_by_type(MediumIndex) == 0 && |
| 133 | num_chunks_by_type(SmallIndex) < small_chunk_limit) { |
| 134 | chunk_word_size = (size_t) small_chunk_size(); |
| 135 | if (word_size + Metachunk::overhead() > small_chunk_size()) { |
| 136 | chunk_word_size = medium_chunk_size(); |
| 137 | } |
| 138 | } else { |
| 139 | chunk_word_size = medium_chunk_size(); |
| 140 | } |
| 141 | |
| 142 | // Might still need a humongous chunk. Enforce |
| 143 | // humongous allocations sizes to be aligned up to |
| 144 | // the smallest chunk size. |
| 145 | size_t if_humongous_sized_chunk = |
| 146 | align_up(word_size + Metachunk::overhead(), |
| 147 | smallest_chunk_size()); |
| 148 | chunk_word_size = |
| 149 | MAX2((size_t) chunk_word_size, if_humongous_sized_chunk); |
| 150 | |
| 151 | assert(!SpaceManager::is_humongous(word_size) || |
| 152 | chunk_word_size == if_humongous_sized_chunk, |
| 153 | "Size calculation is wrong, word_size " SIZE_FORMAT |
| 154 | " chunk_word_size " SIZE_FORMAT, |
| 155 | word_size, chunk_word_size); |
| 156 | Log(gc, metaspace, alloc) log; |
| 157 | if (log.is_trace() && SpaceManager::is_humongous(word_size)) { |
| 158 | log.trace("Metadata humongous allocation:" ); |
| 159 | log.trace(" word_size " PTR_FORMAT, word_size); |
| 160 | log.trace(" chunk_word_size " PTR_FORMAT, chunk_word_size); |
| 161 | log.trace(" chunk overhead " PTR_FORMAT, Metachunk::overhead()); |
| 162 | } |
| 163 | return chunk_word_size; |
| 164 | } |
| 165 | |
| 166 | void SpaceManager::track_metaspace_memory_usage() { |
| 167 | if (is_init_completed()) { |
| 168 | if (is_class()) { |
| 169 | MemoryService::track_compressed_class_memory_usage(); |
| 170 | } |
| 171 | MemoryService::track_metaspace_memory_usage(); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | MetaWord* SpaceManager::grow_and_allocate(size_t word_size) { |
| 176 | assert_lock_strong(_lock); |
| 177 | assert(vs_list()->current_virtual_space() != NULL, |
| 178 | "Should have been set" ); |
| 179 | assert(current_chunk() == NULL || |
| 180 | current_chunk()->allocate(word_size) == NULL, |
| 181 | "Don't need to expand" ); |
| 182 | MutexLocker cl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); |
| 183 | |
| 184 | if (log_is_enabled(Trace, gc, metaspace, freelist)) { |
| 185 | size_t words_left = 0; |
| 186 | size_t words_used = 0; |
| 187 | if (current_chunk() != NULL) { |
| 188 | words_left = current_chunk()->free_word_size(); |
| 189 | words_used = current_chunk()->used_word_size(); |
| 190 | } |
| 191 | log_trace(gc, metaspace, freelist)("SpaceManager::grow_and_allocate for " SIZE_FORMAT " words " SIZE_FORMAT " words used " SIZE_FORMAT " words left" , |
| 192 | word_size, words_used, words_left); |
| 193 | } |
| 194 | |
| 195 | // Get another chunk |
| 196 | size_t chunk_word_size = calc_chunk_size(word_size); |
| 197 | Metachunk* next = get_new_chunk(chunk_word_size); |
| 198 | |
| 199 | MetaWord* mem = NULL; |
| 200 | |
| 201 | // If a chunk was available, add it to the in-use chunk list |
| 202 | // and do an allocation from it. |
| 203 | if (next != NULL) { |
| 204 | // Add to this manager's list of chunks in use. |
| 205 | // If the new chunk is humongous, it was created to serve a single large allocation. In that |
| 206 | // case it usually makes no sense to make it the current chunk, since the next allocation would |
| 207 | // need to allocate a new chunk anyway, while we would now prematurely retire a perfectly |
| 208 | // good chunk which could be used for more normal allocations. |
| 209 | bool make_current = true; |
| 210 | if (next->get_chunk_type() == HumongousIndex && |
| 211 | current_chunk() != NULL) { |
| 212 | make_current = false; |
| 213 | } |
| 214 | add_chunk(next, make_current); |
| 215 | mem = next->allocate(word_size); |
| 216 | } |
| 217 | |
| 218 | // Track metaspace memory usage statistic. |
| 219 | track_metaspace_memory_usage(); |
| 220 | |
| 221 | return mem; |
| 222 | } |
| 223 | |
| 224 | void SpaceManager::print_on(outputStream* st) const { |
| 225 | SpaceManagerStatistics stat; |
| 226 | add_to_statistics(&stat); // will lock _lock. |
| 227 | stat.print_on(st, 1*K, false); |
| 228 | } |
| 229 | |
| 230 | SpaceManager::SpaceManager(Metaspace::MetadataType mdtype, |
| 231 | Metaspace::MetaspaceType space_type,// |
| 232 | Mutex* lock) : |
| 233 | _lock(lock), |
| 234 | _mdtype(mdtype), |
| 235 | _space_type(space_type), |
| 236 | _chunk_list(NULL), |
| 237 | _current_chunk(NULL), |
| 238 | _overhead_words(0), |
| 239 | _capacity_words(0), |
| 240 | _used_words(0), |
| 241 | _block_freelists(NULL) { |
| 242 | Metadebug::init_allocation_fail_alot_count(); |
| 243 | memset(_num_chunks_by_type, 0, sizeof(_num_chunks_by_type)); |
| 244 | log_trace(gc, metaspace, freelist)("SpaceManager(): " PTR_FORMAT, p2i(this)); |
| 245 | } |
| 246 | |
| 247 | void SpaceManager::account_for_new_chunk(const Metachunk* new_chunk) { |
| 248 | |
| 249 | assert_lock_strong(MetaspaceExpand_lock); |
| 250 | |
| 251 | _capacity_words += new_chunk->word_size(); |
| 252 | _overhead_words += Metachunk::overhead(); |
| 253 | DEBUG_ONLY(new_chunk->verify()); |
| 254 | _num_chunks_by_type[new_chunk->get_chunk_type()] ++; |
| 255 | |
| 256 | // Adjust global counters: |
| 257 | MetaspaceUtils::inc_capacity(mdtype(), new_chunk->word_size()); |
| 258 | MetaspaceUtils::inc_overhead(mdtype(), Metachunk::overhead()); |
| 259 | } |
| 260 | |
| 261 | void SpaceManager::account_for_allocation(size_t words) { |
| 262 | // Note: we should be locked with the ClassloaderData-specific metaspace lock. |
| 263 | // We may or may not be locked with the global metaspace expansion lock. |
| 264 | assert_lock_strong(lock()); |
| 265 | |
| 266 | // Add to the per SpaceManager totals. This can be done non-atomically. |
| 267 | _used_words += words; |
| 268 | |
| 269 | // Adjust global counters. This will be done atomically. |
| 270 | MetaspaceUtils::inc_used(mdtype(), words); |
| 271 | } |
| 272 | |
| 273 | void SpaceManager::account_for_spacemanager_death() { |
| 274 | |
| 275 | assert_lock_strong(MetaspaceExpand_lock); |
| 276 | |
| 277 | MetaspaceUtils::dec_capacity(mdtype(), _capacity_words); |
| 278 | MetaspaceUtils::dec_overhead(mdtype(), _overhead_words); |
| 279 | MetaspaceUtils::dec_used(mdtype(), _used_words); |
| 280 | } |
| 281 | |
| 282 | SpaceManager::~SpaceManager() { |
| 283 | |
| 284 | // This call this->_lock which can't be done while holding MetaspaceExpand_lock |
| 285 | DEBUG_ONLY(verify_metrics()); |
| 286 | |
| 287 | MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); |
| 288 | |
| 289 | account_for_spacemanager_death(); |
| 290 | |
| 291 | Log(gc, metaspace, freelist) log; |
| 292 | if (log.is_trace()) { |
| 293 | log.trace("~SpaceManager(): " PTR_FORMAT, p2i(this)); |
| 294 | ResourceMark rm; |
| 295 | LogStream ls(log.trace()); |
| 296 | locked_print_chunks_in_use_on(&ls); |
| 297 | if (block_freelists() != NULL) { |
| 298 | block_freelists()->print_on(&ls); |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | // Add all the chunks in use by this space manager |
| 303 | // to the global list of free chunks. |
| 304 | |
| 305 | // Follow each list of chunks-in-use and add them to the |
| 306 | // free lists. Each list is NULL terminated. |
| 307 | chunk_manager()->return_chunk_list(chunk_list()); |
| 308 | #ifdef ASSERT |
| 309 | _chunk_list = NULL; |
| 310 | _current_chunk = NULL; |
| 311 | #endif |
| 312 | |
| 313 | #ifdef ASSERT |
| 314 | EVERY_NTH(VerifyMetaspaceInterval) |
| 315 | chunk_manager()->locked_verify(true); |
| 316 | END_EVERY_NTH |
| 317 | #endif |
| 318 | |
| 319 | if (_block_freelists != NULL) { |
| 320 | delete _block_freelists; |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | void SpaceManager::deallocate(MetaWord* p, size_t word_size) { |
| 325 | assert_lock_strong(lock()); |
| 326 | // Allocations and deallocations are in raw_word_size |
| 327 | size_t raw_word_size = get_allocation_word_size(word_size); |
| 328 | // Lazily create a block_freelist |
| 329 | if (block_freelists() == NULL) { |
| 330 | _block_freelists = new BlockFreelist(); |
| 331 | } |
| 332 | block_freelists()->return_block(p, raw_word_size); |
| 333 | DEBUG_ONLY(Atomic::inc(&(g_internal_statistics.num_deallocs))); |
| 334 | } |
| 335 | |
| 336 | // Adds a chunk to the list of chunks in use. |
| 337 | void SpaceManager::add_chunk(Metachunk* new_chunk, bool make_current) { |
| 338 | |
| 339 | assert_lock_strong(_lock); |
| 340 | assert(new_chunk != NULL, "Should not be NULL" ); |
| 341 | assert(new_chunk->next() == NULL, "Should not be on a list" ); |
| 342 | |
| 343 | new_chunk->reset_empty(); |
| 344 | |
| 345 | // Find the correct list and and set the current |
| 346 | // chunk for that list. |
| 347 | ChunkIndex index = chunk_manager()->list_index(new_chunk->word_size()); |
| 348 | |
| 349 | if (make_current) { |
| 350 | // If we are to make the chunk current, retire the old current chunk and replace |
| 351 | // it with the new chunk. |
| 352 | retire_current_chunk(); |
| 353 | set_current_chunk(new_chunk); |
| 354 | } |
| 355 | |
| 356 | // Add the new chunk at the head of its respective chunk list. |
| 357 | new_chunk->set_next(_chunk_list); |
| 358 | _chunk_list = new_chunk; |
| 359 | |
| 360 | // Adjust counters. |
| 361 | account_for_new_chunk(new_chunk); |
| 362 | |
| 363 | assert(new_chunk->is_empty(), "Not ready for reuse" ); |
| 364 | Log(gc, metaspace, freelist) log; |
| 365 | if (log.is_trace()) { |
| 366 | log.trace("SpaceManager::added chunk: " ); |
| 367 | ResourceMark rm; |
| 368 | LogStream ls(log.trace()); |
| 369 | new_chunk->print_on(&ls); |
| 370 | chunk_manager()->locked_print_free_chunks(&ls); |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | void SpaceManager::retire_current_chunk() { |
| 375 | if (current_chunk() != NULL) { |
| 376 | size_t remaining_words = current_chunk()->free_word_size(); |
| 377 | if (remaining_words >= SmallBlocks::small_block_min_size()) { |
| 378 | MetaWord* ptr = current_chunk()->allocate(remaining_words); |
| 379 | deallocate(ptr, remaining_words); |
| 380 | account_for_allocation(remaining_words); |
| 381 | } |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | Metachunk* SpaceManager::get_new_chunk(size_t chunk_word_size) { |
| 386 | // Get a chunk from the chunk freelist |
| 387 | Metachunk* next = chunk_manager()->chunk_freelist_allocate(chunk_word_size); |
| 388 | |
| 389 | if (next == NULL) { |
| 390 | next = vs_list()->get_new_chunk(chunk_word_size, |
| 391 | medium_chunk_bunch()); |
| 392 | } |
| 393 | |
| 394 | Log(gc, metaspace, alloc) log; |
| 395 | if (log.is_trace() && next != NULL && |
| 396 | SpaceManager::is_humongous(next->word_size())) { |
| 397 | log.trace(" new humongous chunk word size " PTR_FORMAT, next->word_size()); |
| 398 | } |
| 399 | |
| 400 | return next; |
| 401 | } |
| 402 | |
| 403 | MetaWord* SpaceManager::allocate(size_t word_size) { |
| 404 | MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); |
| 405 | size_t raw_word_size = get_allocation_word_size(word_size); |
| 406 | BlockFreelist* fl = block_freelists(); |
| 407 | MetaWord* p = NULL; |
| 408 | |
| 409 | // Allocation from the dictionary is expensive in the sense that |
| 410 | // the dictionary has to be searched for a size. Don't allocate |
| 411 | // from the dictionary until it starts to get fat. Is this |
| 412 | // a reasonable policy? Maybe an skinny dictionary is fast enough |
| 413 | // for allocations. Do some profiling. JJJ |
| 414 | if (fl != NULL && fl->total_size() > allocation_from_dictionary_limit) { |
| 415 | p = fl->get_block(raw_word_size); |
| 416 | if (p != NULL) { |
| 417 | DEBUG_ONLY(Atomic::inc(&g_internal_statistics.num_allocs_from_deallocated_blocks)); |
| 418 | } |
| 419 | } |
| 420 | if (p == NULL) { |
| 421 | p = allocate_work(raw_word_size); |
| 422 | } |
| 423 | |
| 424 | #ifdef ASSERT |
| 425 | EVERY_NTH(VerifyMetaspaceInterval) |
| 426 | verify_metrics_locked(); |
| 427 | END_EVERY_NTH |
| 428 | #endif |
| 429 | |
| 430 | return p; |
| 431 | } |
| 432 | |
| 433 | // Returns the address of spaced allocated for "word_size". |
| 434 | // This methods does not know about blocks (Metablocks) |
| 435 | MetaWord* SpaceManager::allocate_work(size_t word_size) { |
| 436 | assert_lock_strong(lock()); |
| 437 | #ifdef ASSERT |
| 438 | if (Metadebug::test_metadata_failure()) { |
| 439 | return NULL; |
| 440 | } |
| 441 | #endif |
| 442 | // Is there space in the current chunk? |
| 443 | MetaWord* result = NULL; |
| 444 | |
| 445 | if (current_chunk() != NULL) { |
| 446 | result = current_chunk()->allocate(word_size); |
| 447 | } |
| 448 | |
| 449 | if (result == NULL) { |
| 450 | result = grow_and_allocate(word_size); |
| 451 | } |
| 452 | |
| 453 | if (result != NULL) { |
| 454 | account_for_allocation(word_size); |
| 455 | } |
| 456 | |
| 457 | return result; |
| 458 | } |
| 459 | |
| 460 | void SpaceManager::verify() { |
| 461 | Metachunk* curr = chunk_list(); |
| 462 | while (curr != NULL) { |
| 463 | DEBUG_ONLY(do_verify_chunk(curr);) |
| 464 | assert(curr->is_tagged_free() == false, "Chunk should be tagged as in use." ); |
| 465 | curr = curr->next(); |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | void SpaceManager::verify_chunk_size(Metachunk* chunk) { |
| 470 | assert(is_humongous(chunk->word_size()) || |
| 471 | chunk->word_size() == medium_chunk_size() || |
| 472 | chunk->word_size() == small_chunk_size() || |
| 473 | chunk->word_size() == specialized_chunk_size(), |
| 474 | "Chunk size is wrong" ); |
| 475 | return; |
| 476 | } |
| 477 | |
| 478 | void SpaceManager::add_to_statistics_locked(SpaceManagerStatistics* out) const { |
| 479 | assert_lock_strong(lock()); |
| 480 | Metachunk* chunk = chunk_list(); |
| 481 | while (chunk != NULL) { |
| 482 | UsedChunksStatistics& chunk_stat = out->chunk_stats(chunk->get_chunk_type()); |
| 483 | chunk_stat.add_num(1); |
| 484 | chunk_stat.add_cap(chunk->word_size()); |
| 485 | chunk_stat.add_overhead(Metachunk::overhead()); |
| 486 | chunk_stat.add_used(chunk->used_word_size() - Metachunk::overhead()); |
| 487 | if (chunk != current_chunk()) { |
| 488 | chunk_stat.add_waste(chunk->free_word_size()); |
| 489 | } else { |
| 490 | chunk_stat.add_free(chunk->free_word_size()); |
| 491 | } |
| 492 | chunk = chunk->next(); |
| 493 | } |
| 494 | if (block_freelists() != NULL) { |
| 495 | out->add_free_blocks_info(block_freelists()->num_blocks(), block_freelists()->total_size()); |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | void SpaceManager::add_to_statistics(SpaceManagerStatistics* out) const { |
| 500 | MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); |
| 501 | add_to_statistics_locked(out); |
| 502 | } |
| 503 | |
| 504 | #ifdef ASSERT |
| 505 | void SpaceManager::verify_metrics_locked() const { |
| 506 | assert_lock_strong(lock()); |
| 507 | |
| 508 | SpaceManagerStatistics stat; |
| 509 | add_to_statistics_locked(&stat); |
| 510 | |
| 511 | UsedChunksStatistics chunk_stats = stat.totals(); |
| 512 | |
| 513 | DEBUG_ONLY(chunk_stats.check_sanity()); |
| 514 | |
| 515 | assert_counter(_capacity_words, chunk_stats.cap(), "SpaceManager::_capacity_words" ); |
| 516 | assert_counter(_used_words, chunk_stats.used(), "SpaceManager::_used_words" ); |
| 517 | assert_counter(_overhead_words, chunk_stats.overhead(), "SpaceManager::_overhead_words" ); |
| 518 | } |
| 519 | |
| 520 | void SpaceManager::verify_metrics() const { |
| 521 | MutexLocker cl(lock(), Mutex::_no_safepoint_check_flag); |
| 522 | verify_metrics_locked(); |
| 523 | } |
| 524 | #endif // ASSERT |
| 525 | |
| 526 | |
| 527 | } // namespace metaspace |
| 528 | |
| 529 | |