| /* | |
| * Copyright (c) 2001, 2025, 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 | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| #include "cds/cdsConfig.hpp" | |
| #include "classfile/classLoaderData.hpp" | |
| #include "classfile/vmClasses.hpp" | |
| #include "gc/shared/allocTracer.hpp" | |
| #include "gc/shared/barrierSet.hpp" | |
| #include "gc/shared/collectedHeap.hpp" | |
| #include "gc/shared/collectedHeap.inline.hpp" | |
| #include "gc/shared/gc_globals.hpp" | |
| #include "gc/shared/gcHeapSummary.hpp" | |
| #include "gc/shared/gcLocker.hpp" | |
| #include "gc/shared/gcTrace.hpp" | |
| #include "gc/shared/gcTraceTime.inline.hpp" | |
| #include "gc/shared/gcVMOperations.hpp" | |
| #include "gc/shared/gcWhen.hpp" | |
| #include "gc/shared/memAllocator.hpp" | |
| #include "gc/shared/stringdedup/stringDedup.hpp" | |
| #include "gc/shared/tlab_globals.hpp" | |
| #include "logging/log.hpp" | |
| #include "logging/logStream.hpp" | |
| #include "memory/classLoaderMetaspace.hpp" | |
| #include "memory/metaspace.hpp" | |
| #include "memory/metaspaceUtils.hpp" | |
| #include "memory/reservedSpace.hpp" | |
| #include "memory/universe.hpp" | |
| #include "oops/instanceMirrorKlass.hpp" | |
| #include "oops/oop.inline.hpp" | |
| #include "runtime/handles.inline.hpp" | |
| #include "runtime/init.hpp" | |
| #include "runtime/javaThread.hpp" | |
| #include "runtime/perfData.hpp" | |
| #include "runtime/threadSMR.hpp" | |
| #include "runtime/vmThread.hpp" | |
| #include "services/heapDumper.hpp" | |
| #include "utilities/align.hpp" | |
| #include "utilities/copy.hpp" | |
| #include "utilities/events.hpp" | |
| #include "utilities/ostream.hpp" | |
| class ClassLoaderData; | |
| size_t CollectedHeap::_lab_alignment_reserve = SIZE_MAX; | |
| Klass* CollectedHeap::_filler_object_klass = nullptr; | |
| size_t CollectedHeap::_filler_array_max_size = 0; | |
| size_t CollectedHeap::_stack_chunk_max_size = 0; | |
| class GCLogMessage : public FormatBuffer<512> {}; | |
| template <> | |
| void EventLogBase<GCLogMessage>::print(outputStream* st, GCLogMessage& m) { | |
| st->print_raw(m); | |
| } | |
| class GCLog : public EventLogBase<GCLogMessage> { | |
| protected: | |
| virtual void log_usage(const CollectedHeap* heap, outputStream* st) const = 0; | |
| public: | |
| GCLog(const char* name, const char* handle) : EventLogBase<GCLogMessage>(name, handle) {} | |
| void log_gc(const CollectedHeap* heap, GCWhen::Type when); | |
| }; | |
| void GCLog::log_gc(const CollectedHeap* heap, GCWhen::Type when) { | |
| if (!should_log()) { | |
| return; | |
| } | |
| double timestamp = fetch_timestamp(); | |
| MutexLocker ml(&_mutex, Mutex::_no_safepoint_check_flag); | |
| int index = compute_log_index(); | |
| _records[index].thread = nullptr; // It's the GC thread so it's not that interesting. | |
| _records[index].timestamp = timestamp; | |
| stringStream st(_records[index].data.buffer(), _records[index].data.size()); | |
| st.print("{"); | |
| { | |
| heap->print_invocation_on(&st, _handle, when); | |
| StreamIndentor si(&st, 1); | |
| log_usage(heap, &st); | |
| } | |
| st.print_cr("}"); | |
| } | |
| class GCHeapLog : public GCLog { | |
| private: | |
| void log_usage(const CollectedHeap* heap, outputStream* st) const override { | |
| heap->print_heap_on(st); | |
| } | |
| public: | |
| GCHeapLog() : GCLog("GC Heap Usage History", "heap") {} | |
| }; | |
| class GCMetaspaceLog : public GCLog { | |
| private: | |
| void log_usage(const CollectedHeap* heap, outputStream* st) const override { | |
| MetaspaceUtils::print_on(st); | |
| } | |
| public: | |
| GCMetaspaceLog() : GCLog("Metaspace Usage History", "metaspace") {} | |
| }; | |
| ParallelObjectIterator::ParallelObjectIterator(uint thread_num) : | |
| _impl(Universe::heap()->parallel_object_iterator(thread_num)) | |
| {} | |
| ParallelObjectIterator::~ParallelObjectIterator() { | |
| delete _impl; | |
| } | |
| void ParallelObjectIterator::object_iterate(ObjectClosure* cl, uint worker_id) { | |
| _impl->object_iterate(cl, worker_id); | |
| } | |
| size_t CollectedHeap::unused() const { | |
| MutexLocker ml(Heap_lock); | |
| return capacity() - used(); | |
| } | |
| VirtualSpaceSummary CollectedHeap::create_heap_space_summary() { | |
| size_t capacity_in_words = capacity() / HeapWordSize; | |
| return VirtualSpaceSummary( | |
| _reserved.start(), _reserved.start() + capacity_in_words, _reserved.end()); | |
| } | |
| GCHeapSummary CollectedHeap::create_heap_summary() { | |
| VirtualSpaceSummary heap_space = create_heap_space_summary(); | |
| return GCHeapSummary(heap_space, used()); | |
| } | |
| MetaspaceSummary CollectedHeap::create_metaspace_summary() { | |
| const MetaspaceChunkFreeListSummary& ms_chunk_free_list_summary = | |
| MetaspaceUtils::chunk_free_list_summary(Metaspace::NonClassType); | |
| const MetaspaceChunkFreeListSummary& class_chunk_free_list_summary = | |
| MetaspaceUtils::chunk_free_list_summary(Metaspace::ClassType); | |
| return MetaspaceSummary(MetaspaceGC::capacity_until_GC(), | |
| MetaspaceUtils::get_combined_statistics(), | |
| ms_chunk_free_list_summary, class_chunk_free_list_summary); | |
| } | |
| bool CollectedHeap::contains_null(const oop* p) const { | |
| return *p == nullptr; | |
| } | |
| void CollectedHeap::print_invocation_on(outputStream* st, const char* type, GCWhen::Type when) const { | |
| st->print_cr("%s %s invocations=%u (full %u):", type, GCWhen::to_string(when), total_collections(), total_full_collections()); | |
| } | |
| void CollectedHeap::print_relative_to_gc(GCWhen::Type when) const { | |
| // Print heap information | |
| LogTarget(Debug, gc, heap) lt_heap; | |
| if (lt_heap.is_enabled()) { | |
| LogStream ls(lt_heap); | |
| print_invocation_on(&ls, "Heap", when); | |
| StreamIndentor si(&ls, 1); | |
| print_heap_on(&ls); | |
| } | |
| if (_heap_log != nullptr) { | |
| _heap_log->log_gc(this, when); | |
| } | |
| // Print metaspace information | |
| LogTarget(Debug, gc, metaspace) lt_metaspace; | |
| if (lt_metaspace.is_enabled()) { | |
| LogStream ls(lt_metaspace); | |
| print_invocation_on(&ls, "Metaspace", when); | |
| StreamIndentor indentor(&ls, 1); | |
| MetaspaceUtils::print_on(&ls); | |
| } | |
| if (_metaspace_log != nullptr) { | |
| _metaspace_log->log_gc(this, when); | |
| } | |
| } | |
| void CollectedHeap::print_before_gc() const { | |
| print_relative_to_gc(GCWhen::BeforeGC); | |
| } | |
| void CollectedHeap::print_after_gc() const { | |
| print_relative_to_gc(GCWhen::AfterGC); | |
| } | |
| void CollectedHeap::print() const { | |
| print_heap_on(tty); | |
| print_gc_on(tty); | |
| } | |
| void CollectedHeap::trace_heap(GCWhen::Type when, const GCTracer* gc_tracer) { | |
| const GCHeapSummary& heap_summary = create_heap_summary(); | |
| gc_tracer->report_gc_heap_summary(when, heap_summary); | |
| const MetaspaceSummary& metaspace_summary = create_metaspace_summary(); | |
| gc_tracer->report_metaspace_summary(when, metaspace_summary); | |
| } | |
| void CollectedHeap::trace_heap_before_gc(const GCTracer* gc_tracer) { | |
| trace_heap(GCWhen::BeforeGC, gc_tracer); | |
| } | |
| void CollectedHeap::trace_heap_after_gc(const GCTracer* gc_tracer) { | |
| trace_heap(GCWhen::AfterGC, gc_tracer); | |
| } | |
| // Default implementation, for collectors that don't support the feature. | |
| bool CollectedHeap::supports_concurrent_gc_breakpoints() const { | |
| return false; | |
| } | |
| static bool klass_is_sane(oop object) { | |
| if (UseCompactObjectHeaders) { | |
| // With compact headers, we can't safely access the Klass* when | |
| // the object has been forwarded, because non-full-GC-forwarding | |
| // temporarily overwrites the mark-word, and thus the Klass*, with | |
| // the forwarding pointer, and here we have no way to make a | |
| // distinction between Full-GC and regular GC forwarding. | |
| markWord mark = object->mark(); | |
| if (mark.is_forwarded()) { | |
| // We can't access the Klass*. We optimistically assume that | |
| // it is ok. This happens very rarely. | |
| return true; | |
| } | |
| return Metaspace::contains(mark.klass_without_asserts()); | |
| } | |
| return Metaspace::contains(object->klass_without_asserts()); | |
| } | |
| bool CollectedHeap::is_oop(oop object) const { | |
| if (!is_object_aligned(object)) { | |
| return false; | |
| } | |
| if (!is_in(object)) { | |
| return false; | |
| } | |
| if (!klass_is_sane(object)) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| // Memory state functions. | |
| CollectedHeap::CollectedHeap() : | |
| _capacity_at_last_gc(0), | |
| _used_at_last_gc(0), | |
| _soft_ref_policy(), | |
| _is_stw_gc_active(false), | |
| _last_whole_heap_examined_time_ns(os::javaTimeNanos()), | |
| _total_collections(0), | |
| _total_full_collections(0), | |
| _gc_cause(GCCause::_no_gc), | |
| _gc_lastcause(GCCause::_no_gc) | |
| { | |
| // If the minimum object size is greater than MinObjAlignment, we can | |
| // end up with a shard at the end of the buffer that's smaller than | |
| // the smallest object. We can't allow that because the buffer must | |
| // look like it's full of objects when we retire it, so we make | |
| // sure we have enough space for a filler int array object. | |
| size_t min_size = min_dummy_object_size(); | |
| _lab_alignment_reserve = min_size > (size_t)MinObjAlignment ? align_object_size(min_size) : 0; | |
| const size_t max_len = size_t(arrayOopDesc::max_array_length(T_INT)); | |
| const size_t elements_per_word = HeapWordSize / sizeof(jint); | |
| _filler_array_max_size = align_object_size(filler_array_hdr_size() + | |
| max_len / elements_per_word); | |
| NOT_PRODUCT(_promotion_failure_alot_count = 0;) | |
| NOT_PRODUCT(_promotion_failure_alot_gc_number = 0;) | |
| if (UsePerfData) { | |
| EXCEPTION_MARK; | |
| // create the gc cause jvmstat counters | |
| _perf_gc_cause = PerfDataManager::create_string_variable(SUN_GC, "cause", | |
| 80, GCCause::to_string(_gc_cause), CHECK); | |
| _perf_gc_lastcause = | |
| PerfDataManager::create_string_variable(SUN_GC, "lastCause", | |
| 80, GCCause::to_string(_gc_lastcause), CHECK); | |
| } | |
| // Create the ring log | |
| if (LogEvents) { | |
| _metaspace_log = new GCMetaspaceLog(); | |
| _heap_log = new GCHeapLog(); | |
| } else { | |
| _metaspace_log = nullptr; | |
| _heap_log = nullptr; | |
| } | |
| } | |
| // This interface assumes that it's being called by the | |
| // vm thread. It collects the heap assuming that the | |
| // heap lock is already held and that we are executing in | |
| // the context of the vm thread. | |
| void CollectedHeap::collect_as_vm_thread(GCCause::Cause cause) { | |
| Thread* thread = Thread::current(); | |
| assert(thread->is_VM_thread(), "Precondition#1"); | |
| assert(Heap_lock->is_locked(), "Precondition#2"); | |
| GCCauseSetter gcs(this, cause); | |
| switch (cause) { | |
| case GCCause::_codecache_GC_threshold: | |
| case GCCause::_codecache_GC_aggressive: | |
| case GCCause::_heap_inspection: | |
| case GCCause::_heap_dump: | |
| case GCCause::_metadata_GC_threshold: { | |
| HandleMark hm(thread); | |
| do_full_collection(false); // don't clear all soft refs | |
| break; | |
| } | |
| case GCCause::_metadata_GC_clear_soft_refs: { | |
| HandleMark hm(thread); | |
| do_full_collection(true); // do clear all soft refs | |
| break; | |
| } | |
| default: | |
| ShouldNotReachHere(); // Unexpected use of this function | |
| } | |
| } | |
| MetaWord* CollectedHeap::satisfy_failed_metadata_allocation(ClassLoaderData* loader_data, | |
| size_t word_size, | |
| Metaspace::MetadataType mdtype) { | |
| uint loop_count = 0; | |
| uint gc_count = 0; | |
| uint full_gc_count = 0; | |
| assert(!Heap_lock->owned_by_self(), "Should not be holding the Heap_lock"); | |
| do { | |
| MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype); | |
| if (result != nullptr) { | |
| return result; | |
| } | |
| { // Need lock to get self consistent gc_count's | |
| MutexLocker ml(Heap_lock); | |
| gc_count = total_collections(); | |
| full_gc_count = total_full_collections(); | |
| } | |
| // Generate a VM operation | |
| VM_CollectForMetadataAllocation op(loader_data, | |
| word_size, | |
| mdtype, | |
| gc_count, | |
| full_gc_count, | |
| GCCause::_metadata_GC_threshold); | |
| VMThread::execute(&op); | |
| if (op.gc_succeeded()) { | |
| return op.result(); | |
| } | |
| loop_count++; | |
| if ((QueuedAllocationWarningCount > 0) && | |
| (loop_count % QueuedAllocationWarningCount == 0)) { | |
| log_warning(gc, ergo)("satisfy_failed_metadata_allocation() retries %d times," | |
| " size=%zu", loop_count, word_size); | |
| } | |
| } while (true); // Until a GC is done | |
| } | |
| MemoryUsage CollectedHeap::memory_usage() { | |
| return MemoryUsage(InitialHeapSize, used(), capacity(), max_capacity()); | |
| } | |
| void CollectedHeap::set_gc_cause(GCCause::Cause v) { | |
| if (UsePerfData) { | |
| _gc_lastcause = _gc_cause; | |
| _perf_gc_lastcause->set_value(GCCause::to_string(_gc_lastcause)); | |
| _perf_gc_cause->set_value(GCCause::to_string(v)); | |
| } | |
| _gc_cause = v; | |
| } | |
| // Returns the header size in words aligned to the requirements of the | |
| // array object type. | |
| static int int_array_header_size() { | |
| size_t typesize_in_bytes = arrayOopDesc::header_size_in_bytes(); | |
| return (int)align_up(typesize_in_bytes, HeapWordSize)/HeapWordSize; | |
| } | |
| size_t CollectedHeap::max_tlab_size() const { | |
| // TLABs can't be bigger than we can fill with a int[Integer.MAX_VALUE]. | |
| // This restriction could be removed by enabling filling with multiple arrays. | |
| // If we compute that the reasonable way as | |
| // header_size + ((sizeof(jint) * max_jint) / HeapWordSize) | |
| // we'll overflow on the multiply, so we do the divide first. | |
| // We actually lose a little by dividing first, | |
| // but that just makes the TLAB somewhat smaller than the biggest array, | |
| // which is fine, since we'll be able to fill that. | |
| size_t max_int_size = int_array_header_size() + | |
| sizeof(jint) * | |
| ((juint) max_jint / (size_t) HeapWordSize); | |
| return align_down(max_int_size, MinObjAlignment); | |
| } | |
| size_t CollectedHeap::filler_array_hdr_size() { | |
| return align_object_offset(int_array_header_size()); // align to Long | |
| } | |
| size_t CollectedHeap::filler_array_min_size() { | |
| return align_object_size(filler_array_hdr_size()); // align to MinObjAlignment | |
| } | |
| void CollectedHeap::zap_filler_array_with(HeapWord* start, size_t words, juint value) { | |
| Copy::fill_to_words(start + filler_array_hdr_size(), | |
| words - filler_array_hdr_size(), value); | |
| } | |
| #ifdef ASSERT | |
| void CollectedHeap::fill_args_check(HeapWord* start, size_t words) | |
| { | |
| assert(words >= min_fill_size(), "too small to fill"); | |
| assert(is_object_aligned(words), "unaligned size"); | |
| } | |
| void CollectedHeap::zap_filler_array(HeapWord* start, size_t words, bool zap) | |
| { | |
| if (ZapFillerObjects && zap) { | |
| zap_filler_array_with(start, words, 0XDEAFBABE); | |
| } | |
| } | |
| #endif // ASSERT | |
| void | |
| CollectedHeap::fill_with_array(HeapWord* start, size_t words, bool zap) | |
| { | |
| assert(words >= filler_array_min_size(), "too small for an array"); | |
| assert(words <= filler_array_max_size(), "too big for a single object"); | |
| const size_t payload_size = words - filler_array_hdr_size(); | |
| const size_t len = payload_size * HeapWordSize / sizeof(jint); | |
| assert((int)len >= 0, "size too large %zu becomes %d", words, (int)len); | |
| ObjArrayAllocator allocator(Universe::fillerArrayKlass(), words, (int)len, /* do_zero */ false); | |
| allocator.initialize(start); | |
| if (CDSConfig::is_dumping_heap()) { | |
| // This array is written into the CDS archive. Make sure it | |
| // has deterministic contents. | |
| zap_filler_array_with(start, words, 0); | |
| } else { | |
| DEBUG_ONLY(zap_filler_array(start, words, zap);) | |
| } | |
| } | |
| void | |
| CollectedHeap::fill_with_object_impl(HeapWord* start, size_t words, bool zap) | |
| { | |
| assert(words <= filler_array_max_size(), "too big for a single object"); | |
| if (words >= filler_array_min_size()) { | |
| fill_with_array(start, words, zap); | |
| } else if (words > 0) { | |
| assert(words == min_fill_size(), "unaligned size"); | |
| ObjAllocator allocator(CollectedHeap::filler_object_klass(), words); | |
| allocator.initialize(start); | |
| } | |
| } | |
| void CollectedHeap::fill_with_object(HeapWord* start, size_t words, bool zap) | |
| { | |
| DEBUG_ONLY(fill_args_check(start, words);) | |
| HandleMark hm(Thread::current()); // Free handles before leaving. | |
| fill_with_object_impl(start, words, zap); | |
| } | |
| void CollectedHeap::fill_with_objects(HeapWord* start, size_t words, bool zap) | |
| { | |
| DEBUG_ONLY(fill_args_check(start, words);) | |
| HandleMark hm(Thread::current()); // Free handles before leaving. | |
| // Multiple objects may be required depending on the filler array maximum size. Fill | |
| // the range up to that with objects that are filler_array_max_size sized. The | |
| // remainder is filled with a single object. | |
| const size_t min = min_fill_size(); | |
| const size_t max = filler_array_max_size(); | |
| while (words > max) { | |
| const size_t cur = (words - max) >= min ? max : max - min; | |
| fill_with_array(start, cur, zap); | |
| start += cur; | |
| words -= cur; | |
| } | |
| fill_with_object_impl(start, words, zap); | |
| } | |
| void CollectedHeap::fill_with_dummy_object(HeapWord* start, HeapWord* end, bool zap) { | |
| CollectedHeap::fill_with_object(start, end, zap); | |
| } | |
| void CollectedHeap::ensure_parsability(bool retire_tlabs) { | |
| assert(SafepointSynchronize::is_at_safepoint() || !is_init_completed(), | |
| "Should only be called at a safepoint or at start-up"); | |
| ThreadLocalAllocStats stats; | |
| for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next();) { | |
| BarrierSet::barrier_set()->make_parsable(thread); | |
| if (UseTLAB) { | |
| if (retire_tlabs || ZeroTLAB) { | |
| thread->retire_tlab(&stats); | |
| } else { | |
| thread->tlab().make_parsable(); | |
| } | |
| } | |
| } | |
| stats.publish(); | |
| } | |
| void CollectedHeap::resize_all_tlabs() { | |
| assert(SafepointSynchronize::is_at_safepoint() || !is_init_completed(), | |
| "Should only resize tlabs at safepoint"); | |
| if (UseTLAB && ResizeTLAB) { | |
| for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) { | |
| thread->tlab().resize(); | |
| } | |
| } | |
| } | |
| jlong CollectedHeap::millis_since_last_whole_heap_examined() { | |
| return (os::javaTimeNanos() - _last_whole_heap_examined_time_ns) / NANOSECS_PER_MILLISEC; | |
| } | |
| void CollectedHeap::record_whole_heap_examined_timestamp() { | |
| _last_whole_heap_examined_time_ns = os::javaTimeNanos(); | |
| } | |
| void CollectedHeap::full_gc_dump(GCTimer* timer, bool before) { | |
| assert(timer != nullptr, "timer is null"); | |
| static uint count = 0; | |
| if ((HeapDumpBeforeFullGC && before) || (HeapDumpAfterFullGC && !before)) { | |
| if (FullGCHeapDumpLimit == 0 || count < FullGCHeapDumpLimit) { | |
| GCTraceTime(Info, gc) tm(before ? "Heap Dump (before full gc)" : "Heap Dump (after full gc)", timer); | |
| HeapDumper::dump_heap(); | |
| count++; | |
| } | |
| } | |
| LogTarget(Trace, gc, classhisto) lt; | |
| if (lt.is_enabled()) { | |
| GCTraceTime(Trace, gc, classhisto) tm(before ? "Class Histogram (before full gc)" : "Class Histogram (after full gc)", timer); | |
| LogStream ls(lt); | |
| VM_GC_HeapInspection inspector(&ls, false /* ! full gc */); | |
| inspector.doit(); | |
| } | |
| } | |
| void CollectedHeap::pre_full_gc_dump(GCTimer* timer) { | |
| full_gc_dump(timer, true); | |
| } | |
| void CollectedHeap::post_full_gc_dump(GCTimer* timer) { | |
| full_gc_dump(timer, false); | |
| } | |
| void CollectedHeap::initialize_reserved_region(const ReservedHeapSpace& rs) { | |
| // It is important to do this in a way such that concurrent readers can't | |
| // temporarily think something is in the heap. (Seen this happen in asserts.) | |
| _reserved.set_word_size(0); | |
| _reserved.set_start((HeapWord*)rs.base()); | |
| _reserved.set_end((HeapWord*)rs.end()); | |
| } | |
| void CollectedHeap::post_initialize() { | |
| StringDedup::initialize(); | |
| initialize_serviceability(); | |
| } | |
| #ifndef PRODUCT | |
| bool CollectedHeap::promotion_should_fail(volatile size_t* count) { | |
| // Access to count is not atomic; the value does not have to be exact. | |
| if (PromotionFailureALot) { | |
| const size_t gc_num = total_collections(); | |
| const size_t elapsed_gcs = gc_num - _promotion_failure_alot_gc_number; | |
| if (elapsed_gcs >= PromotionFailureALotInterval) { | |
| // Test for unsigned arithmetic wrap-around. | |
| if (++*count >= PromotionFailureALotCount) { | |
| *count = 0; | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| bool CollectedHeap::promotion_should_fail() { | |
| return promotion_should_fail(&_promotion_failure_alot_count); | |
| } | |
| void CollectedHeap::reset_promotion_should_fail(volatile size_t* count) { | |
| if (PromotionFailureALot) { | |
| _promotion_failure_alot_gc_number = total_collections(); | |
| *count = 0; | |
| } | |
| } | |
| void CollectedHeap::reset_promotion_should_fail() { | |
| reset_promotion_should_fail(&_promotion_failure_alot_count); | |
| } | |
| #endif // #ifndef PRODUCT | |
| // It's the caller's responsibility to ensure glitch-freedom | |
| // (if required). | |
| void CollectedHeap::update_capacity_and_used_at_gc() { | |
| _capacity_at_last_gc = capacity(); | |
| _used_at_last_gc = used(); | |
| } |