Index: src/heap.cc |
=================================================================== |
--- src/heap.cc (revision 9327) |
+++ src/heap.cc (working copy) |
@@ -36,13 +36,16 @@ |
#include "deoptimizer.h" |
#include "global-handles.h" |
#include "heap-profiler.h" |
+#include "incremental-marking.h" |
#include "liveobjectlist-inl.h" |
#include "mark-compact.h" |
#include "natives.h" |
#include "objects-visiting.h" |
+#include "objects-visiting-inl.h" |
#include "runtime-profiler.h" |
#include "scopeinfo.h" |
#include "snapshot.h" |
+#include "store-buffer.h" |
#include "v8threads.h" |
#include "vm-state-inl.h" |
#if V8_TARGET_ARCH_ARM && !V8_INTERPRETED_REGEXP |
@@ -58,10 +61,6 @@ |
namespace internal { |
-static const intptr_t kMinimumPromotionLimit = 2 * MB; |
-static const intptr_t kMinimumAllocationLimit = 8 * MB; |
- |
- |
static Mutex* gc_initializer_mutex = OS::CreateMutex(); |
@@ -70,27 +69,21 @@ |
// semispace_size_ should be a power of 2 and old_generation_size_ should be |
// a multiple of Page::kPageSize. |
#if defined(ANDROID) |
- reserved_semispace_size_(2*MB), |
- max_semispace_size_(2*MB), |
- initial_semispace_size_(128*KB), |
- max_old_generation_size_(192*MB), |
- max_executable_size_(max_old_generation_size_), |
+#define LUMP_OF_MEMORY (128 * KB) |
code_range_size_(0), |
#elif defined(V8_TARGET_ARCH_X64) |
- reserved_semispace_size_(16*MB), |
- max_semispace_size_(16*MB), |
- initial_semispace_size_(1*MB), |
- max_old_generation_size_(1400*MB), |
- max_executable_size_(256*MB), |
+#define LUMP_OF_MEMORY (2 * MB) |
code_range_size_(512*MB), |
#else |
- reserved_semispace_size_(8*MB), |
- max_semispace_size_(8*MB), |
- initial_semispace_size_(512*KB), |
- max_old_generation_size_(700*MB), |
- max_executable_size_(128*MB), |
+#define LUMP_OF_MEMORY MB |
code_range_size_(0), |
#endif |
+ reserved_semispace_size_(8 * Max(LUMP_OF_MEMORY, Page::kPageSize)), |
+ max_semispace_size_(8 * Max(LUMP_OF_MEMORY, Page::kPageSize)), |
+ initial_semispace_size_(Max(LUMP_OF_MEMORY, Page::kPageSize)), |
+ max_old_generation_size_(1400ul * LUMP_OF_MEMORY), |
+ max_executable_size_(256l * LUMP_OF_MEMORY), |
+ |
// Variables set based on semispace_size_ and old_generation_size_ in |
// ConfigureHeap (survived_since_last_expansion_, external_allocation_limit_) |
// Will be 4 * reserved_semispace_size_ to ensure that young |
@@ -100,6 +93,7 @@ |
always_allocate_scope_depth_(0), |
linear_allocation_scope_depth_(0), |
contexts_disposed_(0), |
+ scan_on_scavenge_pages_(0), |
new_space_(this), |
old_pointer_space_(NULL), |
old_data_space_(NULL), |
@@ -109,7 +103,6 @@ |
lo_space_(NULL), |
gc_state_(NOT_IN_GC), |
gc_post_processing_depth_(0), |
- mc_count_(0), |
ms_count_(0), |
gc_count_(0), |
unflattened_strings_length_(0), |
@@ -121,10 +114,13 @@ |
#endif // DEBUG |
old_gen_promotion_limit_(kMinimumPromotionLimit), |
old_gen_allocation_limit_(kMinimumAllocationLimit), |
+ old_gen_limit_factor_(1), |
+ size_of_old_gen_at_last_old_space_gc_(0), |
external_allocation_limit_(0), |
amount_of_external_allocated_memory_(0), |
amount_of_external_allocated_memory_at_last_global_gc_(0), |
old_gen_exhausted_(false), |
+ store_buffer_rebuilder_(store_buffer()), |
hidden_symbol_(NULL), |
global_gc_prologue_callback_(NULL), |
global_gc_epilogue_callback_(NULL), |
@@ -141,12 +137,15 @@ |
min_in_mutator_(kMaxInt), |
alive_after_last_gc_(0), |
last_gc_end_timestamp_(0.0), |
- page_watermark_invalidated_mark_(1 << Page::WATERMARK_INVALIDATED), |
+ store_buffer_(this), |
+ marking_(this), |
+ incremental_marking_(this), |
number_idle_notifications_(0), |
last_idle_notification_gc_count_(0), |
last_idle_notification_gc_count_init_(false), |
configured_(false), |
- is_safe_to_read_maps_(true) { |
+ last_empty_page_was_given_back_to_the_os_(false), |
+ chunks_queued_for_free_(NULL) { |
// Allow build-time customization of the max semispace size. Building |
// V8 with snapshots and a non-default max semispace size is much |
// easier if you can define it as part of the build environment. |
@@ -224,29 +223,10 @@ |
int Heap::GcSafeSizeOfOldObject(HeapObject* object) { |
- ASSERT(!HEAP->InNewSpace(object)); // Code only works for old objects. |
- ASSERT(!HEAP->mark_compact_collector()->are_map_pointers_encoded()); |
- MapWord map_word = object->map_word(); |
- map_word.ClearMark(); |
- map_word.ClearOverflow(); |
- return object->SizeFromMap(map_word.ToMap()); |
-} |
- |
- |
-int Heap::GcSafeSizeOfOldObjectWithEncodedMap(HeapObject* object) { |
- ASSERT(!HEAP->InNewSpace(object)); // Code only works for old objects. |
- ASSERT(HEAP->mark_compact_collector()->are_map_pointers_encoded()); |
- uint32_t marker = Memory::uint32_at(object->address()); |
- if (marker == MarkCompactCollector::kSingleFreeEncoding) { |
- return kIntSize; |
- } else if (marker == MarkCompactCollector::kMultiFreeEncoding) { |
- return Memory::int_at(object->address() + kIntSize); |
- } else { |
- MapWord map_word = object->map_word(); |
- Address map_address = map_word.DecodeMapAddress(HEAP->map_space()); |
- Map* map = reinterpret_cast<Map*>(HeapObject::FromAddress(map_address)); |
- return object->SizeFromMap(map); |
+ if (IntrusiveMarking::IsMarked(object)) { |
+ return IntrusiveMarking::SizeOfMarkedObject(object); |
} |
+ return object->SizeFromMap(object->map()); |
} |
@@ -400,6 +380,7 @@ |
#endif // DEBUG |
LiveObjectList::GCPrologue(); |
+ store_buffer()->GCPrologue(); |
} |
intptr_t Heap::SizeOfObjects() { |
@@ -412,6 +393,7 @@ |
} |
void Heap::GarbageCollectionEpilogue() { |
+ store_buffer()->GCEpilogue(); |
LiveObjectList::GCEpilogue(); |
#ifdef DEBUG |
allow_allocation(true); |
@@ -443,13 +425,13 @@ |
} |
-void Heap::CollectAllGarbage(bool force_compaction) { |
+void Heap::CollectAllGarbage(int flags) { |
// Since we are ignoring the return value, the exact choice of space does |
// not matter, so long as we do not specify NEW_SPACE, which would not |
// cause a full GC. |
- mark_compact_collector_.SetForceCompaction(force_compaction); |
+ mark_compact_collector_.SetFlags(flags); |
CollectGarbage(OLD_POINTER_SPACE); |
- mark_compact_collector_.SetForceCompaction(false); |
+ mark_compact_collector_.SetFlags(kNoGCFlags); |
} |
@@ -457,8 +439,6 @@ |
// Since we are ignoring the return value, the exact choice of space does |
// not matter, so long as we do not specify NEW_SPACE, which would not |
// cause a full GC. |
- mark_compact_collector()->SetForceCompaction(true); |
- |
// Major GC would invoke weak handle callbacks on weakly reachable |
// handles, but won't collect weakly reachable objects until next |
// major GC. Therefore if we collect aggressively and weak handle callback |
@@ -467,13 +447,14 @@ |
// Note: as weak callbacks can execute arbitrary code, we cannot |
// hope that eventually there will be no weak callbacks invocations. |
// Therefore stop recollecting after several attempts. |
+ mark_compact_collector()->SetFlags(kMakeHeapIterableMask); |
const int kMaxNumberOfAttempts = 7; |
for (int attempt = 0; attempt < kMaxNumberOfAttempts; attempt++) { |
if (!CollectGarbage(OLD_POINTER_SPACE, MARK_COMPACTOR)) { |
break; |
} |
} |
- mark_compact_collector()->SetForceCompaction(false); |
+ mark_compact_collector()->SetFlags(kNoGCFlags); |
} |
@@ -490,6 +471,23 @@ |
allocation_timeout_ = Max(6, FLAG_gc_interval); |
#endif |
+ if (collector == SCAVENGER && !incremental_marking()->IsStopped()) { |
+ if (FLAG_trace_incremental_marking) { |
+ PrintF("[IncrementalMarking] Scavenge during marking.\n"); |
+ } |
+ } |
+ |
+ if (collector == MARK_COMPACTOR && |
+ !mark_compact_collector()->PreciseSweepingRequired() && |
+ !incremental_marking()->IsStopped() && |
+ !incremental_marking()->should_hurry() && |
+ FLAG_incremental_marking_steps) { |
+ if (FLAG_trace_incremental_marking) { |
+ PrintF("[IncrementalMarking] Delaying MarkSweep.\n"); |
+ } |
+ collector = SCAVENGER; |
+ } |
+ |
bool next_gc_likely_to_collect_more = false; |
{ GCTracer tracer(this); |
@@ -512,13 +510,24 @@ |
GarbageCollectionEpilogue(); |
} |
+ ASSERT(collector == SCAVENGER || incremental_marking()->IsStopped()); |
+ if (incremental_marking()->IsStopped()) { |
+ if (incremental_marking()->WorthActivating() && NextGCIsLikelyToBeFull()) { |
+ incremental_marking()->Start(); |
+ } |
+ } |
+ |
return next_gc_likely_to_collect_more; |
} |
void Heap::PerformScavenge() { |
GCTracer tracer(this); |
- PerformGarbageCollection(SCAVENGER, &tracer); |
+ if (incremental_marking()->IsStopped()) { |
+ PerformGarbageCollection(SCAVENGER, &tracer); |
+ } else { |
+ PerformGarbageCollection(MARK_COMPACTOR, &tracer); |
+ } |
} |
@@ -610,13 +619,6 @@ |
// Committing memory to from space failed. |
// Try shrinking and try again. |
- PagedSpaces spaces; |
- for (PagedSpace* space = spaces.next(); |
- space != NULL; |
- space = spaces.next()) { |
- space->RelinkPageListInChunkOrder(true); |
- } |
- |
Shrink(); |
if (new_space_.CommitFromSpaceIfNeeded()) return; |
@@ -647,7 +649,10 @@ |
void Heap::ClearNormalizedMapCaches() { |
- if (isolate_->bootstrapper()->IsActive()) return; |
+ if (isolate_->bootstrapper()->IsActive() && |
+ !incremental_marking()->IsMarking()) { |
+ return; |
+ } |
Object* context = global_contexts_list_; |
while (!context->IsUndefined()) { |
@@ -657,24 +662,6 @@ |
} |
-#ifdef DEBUG |
- |
-enum PageWatermarkValidity { |
- ALL_VALID, |
- ALL_INVALID |
-}; |
- |
-static void VerifyPageWatermarkValidity(PagedSpace* space, |
- PageWatermarkValidity validity) { |
- PageIterator it(space, PageIterator::PAGES_IN_USE); |
- bool expected_value = (validity == ALL_VALID); |
- while (it.has_next()) { |
- Page* page = it.next(); |
- ASSERT(page->IsWatermarkValid() == expected_value); |
- } |
-} |
-#endif |
- |
void Heap::UpdateSurvivalRateTrend(int start_new_space_size) { |
double survival_rate = |
(static_cast<double>(young_survivors_after_last_gc_) * 100) / |
@@ -727,6 +714,13 @@ |
int start_new_space_size = Heap::new_space()->SizeAsInt(); |
+ if (IsHighSurvivalRate()) { |
+ // We speed up the incremental marker if it is running so that it |
+ // does not fall behind the rate of promotion, which would cause a |
+ // constantly growing old space. |
+ incremental_marking()->NotifyOfHighPromotionRate(); |
+ } |
+ |
if (collector == MARK_COMPACTOR) { |
// Perform mark-sweep with optional compaction. |
MarkCompact(tracer); |
@@ -736,11 +730,7 @@ |
UpdateSurvivalRateTrend(start_new_space_size); |
- intptr_t old_gen_size = PromotedSpaceSize(); |
- old_gen_promotion_limit_ = |
- old_gen_size + Max(kMinimumPromotionLimit, old_gen_size / 3); |
- old_gen_allocation_limit_ = |
- old_gen_size + Max(kMinimumAllocationLimit, old_gen_size / 2); |
+ size_of_old_gen_at_last_old_space_gc_ = PromotedSpaceSize(); |
if (high_survival_rate_during_scavenges && |
IsStableOrIncreasingSurvivalTrend()) { |
@@ -750,10 +740,16 @@ |
// In this case we aggressively raise old generation memory limits to |
// postpone subsequent mark-sweep collection and thus trade memory |
// space for the mutation speed. |
- old_gen_promotion_limit_ *= 2; |
- old_gen_allocation_limit_ *= 2; |
+ old_gen_limit_factor_ = 2; |
+ } else { |
+ old_gen_limit_factor_ = 1; |
} |
+ old_gen_promotion_limit_ = |
+ OldGenPromotionLimit(size_of_old_gen_at_last_old_space_gc_); |
+ old_gen_allocation_limit_ = |
+ OldGenAllocationLimit(size_of_old_gen_at_last_old_space_gc_); |
+ |
old_gen_exhausted_ = false; |
} else { |
tracer_ = tracer; |
@@ -782,9 +778,7 @@ |
amount_of_external_allocated_memory_; |
} |
- GCCallbackFlags callback_flags = tracer->is_compacting() |
- ? kGCCallbackFlagCompacted |
- : kNoGCCallbackFlags; |
+ GCCallbackFlags callback_flags = kNoGCCallbackFlags; |
for (int i = 0; i < gc_epilogue_callbacks_.length(); ++i) { |
if (gc_type & gc_epilogue_callbacks_[i].gc_type) { |
gc_epilogue_callbacks_[i].callback(gc_type, callback_flags); |
@@ -808,20 +802,12 @@ |
mark_compact_collector_.Prepare(tracer); |
- bool is_compacting = mark_compact_collector_.IsCompacting(); |
+ ms_count_++; |
+ tracer->set_full_gc_count(ms_count_); |
- if (is_compacting) { |
- mc_count_++; |
- } else { |
- ms_count_++; |
- } |
- tracer->set_full_gc_count(mc_count_ + ms_count_); |
+ MarkCompactPrologue(); |
- MarkCompactPrologue(is_compacting); |
- |
- is_safe_to_read_maps_ = false; |
mark_compact_collector_.CollectGarbage(); |
- is_safe_to_read_maps_ = true; |
LOG(isolate_, ResourceEvent("markcompact", "end")); |
@@ -835,7 +821,7 @@ |
} |
-void Heap::MarkCompactPrologue(bool is_compacting) { |
+void Heap::MarkCompactPrologue() { |
// At any old GC clear the keyed lookup cache to enable collection of unused |
// maps. |
isolate_->keyed_lookup_cache()->Clear(); |
@@ -847,7 +833,8 @@ |
CompletelyClearInstanceofCache(); |
- if (is_compacting) FlushNumberStringCache(); |
+ // TODO(1605) select heuristic for flushing NumberString cache with |
+ // FlushNumberStringCache |
if (FLAG_cleanup_code_caches_at_gc) { |
polymorphic_code_cache()->set_cache(undefined_value()); |
} |
@@ -911,14 +898,18 @@ |
// do not expect them. |
VerifyNonPointerSpacePointersVisitor v; |
HeapObjectIterator code_it(HEAP->code_space()); |
- for (HeapObject* object = code_it.next(); |
- object != NULL; object = code_it.next()) |
+ for (HeapObject* object = code_it.Next(); |
+ object != NULL; object = code_it.Next()) |
object->Iterate(&v); |
- HeapObjectIterator data_it(HEAP->old_data_space()); |
- for (HeapObject* object = data_it.next(); |
- object != NULL; object = data_it.next()) |
- object->Iterate(&v); |
+ // The old data space was normally swept conservatively so that the iterator |
+ // doesn't work, so we normally skip the next bit. |
+ if (!HEAP->old_data_space()->was_swept_conservatively()) { |
+ HeapObjectIterator data_it(HEAP->old_data_space()); |
+ for (HeapObject* object = data_it.Next(); |
+ object != NULL; object = data_it.Next()) |
+ object->Iterate(&v); |
+ } |
} |
#endif |
@@ -940,6 +931,64 @@ |
} |
+void Heap::ScavengeStoreBufferCallback( |
+ Heap* heap, |
+ MemoryChunk* page, |
+ StoreBufferEvent event) { |
+ heap->store_buffer_rebuilder_.Callback(page, event); |
+} |
+ |
+ |
+void StoreBufferRebuilder::Callback(MemoryChunk* page, StoreBufferEvent event) { |
+ if (event == kStoreBufferStartScanningPagesEvent) { |
+ start_of_current_page_ = NULL; |
+ current_page_ = NULL; |
+ } else if (event == kStoreBufferScanningPageEvent) { |
+ if (current_page_ != NULL) { |
+ // If this page already overflowed the store buffer during this iteration. |
+ if (current_page_->scan_on_scavenge()) { |
+ // Then we should wipe out the entries that have been added for it. |
+ store_buffer_->SetTop(start_of_current_page_); |
+ } else if (store_buffer_->Top() - start_of_current_page_ >= |
+ (store_buffer_->Limit() - store_buffer_->Top()) >> 2) { |
+ // Did we find too many pointers in the previous page? The heuristic is |
+ // that no page can take more then 1/5 the remaining slots in the store |
+ // buffer. |
+ current_page_->set_scan_on_scavenge(true); |
+ store_buffer_->SetTop(start_of_current_page_); |
+ } else { |
+ // In this case the page we scanned took a reasonable number of slots in |
+ // the store buffer. It has now been rehabilitated and is no longer |
+ // marked scan_on_scavenge. |
+ ASSERT(!current_page_->scan_on_scavenge()); |
+ } |
+ } |
+ start_of_current_page_ = store_buffer_->Top(); |
+ current_page_ = page; |
+ } else if (event == kStoreBufferFullEvent) { |
+ // The current page overflowed the store buffer again. Wipe out its entries |
+ // in the store buffer and mark it scan-on-scavenge again. This may happen |
+ // several times while scanning. |
+ if (current_page_ == NULL) { |
+ // Store Buffer overflowed while scanning promoted objects. These are not |
+ // in any particular page, though they are likely to be clustered by the |
+ // allocation routines. |
+ store_buffer_->HandleFullness(); |
+ } else { |
+ // Store Buffer overflowed while scanning a particular old space page for |
+ // pointers to new space. |
+ ASSERT(current_page_ == page); |
+ ASSERT(page != NULL); |
+ current_page_->set_scan_on_scavenge(true); |
+ ASSERT(start_of_current_page_ != store_buffer_->Top()); |
+ store_buffer_->SetTop(start_of_current_page_); |
+ } |
+ } else { |
+ UNREACHABLE(); |
+ } |
+} |
+ |
+ |
void Heap::Scavenge() { |
#ifdef DEBUG |
if (FLAG_enable_slow_asserts) VerifyNonPointerSpacePointers(); |
@@ -947,22 +996,6 @@ |
gc_state_ = SCAVENGE; |
- SwitchScavengingVisitorsTableIfProfilingWasEnabled(); |
- |
- Page::FlipMeaningOfInvalidatedWatermarkFlag(this); |
-#ifdef DEBUG |
- VerifyPageWatermarkValidity(old_pointer_space_, ALL_VALID); |
- VerifyPageWatermarkValidity(map_space_, ALL_VALID); |
-#endif |
- |
- // We do not update an allocation watermark of the top page during linear |
- // allocation to avoid overhead. So to maintain the watermark invariant |
- // we have to manually cache the watermark and mark the top page as having an |
- // invalid watermark. This guarantees that dirty regions iteration will use a |
- // correct watermark even if a linear allocation happens. |
- old_pointer_space_->FlushTopPageWatermark(); |
- map_space_->FlushTopPageWatermark(); |
- |
// Implements Cheney's copying algorithm |
LOG(isolate_, ResourceEvent("scavenge", "begin")); |
@@ -974,6 +1007,13 @@ |
CheckNewSpaceExpansionCriteria(); |
+ SelectScavengingVisitorsTable(); |
+ |
+ incremental_marking()->PrepareForScavenge(); |
+ |
+ old_pointer_space()->AdvanceSweeper(new_space_.Size()); |
+ old_data_space()->AdvanceSweeper(new_space_.Size()); |
+ |
// Flip the semispaces. After flipping, to space is empty, from space has |
// live objects. |
new_space_.Flip(); |
@@ -996,32 +1036,29 @@ |
// for the addresses of promoted objects: every object promoted |
// frees up its size in bytes from the top of the new space, and |
// objects are at least one pointer in size. |
- Address new_space_front = new_space_.ToSpaceLow(); |
- promotion_queue_.Initialize(new_space_.ToSpaceHigh()); |
+ Address new_space_front = new_space_.ToSpaceStart(); |
+ promotion_queue_.Initialize(new_space_.ToSpaceEnd()); |
- is_safe_to_read_maps_ = false; |
+#ifdef DEBUG |
+ store_buffer()->Clean(); |
+#endif |
+ |
ScavengeVisitor scavenge_visitor(this); |
// Copy roots. |
IterateRoots(&scavenge_visitor, VISIT_ALL_IN_SCAVENGE); |
- // Copy objects reachable from the old generation. By definition, |
- // there are no intergenerational pointers in code or data spaces. |
- IterateDirtyRegions(old_pointer_space_, |
- &Heap::IteratePointersInDirtyRegion, |
- &ScavengePointer, |
- WATERMARK_CAN_BE_INVALID); |
+ // Copy objects reachable from the old generation. |
+ { |
+ StoreBufferRebuildScope scope(this, |
+ store_buffer(), |
+ &ScavengeStoreBufferCallback); |
+ store_buffer()->IteratePointersToNewSpace(&ScavengeObject); |
+ } |
- IterateDirtyRegions(map_space_, |
- &IteratePointersInDirtyMapsRegion, |
- &ScavengePointer, |
- WATERMARK_CAN_BE_INVALID); |
- |
- lo_space_->IterateDirtyRegions(&ScavengePointer); |
- |
// Copy objects reachable from cells by scavenging cell values directly. |
HeapObjectIterator cell_iterator(cell_space_); |
- for (HeapObject* cell = cell_iterator.next(); |
- cell != NULL; cell = cell_iterator.next()) { |
+ for (HeapObject* cell = cell_iterator.Next(); |
+ cell != NULL; cell = cell_iterator.Next()) { |
if (cell->IsJSGlobalPropertyCell()) { |
Address value_address = |
reinterpret_cast<Address>(cell) + |
@@ -1046,14 +1083,16 @@ |
LiveObjectList::UpdateReferencesForScavengeGC(); |
isolate()->runtime_profiler()->UpdateSamplesAfterScavenge(); |
+ incremental_marking()->UpdateMarkingDequeAfterScavenge(); |
ASSERT(new_space_front == new_space_.top()); |
- is_safe_to_read_maps_ = true; |
- |
// Set age mark. |
new_space_.set_age_mark(new_space_.top()); |
+ new_space_.LowerInlineAllocationLimit( |
+ new_space_.inline_allocation_limit_step()); |
+ |
// Update how much has survived scavenge. |
IncrementYoungSurvivorsCounter(static_cast<int>( |
(PromotedSpaceSize() - survived_watermark) + new_space_.Size())); |
@@ -1112,35 +1151,56 @@ |
} |
+void Heap::UpdateReferencesInExternalStringTable( |
+ ExternalStringTableUpdaterCallback updater_func) { |
+ |
+ // Update old space string references. |
+ if (external_string_table_.old_space_strings_.length() > 0) { |
+ Object** start = &external_string_table_.old_space_strings_[0]; |
+ Object** end = start + external_string_table_.old_space_strings_.length(); |
+ for (Object** p = start; p < end; ++p) *p = updater_func(this, p); |
+ } |
+ |
+ UpdateNewSpaceReferencesInExternalStringTable(updater_func); |
+} |
+ |
+ |
static Object* ProcessFunctionWeakReferences(Heap* heap, |
Object* function, |
WeakObjectRetainer* retainer) { |
- Object* head = heap->undefined_value(); |
+ Object* undefined = heap->undefined_value(); |
+ Object* head = undefined; |
JSFunction* tail = NULL; |
Object* candidate = function; |
- while (candidate != heap->undefined_value()) { |
+ while (candidate != undefined) { |
// Check whether to keep the candidate in the list. |
JSFunction* candidate_function = reinterpret_cast<JSFunction*>(candidate); |
Object* retain = retainer->RetainAs(candidate); |
if (retain != NULL) { |
- if (head == heap->undefined_value()) { |
+ if (head == undefined) { |
// First element in the list. |
- head = candidate_function; |
+ head = retain; |
} else { |
// Subsequent elements in the list. |
ASSERT(tail != NULL); |
- tail->set_next_function_link(candidate_function); |
+ tail->set_next_function_link(retain); |
} |
// Retained function is new tail. |
+ candidate_function = reinterpret_cast<JSFunction*>(retain); |
tail = candidate_function; |
+ |
+ ASSERT(retain->IsUndefined() || retain->IsJSFunction()); |
+ |
+ if (retain == undefined) break; |
} |
+ |
// Move to next element in the list. |
candidate = candidate_function->next_function_link(); |
} |
// Terminate the list if there is one or more elements. |
if (tail != NULL) { |
- tail->set_next_function_link(heap->undefined_value()); |
+ tail->set_next_function_link(undefined); |
} |
return head; |
@@ -1148,28 +1208,32 @@ |
void Heap::ProcessWeakReferences(WeakObjectRetainer* retainer) { |
- Object* head = undefined_value(); |
+ Object* undefined = undefined_value(); |
+ Object* head = undefined; |
Context* tail = NULL; |
Object* candidate = global_contexts_list_; |
- while (candidate != undefined_value()) { |
+ while (candidate != undefined) { |
// Check whether to keep the candidate in the list. |
Context* candidate_context = reinterpret_cast<Context*>(candidate); |
Object* retain = retainer->RetainAs(candidate); |
if (retain != NULL) { |
- if (head == undefined_value()) { |
+ if (head == undefined) { |
// First element in the list. |
- head = candidate_context; |
+ head = retain; |
} else { |
// Subsequent elements in the list. |
ASSERT(tail != NULL); |
tail->set_unchecked(this, |
Context::NEXT_CONTEXT_LINK, |
- candidate_context, |
+ retain, |
UPDATE_WRITE_BARRIER); |
} |
// Retained context is new tail. |
+ candidate_context = reinterpret_cast<Context*>(retain); |
tail = candidate_context; |
+ if (retain == undefined) break; |
+ |
// Process the weak list of optimized functions for the context. |
Object* function_list_head = |
ProcessFunctionWeakReferences( |
@@ -1181,6 +1245,7 @@ |
function_list_head, |
UPDATE_WRITE_BARRIER); |
} |
+ |
// Move to next element in the list. |
candidate = candidate_context->get(Context::NEXT_CONTEXT_LINK); |
} |
@@ -1212,35 +1277,45 @@ |
Address Heap::DoScavenge(ObjectVisitor* scavenge_visitor, |
Address new_space_front) { |
do { |
- ASSERT(new_space_front <= new_space_.top()); |
- |
+ SemiSpace::AssertValidRange(new_space_front, new_space_.top()); |
// The addresses new_space_front and new_space_.top() define a |
// queue of unprocessed copied objects. Process them until the |
// queue is empty. |
- while (new_space_front < new_space_.top()) { |
- HeapObject* object = HeapObject::FromAddress(new_space_front); |
- new_space_front += NewSpaceScavenger::IterateBody(object->map(), object); |
+ while (new_space_front != new_space_.top()) { |
+ if (!NewSpacePage::IsAtEnd(new_space_front)) { |
+ HeapObject* object = HeapObject::FromAddress(new_space_front); |
+ new_space_front += |
+ NewSpaceScavenger::IterateBody(object->map(), object); |
+ } else { |
+ new_space_front = |
+ NewSpacePage::FromLimit(new_space_front)->next_page()->body(); |
+ } |
} |
// Promote and process all the to-be-promoted objects. |
- while (!promotion_queue_.is_empty()) { |
- HeapObject* target; |
- int size; |
- promotion_queue_.remove(&target, &size); |
+ { |
+ StoreBufferRebuildScope scope(this, |
+ store_buffer(), |
+ &ScavengeStoreBufferCallback); |
+ while (!promotion_queue()->is_empty()) { |
+ HeapObject* target; |
+ int size; |
+ promotion_queue()->remove(&target, &size); |
- // Promoted object might be already partially visited |
- // during dirty regions iteration. Thus we search specificly |
- // for pointers to from semispace instead of looking for pointers |
- // to new space. |
- ASSERT(!target->IsMap()); |
- IterateAndMarkPointersToFromSpace(target->address(), |
- target->address() + size, |
- &ScavengePointer); |
+ // Promoted object might be already partially visited |
+ // during old space pointer iteration. Thus we search specificly |
+ // for pointers to from semispace instead of looking for pointers |
+ // to new space. |
+ ASSERT(!target->IsMap()); |
+ IterateAndMarkPointersToFromSpace(target->address(), |
+ target->address() + size, |
+ &ScavengeObject); |
+ } |
} |
// Take another spin if there are now unswept objects in new space |
// (there are currently no more unswept promoted objects). |
- } while (new_space_front < new_space_.top()); |
+ } while (new_space_front != new_space_.top()); |
return new_space_front; |
} |
@@ -1252,26 +1327,11 @@ |
}; |
-typedef void (*ScavengingCallback)(Map* map, |
- HeapObject** slot, |
- HeapObject* object); |
+enum MarksHandling { TRANSFER_MARKS, IGNORE_MARKS }; |
-static Atomic32 scavenging_visitors_table_mode_; |
-static VisitorDispatchTable<ScavengingCallback> scavenging_visitors_table_; |
- |
- |
-INLINE(static void DoScavengeObject(Map* map, |
- HeapObject** slot, |
- HeapObject* obj)); |
- |
- |
-void DoScavengeObject(Map* map, HeapObject** slot, HeapObject* obj) { |
- scavenging_visitors_table_.GetVisitor(map)(map, slot, obj); |
-} |
- |
- |
-template<LoggingAndProfiling logging_and_profiling_mode> |
+template<MarksHandling marks_handling, |
+ LoggingAndProfiling logging_and_profiling_mode> |
class ScavengingVisitor : public StaticVisitorBase { |
public: |
static void Initialize() { |
@@ -1306,9 +1366,13 @@ |
&ObjectEvacuationStrategy<POINTER_OBJECT>:: |
Visit); |
- table_.Register(kVisitJSFunction, |
- &ObjectEvacuationStrategy<POINTER_OBJECT>:: |
- template VisitSpecialized<JSFunction::kSize>); |
+ if (marks_handling == IGNORE_MARKS) { |
+ table_.Register(kVisitJSFunction, |
+ &ObjectEvacuationStrategy<POINTER_OBJECT>:: |
+ template VisitSpecialized<JSFunction::kSize>); |
+ } else { |
+ table_.Register(kVisitJSFunction, &EvacuateJSFunction); |
+ } |
table_.RegisterSpecializations<ObjectEvacuationStrategy<DATA_OBJECT>, |
kVisitDataObject, |
@@ -1373,10 +1437,15 @@ |
} |
} |
+ if (marks_handling == TRANSFER_MARKS) { |
+ if (Marking::TransferColor(source, target)) { |
+ MemoryChunk::IncrementLiveBytes(target->address(), size); |
+ } |
+ } |
+ |
return target; |
} |
- |
template<ObjectContents object_contents, SizeRestriction size_restriction> |
static inline void EvacuateObject(Map* map, |
HeapObject** slot, |
@@ -1386,13 +1455,14 @@ |
(object_size <= Page::kMaxHeapObjectSize)); |
ASSERT(object->Size() == object_size); |
- Heap* heap = map->heap(); |
+ Heap* heap = map->GetHeap(); |
if (heap->ShouldBePromoted(object->address(), object_size)) { |
MaybeObject* maybe_result; |
if ((size_restriction != SMALL) && |
(object_size > Page::kMaxHeapObjectSize)) { |
- maybe_result = heap->lo_space()->AllocateRawFixedArray(object_size); |
+ maybe_result = heap->lo_space()->AllocateRaw(object_size, |
+ NOT_EXECUTABLE); |
} else { |
if (object_contents == DATA_OBJECT) { |
maybe_result = heap->old_data_space()->AllocateRaw(object_size); |
@@ -1414,13 +1484,36 @@ |
return; |
} |
} |
- Object* result = |
- heap->new_space()->AllocateRaw(object_size)->ToObjectUnchecked(); |
+ MaybeObject* allocation = heap->new_space()->AllocateRaw(object_size); |
+ Object* result = allocation->ToObjectUnchecked(); |
+ |
*slot = MigrateObject(heap, object, HeapObject::cast(result), object_size); |
return; |
} |
+ static inline void EvacuateJSFunction(Map* map, |
+ HeapObject** slot, |
+ HeapObject* object) { |
+ ObjectEvacuationStrategy<POINTER_OBJECT>:: |
+ template VisitSpecialized<JSFunction::kSize>(map, slot, object); |
+ |
+ HeapObject* target = *slot; |
+ MarkBit mark_bit = Marking::MarkBitFrom(target); |
+ if (Marking::IsBlack(mark_bit)) { |
+ // This object is black and it might not be rescanned by marker. |
+ // We should explicitly record code entry slot for compaction because |
+ // promotion queue processing (IterateAndMarkPointersToFromSpace) will |
+ // miss it as it is not HeapObject-tagged. |
+ Address code_entry_slot = |
+ target->address() + JSFunction::kCodeEntryOffset; |
+ Code* code = Code::cast(Code::GetObjectFromEntryAddress(code_entry_slot)); |
+ map->GetHeap()->mark_compact_collector()-> |
+ RecordCodeEntrySlot(code_entry_slot, code); |
+ } |
+ } |
+ |
+ |
static inline void EvacuateFixedArray(Map* map, |
HeapObject** slot, |
HeapObject* object) { |
@@ -1479,14 +1572,17 @@ |
HeapObject* object) { |
ASSERT(IsShortcutCandidate(map->instance_type())); |
- if (ConsString::cast(object)->unchecked_second() == |
- map->heap()->empty_string()) { |
+ Heap* heap = map->GetHeap(); |
+ |
+ if (marks_handling == IGNORE_MARKS && |
+ ConsString::cast(object)->unchecked_second() == |
+ heap->empty_string()) { |
HeapObject* first = |
HeapObject::cast(ConsString::cast(object)->unchecked_first()); |
*slot = first; |
- if (!map->heap()->InNewSpace(first)) { |
+ if (!heap->InNewSpace(first)) { |
object->set_map_word(MapWord::FromForwardingAddress(first)); |
return; |
} |
@@ -1500,7 +1596,7 @@ |
return; |
} |
- DoScavengeObject(first->map(), slot, first); |
+ heap->DoScavengeObject(first->map(), slot, first); |
object->set_map_word(MapWord::FromForwardingAddress(*slot)); |
return; |
} |
@@ -1531,45 +1627,49 @@ |
}; |
-template<LoggingAndProfiling logging_and_profiling_mode> |
+template<MarksHandling marks_handling, |
+ LoggingAndProfiling logging_and_profiling_mode> |
VisitorDispatchTable<ScavengingCallback> |
- ScavengingVisitor<logging_and_profiling_mode>::table_; |
+ ScavengingVisitor<marks_handling, logging_and_profiling_mode>::table_; |
static void InitializeScavengingVisitorsTables() { |
- ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>::Initialize(); |
- ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED>::Initialize(); |
- scavenging_visitors_table_.CopyFrom( |
- ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>::GetTable()); |
- scavenging_visitors_table_mode_ = LOGGING_AND_PROFILING_DISABLED; |
+ ScavengingVisitor<TRANSFER_MARKS, |
+ LOGGING_AND_PROFILING_DISABLED>::Initialize(); |
+ ScavengingVisitor<IGNORE_MARKS, LOGGING_AND_PROFILING_DISABLED>::Initialize(); |
+ ScavengingVisitor<TRANSFER_MARKS, |
+ LOGGING_AND_PROFILING_ENABLED>::Initialize(); |
+ ScavengingVisitor<IGNORE_MARKS, LOGGING_AND_PROFILING_ENABLED>::Initialize(); |
} |
-void Heap::SwitchScavengingVisitorsTableIfProfilingWasEnabled() { |
- if (scavenging_visitors_table_mode_ == LOGGING_AND_PROFILING_ENABLED) { |
- // Table was already updated by some isolate. |
- return; |
- } |
- |
- if (isolate()->logger()->is_logging() | |
+void Heap::SelectScavengingVisitorsTable() { |
+ bool logging_and_profiling = |
+ isolate()->logger()->is_logging() || |
CpuProfiler::is_profiling(isolate()) || |
(isolate()->heap_profiler() != NULL && |
- isolate()->heap_profiler()->is_profiling())) { |
- // If one of the isolates is doing scavenge at this moment of time |
- // it might see this table in an inconsitent state when |
- // some of the callbacks point to |
- // ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED> and others |
- // to ScavengingVisitor<LOGGING_AND_PROFILING_DISABLED>. |
- // However this does not lead to any bugs as such isolate does not have |
- // profiling enabled and any isolate with enabled profiling is guaranteed |
- // to see the table in the consistent state. |
- scavenging_visitors_table_.CopyFrom( |
- ScavengingVisitor<LOGGING_AND_PROFILING_ENABLED>::GetTable()); |
+ isolate()->heap_profiler()->is_profiling()); |
- // We use Release_Store to prevent reordering of this write before writes |
- // to the table. |
- Release_Store(&scavenging_visitors_table_mode_, |
- LOGGING_AND_PROFILING_ENABLED); |
+ if (!incremental_marking()->IsMarking()) { |
+ if (!logging_and_profiling) { |
+ scavenging_visitors_table_.CopyFrom( |
+ ScavengingVisitor<IGNORE_MARKS, |
+ LOGGING_AND_PROFILING_DISABLED>::GetTable()); |
+ } else { |
+ scavenging_visitors_table_.CopyFrom( |
+ ScavengingVisitor<IGNORE_MARKS, |
+ LOGGING_AND_PROFILING_ENABLED>::GetTable()); |
+ } |
+ } else { |
+ if (!logging_and_profiling) { |
+ scavenging_visitors_table_.CopyFrom( |
+ ScavengingVisitor<TRANSFER_MARKS, |
+ LOGGING_AND_PROFILING_DISABLED>::GetTable()); |
+ } else { |
+ scavenging_visitors_table_.CopyFrom( |
+ ScavengingVisitor<TRANSFER_MARKS, |
+ LOGGING_AND_PROFILING_ENABLED>::GetTable()); |
+ } |
} |
} |
@@ -1579,7 +1679,7 @@ |
MapWord first_word = object->map_word(); |
ASSERT(!first_word.IsForwardingAddress()); |
Map* map = first_word.ToMap(); |
- DoScavengeObject(map, p, object); |
+ map->GetHeap()->DoScavengeObject(map, p, object); |
} |
@@ -1707,7 +1807,7 @@ |
} |
set_empty_fixed_array(FixedArray::cast(obj)); |
- { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_DATA_SPACE); |
+ { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_POINTER_SPACE); |
if (!maybe_obj->ToObject(&obj)) return false; |
} |
set_null_value(obj); |
@@ -1798,6 +1898,12 @@ |
} |
set_byte_array_map(Map::cast(obj)); |
+ { MaybeObject* maybe_obj = |
+ AllocateMap(FREE_SPACE_TYPE, kVariableSizeSentinel); |
+ if (!maybe_obj->ToObject(&obj)) return false; |
+ } |
+ set_free_space_map(Map::cast(obj)); |
+ |
{ MaybeObject* maybe_obj = AllocateByteArray(0, TENURED); |
if (!maybe_obj->ToObject(&obj)) return false; |
} |
@@ -1998,7 +2104,7 @@ |
Object* to_number, |
byte kind) { |
Object* result; |
- { MaybeObject* maybe_result = Allocate(oddball_map(), OLD_DATA_SPACE); |
+ { MaybeObject* maybe_result = Allocate(oddball_map(), OLD_POINTER_SPACE); |
if (!maybe_result->ToObject(&result)) return maybe_result; |
} |
return Oddball::cast(result)->Initialize(to_string, to_number, kind); |
@@ -2083,7 +2189,7 @@ |
} |
set_nan_value(obj); |
- { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_DATA_SPACE); |
+ { MaybeObject* maybe_obj = Allocate(oddball_map(), OLD_POINTER_SPACE); |
if (!maybe_obj->ToObject(&obj)) return false; |
} |
set_undefined_value(obj); |
@@ -2905,7 +3011,7 @@ |
Object* result; |
{ MaybeObject* maybe_result = (size <= MaxObjectSizeInPagedSpace()) |
? old_data_space_->AllocateRaw(size) |
- : lo_space_->AllocateRaw(size); |
+ : lo_space_->AllocateRaw(size, NOT_EXECUTABLE); |
if (!maybe_result->ToObject(&result)) return maybe_result; |
} |
@@ -2941,8 +3047,8 @@ |
} else if (size == 2 * kPointerSize) { |
filler->set_map(two_pointer_filler_map()); |
} else { |
- filler->set_map(byte_array_map()); |
- ByteArray::cast(filler)->set_length(ByteArray::LengthFor(size)); |
+ filler->set_map(free_space_map()); |
+ FreeSpace::cast(filler)->set_size(size); |
} |
} |
@@ -2988,7 +3094,7 @@ |
// Large code objects and code objects which should stay at a fixed address |
// are allocated in large object space. |
if (obj_size > MaxObjectSizeInPagedSpace() || immovable) { |
- maybe_result = lo_space_->AllocateRawCode(obj_size); |
+ maybe_result = lo_space_->AllocateRaw(obj_size, EXECUTABLE); |
} else { |
maybe_result = code_space_->AllocateRaw(obj_size); |
} |
@@ -3033,7 +3139,7 @@ |
int obj_size = code->Size(); |
MaybeObject* maybe_result; |
if (obj_size > MaxObjectSizeInPagedSpace()) { |
- maybe_result = lo_space_->AllocateRawCode(obj_size); |
+ maybe_result = lo_space_->AllocateRaw(obj_size, EXECUTABLE); |
} else { |
maybe_result = code_space_->AllocateRaw(obj_size); |
} |
@@ -3076,7 +3182,7 @@ |
MaybeObject* maybe_result; |
if (new_obj_size > MaxObjectSizeInPagedSpace()) { |
- maybe_result = lo_space_->AllocateRawCode(new_obj_size); |
+ maybe_result = lo_space_->AllocateRaw(new_obj_size, EXECUTABLE); |
} else { |
maybe_result = code_space_->AllocateRaw(new_obj_size); |
} |
@@ -3828,7 +3934,7 @@ |
// Allocate string. |
Object* result; |
{ MaybeObject* maybe_result = (size > MaxObjectSizeInPagedSpace()) |
- ? lo_space_->AllocateRaw(size) |
+ ? lo_space_->AllocateRaw(size, NOT_EXECUTABLE) |
: old_data_space_->AllocateRaw(size); |
if (!maybe_result->ToObject(&result)) return maybe_result; |
} |
@@ -3945,7 +4051,7 @@ |
int size = FixedArray::SizeFor(length); |
return size <= kMaxObjectSizeInNewSpace |
? new_space_.AllocateRaw(size) |
- : lo_space_->AllocateRawFixedArray(size); |
+ : lo_space_->AllocateRaw(size, NOT_EXECUTABLE); |
} |
@@ -4276,6 +4382,21 @@ |
} |
+bool Heap::IsHeapIterable() { |
+ return (!old_pointer_space()->was_swept_conservatively() && |
+ !old_data_space()->was_swept_conservatively()); |
+} |
+ |
+ |
+void Heap::EnsureHeapIsIterable() { |
+ ASSERT(IsAllocationAllowed()); |
+ if (!IsHeapIterable()) { |
+ CollectAllGarbage(kMakeHeapIterableMask); |
+ } |
+ ASSERT(IsHeapIterable()); |
+} |
+ |
+ |
bool Heap::IdleNotification() { |
static const int kIdlesBeforeScavenge = 4; |
static const int kIdlesBeforeMarkSweep = 7; |
@@ -4306,7 +4427,7 @@ |
if (number_idle_notifications_ == kIdlesBeforeScavenge) { |
if (contexts_disposed_ > 0) { |
HistogramTimerScope scope(isolate_->counters()->gc_context()); |
- CollectAllGarbage(false); |
+ CollectAllGarbage(kNoGCFlags); |
} else { |
CollectGarbage(NEW_SPACE); |
} |
@@ -4318,12 +4439,12 @@ |
// generated code for cached functions. |
isolate_->compilation_cache()->Clear(); |
- CollectAllGarbage(false); |
+ CollectAllGarbage(kNoGCFlags); |
new_space_.Shrink(); |
last_idle_notification_gc_count_ = gc_count_; |
} else if (number_idle_notifications_ == kIdlesBeforeMarkCompact) { |
- CollectAllGarbage(true); |
+ CollectAllGarbage(kNoGCFlags); |
new_space_.Shrink(); |
last_idle_notification_gc_count_ = gc_count_; |
number_idle_notifications_ = 0; |
@@ -4333,7 +4454,7 @@ |
contexts_disposed_ = 0; |
} else { |
HistogramTimerScope scope(isolate_->counters()->gc_context()); |
- CollectAllGarbage(false); |
+ CollectAllGarbage(kNoGCFlags); |
last_idle_notification_gc_count_ = gc_count_; |
} |
// If this is the first idle notification, we reset the |
@@ -4353,7 +4474,7 @@ |
// Make sure that we have no pending context disposals and |
// conditionally uncommit from space. |
- ASSERT(contexts_disposed_ == 0); |
+ ASSERT((contexts_disposed_ == 0) || incremental_marking()->IsMarking()); |
if (uncommit) UncommitFromSpace(); |
return finished; |
} |
@@ -4388,11 +4509,11 @@ |
USE(title); |
PrintF(">>>>>> =============== %s (%d) =============== >>>>>>\n", |
title, gc_count_); |
- PrintF("mark-compact GC : %d\n", mc_count_); |
PrintF("old_gen_promotion_limit_ %" V8_PTR_PREFIX "d\n", |
old_gen_promotion_limit_); |
PrintF("old_gen_allocation_limit_ %" V8_PTR_PREFIX "d\n", |
old_gen_allocation_limit_); |
+ PrintF("old_gen_limit_factor_ %d\n", old_gen_limit_factor_); |
PrintF("\n"); |
PrintF("Number of handles : %d\n", HandleScope::NumberOfHandles()); |
@@ -4469,70 +4590,19 @@ |
#ifdef DEBUG |
-static void DummyScavengePointer(HeapObject** p) { |
-} |
- |
- |
-static void VerifyPointersUnderWatermark( |
- PagedSpace* space, |
- DirtyRegionCallback visit_dirty_region) { |
- PageIterator it(space, PageIterator::PAGES_IN_USE); |
- |
- while (it.has_next()) { |
- Page* page = it.next(); |
- Address start = page->ObjectAreaStart(); |
- Address end = page->AllocationWatermark(); |
- |
- HEAP->IterateDirtyRegions(Page::kAllRegionsDirtyMarks, |
- start, |
- end, |
- visit_dirty_region, |
- &DummyScavengePointer); |
- } |
-} |
- |
- |
-static void VerifyPointersUnderWatermark(LargeObjectSpace* space) { |
- LargeObjectIterator it(space); |
- for (HeapObject* object = it.next(); object != NULL; object = it.next()) { |
- if (object->IsFixedArray()) { |
- Address slot_address = object->address(); |
- Address end = object->address() + object->Size(); |
- |
- while (slot_address < end) { |
- HeapObject** slot = reinterpret_cast<HeapObject**>(slot_address); |
- // When we are not in GC the Heap::InNewSpace() predicate |
- // checks that pointers which satisfy predicate point into |
- // the active semispace. |
- HEAP->InNewSpace(*slot); |
- slot_address += kPointerSize; |
- } |
- } |
- } |
-} |
- |
- |
void Heap::Verify() { |
ASSERT(HasBeenSetup()); |
+ store_buffer()->Verify(); |
+ |
VerifyPointersVisitor visitor; |
IterateRoots(&visitor, VISIT_ONLY_STRONG); |
new_space_.Verify(); |
- VerifyPointersAndDirtyRegionsVisitor dirty_regions_visitor; |
- old_pointer_space_->Verify(&dirty_regions_visitor); |
- map_space_->Verify(&dirty_regions_visitor); |
+ old_pointer_space_->Verify(&visitor); |
+ map_space_->Verify(&visitor); |
- VerifyPointersUnderWatermark(old_pointer_space_, |
- &IteratePointersInDirtyRegion); |
- VerifyPointersUnderWatermark(map_space_, |
- &IteratePointersInDirtyMapsRegion); |
- VerifyPointersUnderWatermark(lo_space_); |
- |
- VerifyPageWatermarkValidity(old_pointer_space_, ALL_INVALID); |
- VerifyPageWatermarkValidity(map_space_, ALL_INVALID); |
- |
VerifyPointersVisitor no_dirty_regions_visitor; |
old_data_space_->Verify(&no_dirty_regions_visitor); |
code_space_->Verify(&no_dirty_regions_visitor); |
@@ -4540,6 +4610,7 @@ |
lo_space_->Verify(); |
} |
+ |
#endif // DEBUG |
@@ -4635,275 +4706,221 @@ |
#ifdef DEBUG |
void Heap::ZapFromSpace() { |
- ASSERT(reinterpret_cast<Object*>(kFromSpaceZapValue)->IsFailure()); |
- for (Address a = new_space_.FromSpaceLow(); |
- a < new_space_.FromSpaceHigh(); |
- a += kPointerSize) { |
- Memory::Address_at(a) = kFromSpaceZapValue; |
+ NewSpacePageIterator it(new_space_.FromSpaceStart(), |
+ new_space_.FromSpaceEnd()); |
+ while (it.has_next()) { |
+ NewSpacePage* page = it.next(); |
+ for (Address cursor = page->body(), limit = page->body_limit(); |
+ cursor < limit; |
+ cursor += kPointerSize) { |
+ Memory::Address_at(cursor) = kFromSpaceZapValue; |
+ } |
} |
} |
#endif // DEBUG |
-bool Heap::IteratePointersInDirtyRegion(Heap* heap, |
- Address start, |
- Address end, |
- ObjectSlotCallback copy_object_func) { |
+void Heap::IterateAndMarkPointersToFromSpace(Address start, |
+ Address end, |
+ ObjectSlotCallback callback) { |
Address slot_address = start; |
- bool pointers_to_new_space_found = false; |
+ // We are not collecting slots on new space objects during mutation |
+ // thus we have to scan for pointers to evacuation candidates when we |
+ // promote objects. But we should not record any slots in non-black |
+ // objects. Grey object's slots would be rescanned. |
+ // White object might not survive until the end of collection |
+ // it would be a violation of the invariant to record it's slots. |
+ bool record_slots = false; |
+ if (incremental_marking()->IsCompacting()) { |
+ MarkBit mark_bit = Marking::MarkBitFrom(HeapObject::FromAddress(start)); |
+ record_slots = Marking::IsBlack(mark_bit); |
+ } |
+ |
while (slot_address < end) { |
Object** slot = reinterpret_cast<Object**>(slot_address); |
- if (heap->InNewSpace(*slot)) { |
- ASSERT((*slot)->IsHeapObject()); |
- copy_object_func(reinterpret_cast<HeapObject**>(slot)); |
- if (heap->InNewSpace(*slot)) { |
- ASSERT((*slot)->IsHeapObject()); |
- pointers_to_new_space_found = true; |
+ Object* object = *slot; |
+ // If the store buffer becomes overfull we mark pages as being exempt from |
+ // the store buffer. These pages are scanned to find pointers that point |
+ // to the new space. In that case we may hit newly promoted objects and |
+ // fix the pointers before the promotion queue gets to them. Thus the 'if'. |
+ if (object->IsHeapObject()) { |
+ if (Heap::InFromSpace(object)) { |
+ callback(reinterpret_cast<HeapObject**>(slot), |
+ HeapObject::cast(object)); |
+ Object* new_object = *slot; |
+ if (InNewSpace(new_object)) { |
+ ASSERT(Heap::InToSpace(new_object)); |
+ ASSERT(new_object->IsHeapObject()); |
+ store_buffer_.EnterDirectlyIntoStoreBuffer( |
+ reinterpret_cast<Address>(slot)); |
+ } |
+ ASSERT(!MarkCompactCollector::IsOnEvacuationCandidate(new_object)); |
+ } else if (record_slots && |
+ MarkCompactCollector::IsOnEvacuationCandidate(object)) { |
+ mark_compact_collector()->RecordSlot(slot, slot, object); |
} |
} |
slot_address += kPointerSize; |
} |
- return pointers_to_new_space_found; |
} |
-// Compute start address of the first map following given addr. |
-static inline Address MapStartAlign(Address addr) { |
- Address page = Page::FromAddress(addr)->ObjectAreaStart(); |
- return page + (((addr - page) + (Map::kSize - 1)) / Map::kSize * Map::kSize); |
-} |
+#ifdef DEBUG |
+typedef bool (*CheckStoreBufferFilter)(Object** addr); |
-// Compute end address of the first map preceding given addr. |
-static inline Address MapEndAlign(Address addr) { |
- Address page = Page::FromAllocationTop(addr)->ObjectAreaStart(); |
- return page + ((addr - page) / Map::kSize * Map::kSize); |
+bool IsAMapPointerAddress(Object** addr) { |
+ uintptr_t a = reinterpret_cast<uintptr_t>(addr); |
+ int mod = a % Map::kSize; |
+ return mod >= Map::kPointerFieldsBeginOffset && |
+ mod < Map::kPointerFieldsEndOffset; |
} |
-static bool IteratePointersInDirtyMaps(Address start, |
- Address end, |
- ObjectSlotCallback copy_object_func) { |
- ASSERT(MapStartAlign(start) == start); |
- ASSERT(MapEndAlign(end) == end); |
+bool EverythingsAPointer(Object** addr) { |
+ return true; |
+} |
- Address map_address = start; |
- bool pointers_to_new_space_found = false; |
- Heap* heap = HEAP; |
- while (map_address < end) { |
- ASSERT(!heap->InNewSpace(Memory::Object_at(map_address))); |
- ASSERT(Memory::Object_at(map_address)->IsMap()); |
- |
- Address pointer_fields_start = map_address + Map::kPointerFieldsBeginOffset; |
- Address pointer_fields_end = map_address + Map::kPointerFieldsEndOffset; |
- |
- if (Heap::IteratePointersInDirtyRegion(heap, |
- pointer_fields_start, |
- pointer_fields_end, |
- copy_object_func)) { |
- pointers_to_new_space_found = true; |
+static void CheckStoreBuffer(Heap* heap, |
+ Object** current, |
+ Object** limit, |
+ Object**** store_buffer_position, |
+ Object*** store_buffer_top, |
+ CheckStoreBufferFilter filter, |
+ Address special_garbage_start, |
+ Address special_garbage_end) { |
+ Map* free_space_map = heap->free_space_map(); |
+ for ( ; current < limit; current++) { |
+ Object* o = *current; |
+ Address current_address = reinterpret_cast<Address>(current); |
+ // Skip free space. |
+ if (o == free_space_map) { |
+ Address current_address = reinterpret_cast<Address>(current); |
+ FreeSpace* free_space = |
+ FreeSpace::cast(HeapObject::FromAddress(current_address)); |
+ int skip = free_space->Size(); |
+ ASSERT(current_address + skip <= reinterpret_cast<Address>(limit)); |
+ ASSERT(skip > 0); |
+ current_address += skip - kPointerSize; |
+ current = reinterpret_cast<Object**>(current_address); |
+ continue; |
} |
- |
- map_address += Map::kSize; |
+ // Skip the current linear allocation space between top and limit which is |
+ // unmarked with the free space map, but can contain junk. |
+ if (current_address == special_garbage_start && |
+ special_garbage_end != special_garbage_start) { |
+ current_address = special_garbage_end - kPointerSize; |
+ current = reinterpret_cast<Object**>(current_address); |
+ continue; |
+ } |
+ if (!(*filter)(current)) continue; |
+ ASSERT(current_address < special_garbage_start || |
+ current_address >= special_garbage_end); |
+ ASSERT(reinterpret_cast<uintptr_t>(o) != kFreeListZapValue); |
+ // We have to check that the pointer does not point into new space |
+ // without trying to cast it to a heap object since the hash field of |
+ // a string can contain values like 1 and 3 which are tagged null |
+ // pointers. |
+ if (!heap->InNewSpace(o)) continue; |
+ while (**store_buffer_position < current && |
+ *store_buffer_position < store_buffer_top) { |
+ (*store_buffer_position)++; |
+ } |
+ if (**store_buffer_position != current || |
+ *store_buffer_position == store_buffer_top) { |
+ Object** obj_start = current; |
+ while (!(*obj_start)->IsMap()) obj_start--; |
+ UNREACHABLE(); |
+ } |
} |
- |
- return pointers_to_new_space_found; |
} |
-bool Heap::IteratePointersInDirtyMapsRegion( |
- Heap* heap, |
- Address start, |
- Address end, |
- ObjectSlotCallback copy_object_func) { |
- Address map_aligned_start = MapStartAlign(start); |
- Address map_aligned_end = MapEndAlign(end); |
+// Check that the store buffer contains all intergenerational pointers by |
+// scanning a page and ensuring that all pointers to young space are in the |
+// store buffer. |
+void Heap::OldPointerSpaceCheckStoreBuffer() { |
+ OldSpace* space = old_pointer_space(); |
+ PageIterator pages(space); |
- bool contains_pointers_to_new_space = false; |
+ store_buffer()->SortUniq(); |
- if (map_aligned_start != start) { |
- Address prev_map = map_aligned_start - Map::kSize; |
- ASSERT(Memory::Object_at(prev_map)->IsMap()); |
+ while (pages.has_next()) { |
+ Page* page = pages.next(); |
+ Object** current = reinterpret_cast<Object**>(page->ObjectAreaStart()); |
- Address pointer_fields_start = |
- Max(start, prev_map + Map::kPointerFieldsBeginOffset); |
+ Address end = page->ObjectAreaEnd(); |
- Address pointer_fields_end = |
- Min(prev_map + Map::kPointerFieldsEndOffset, end); |
+ Object*** store_buffer_position = store_buffer()->Start(); |
+ Object*** store_buffer_top = store_buffer()->Top(); |
- contains_pointers_to_new_space = |
- IteratePointersInDirtyRegion(heap, |
- pointer_fields_start, |
- pointer_fields_end, |
- copy_object_func) |
- || contains_pointers_to_new_space; |
+ Object** limit = reinterpret_cast<Object**>(end); |
+ CheckStoreBuffer(this, |
+ current, |
+ limit, |
+ &store_buffer_position, |
+ store_buffer_top, |
+ &EverythingsAPointer, |
+ space->top(), |
+ space->limit()); |
} |
- |
- contains_pointers_to_new_space = |
- IteratePointersInDirtyMaps(map_aligned_start, |
- map_aligned_end, |
- copy_object_func) |
- || contains_pointers_to_new_space; |
- |
- if (map_aligned_end != end) { |
- ASSERT(Memory::Object_at(map_aligned_end)->IsMap()); |
- |
- Address pointer_fields_start = |
- map_aligned_end + Map::kPointerFieldsBeginOffset; |
- |
- Address pointer_fields_end = |
- Min(end, map_aligned_end + Map::kPointerFieldsEndOffset); |
- |
- contains_pointers_to_new_space = |
- IteratePointersInDirtyRegion(heap, |
- pointer_fields_start, |
- pointer_fields_end, |
- copy_object_func) |
- || contains_pointers_to_new_space; |
- } |
- |
- return contains_pointers_to_new_space; |
} |
-void Heap::IterateAndMarkPointersToFromSpace(Address start, |
- Address end, |
- ObjectSlotCallback callback) { |
- Address slot_address = start; |
- Page* page = Page::FromAddress(start); |
+void Heap::MapSpaceCheckStoreBuffer() { |
+ MapSpace* space = map_space(); |
+ PageIterator pages(space); |
- uint32_t marks = page->GetRegionMarks(); |
+ store_buffer()->SortUniq(); |
- while (slot_address < end) { |
- Object** slot = reinterpret_cast<Object**>(slot_address); |
- if (InFromSpace(*slot)) { |
- ASSERT((*slot)->IsHeapObject()); |
- callback(reinterpret_cast<HeapObject**>(slot)); |
- if (InNewSpace(*slot)) { |
- ASSERT((*slot)->IsHeapObject()); |
- marks |= page->GetRegionMaskForAddress(slot_address); |
- } |
- } |
- slot_address += kPointerSize; |
- } |
+ while (pages.has_next()) { |
+ Page* page = pages.next(); |
+ Object** current = reinterpret_cast<Object**>(page->ObjectAreaStart()); |
- page->SetRegionMarks(marks); |
-} |
+ Address end = page->ObjectAreaEnd(); |
+ Object*** store_buffer_position = store_buffer()->Start(); |
+ Object*** store_buffer_top = store_buffer()->Top(); |
-uint32_t Heap::IterateDirtyRegions( |
- uint32_t marks, |
- Address area_start, |
- Address area_end, |
- DirtyRegionCallback visit_dirty_region, |
- ObjectSlotCallback copy_object_func) { |
- uint32_t newmarks = 0; |
- uint32_t mask = 1; |
- |
- if (area_start >= area_end) { |
- return newmarks; |
+ Object** limit = reinterpret_cast<Object**>(end); |
+ CheckStoreBuffer(this, |
+ current, |
+ limit, |
+ &store_buffer_position, |
+ store_buffer_top, |
+ &IsAMapPointerAddress, |
+ space->top(), |
+ space->limit()); |
} |
- |
- Address region_start = area_start; |
- |
- // area_start does not necessarily coincide with start of the first region. |
- // Thus to calculate the beginning of the next region we have to align |
- // area_start by Page::kRegionSize. |
- Address second_region = |
- reinterpret_cast<Address>( |
- reinterpret_cast<intptr_t>(area_start + Page::kRegionSize) & |
- ~Page::kRegionAlignmentMask); |
- |
- // Next region might be beyond area_end. |
- Address region_end = Min(second_region, area_end); |
- |
- if (marks & mask) { |
- if (visit_dirty_region(this, region_start, region_end, copy_object_func)) { |
- newmarks |= mask; |
- } |
- } |
- mask <<= 1; |
- |
- // Iterate subsequent regions which fully lay inside [area_start, area_end[. |
- region_start = region_end; |
- region_end = region_start + Page::kRegionSize; |
- |
- while (region_end <= area_end) { |
- if (marks & mask) { |
- if (visit_dirty_region(this, |
- region_start, |
- region_end, |
- copy_object_func)) { |
- newmarks |= mask; |
- } |
- } |
- |
- region_start = region_end; |
- region_end = region_start + Page::kRegionSize; |
- |
- mask <<= 1; |
- } |
- |
- if (region_start != area_end) { |
- // A small piece of area left uniterated because area_end does not coincide |
- // with region end. Check whether region covering last part of area is |
- // dirty. |
- if (marks & mask) { |
- if (visit_dirty_region(this, region_start, area_end, copy_object_func)) { |
- newmarks |= mask; |
- } |
- } |
- } |
- |
- return newmarks; |
} |
- |
-void Heap::IterateDirtyRegions( |
- PagedSpace* space, |
- DirtyRegionCallback visit_dirty_region, |
- ObjectSlotCallback copy_object_func, |
- ExpectedPageWatermarkState expected_page_watermark_state) { |
- |
- PageIterator it(space, PageIterator::PAGES_IN_USE); |
- |
- while (it.has_next()) { |
- Page* page = it.next(); |
- uint32_t marks = page->GetRegionMarks(); |
- |
- if (marks != Page::kAllRegionsCleanMarks) { |
- Address start = page->ObjectAreaStart(); |
- |
- // Do not try to visit pointers beyond page allocation watermark. |
- // Page can contain garbage pointers there. |
- Address end; |
- |
- if ((expected_page_watermark_state == WATERMARK_SHOULD_BE_VALID) || |
- page->IsWatermarkValid()) { |
- end = page->AllocationWatermark(); |
- } else { |
- end = page->CachedAllocationWatermark(); |
- } |
- |
- ASSERT(space == old_pointer_space_ || |
- (space == map_space_ && |
- ((page->ObjectAreaStart() - end) % Map::kSize == 0))); |
- |
- page->SetRegionMarks(IterateDirtyRegions(marks, |
- start, |
- end, |
- visit_dirty_region, |
- copy_object_func)); |
+void Heap::LargeObjectSpaceCheckStoreBuffer() { |
+ LargeObjectIterator it(lo_space()); |
+ for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) { |
+ // We only have code, sequential strings, or fixed arrays in large |
+ // object space, and only fixed arrays can possibly contain pointers to |
+ // the young generation. |
+ if (object->IsFixedArray()) { |
+ Object*** store_buffer_position = store_buffer()->Start(); |
+ Object*** store_buffer_top = store_buffer()->Top(); |
+ Object** current = reinterpret_cast<Object**>(object->address()); |
+ Object** limit = |
+ reinterpret_cast<Object**>(object->address() + object->Size()); |
+ CheckStoreBuffer(this, |
+ current, |
+ limit, |
+ &store_buffer_position, |
+ store_buffer_top, |
+ &EverythingsAPointer, |
+ NULL, |
+ NULL); |
} |
- |
- // Mark page watermark as invalid to maintain watermark validity invariant. |
- // See Page::FlipMeaningOfInvalidatedWatermarkFlag() for details. |
- page->InvalidateWatermark(true); |
} |
} |
+#endif |
void Heap::IterateRoots(ObjectVisitor* v, VisitMode mode) { |
@@ -4955,8 +4972,7 @@ |
// Iterate over the builtin code objects and code stubs in the |
// heap. Note that it is not necessary to iterate over code objects |
// on scavenge collections. |
- if (mode != VISIT_ALL_IN_SCAVENGE && |
- mode != VISIT_ALL_IN_SWEEP_NEWSPACE) { |
+ if (mode != VISIT_ALL_IN_SCAVENGE) { |
isolate_->builtins()->IterateBuiltins(v); |
} |
v->Synchronize("builtins"); |
@@ -5000,11 +5016,20 @@ |
// and through the API, we should gracefully handle the case that the heap |
// size is not big enough to fit all the initial objects. |
bool Heap::ConfigureHeap(int max_semispace_size, |
- int max_old_gen_size, |
- int max_executable_size) { |
+ intptr_t max_old_gen_size, |
+ intptr_t max_executable_size) { |
if (HasBeenSetup()) return false; |
- if (max_semispace_size > 0) max_semispace_size_ = max_semispace_size; |
+ if (max_semispace_size > 0) { |
+ if (max_semispace_size < Page::kPageSize) { |
+ max_semispace_size = Page::kPageSize; |
+ if (FLAG_trace_gc) { |
+ PrintF("Max semispace size cannot be less than %dkbytes", |
+ Page::kPageSize >> 10); |
+ } |
+ } |
+ max_semispace_size_ = max_semispace_size; |
+ } |
if (Snapshot::IsEnabled()) { |
// If we are using a snapshot we always reserve the default amount |
@@ -5014,6 +5039,10 @@ |
// than the default reserved semispace size. |
if (max_semispace_size_ > reserved_semispace_size_) { |
max_semispace_size_ = reserved_semispace_size_; |
+ if (FLAG_trace_gc) { |
+ PrintF("Max semispace size cannot be more than %dkbytes", |
+ reserved_semispace_size_ >> 10); |
+ } |
} |
} else { |
// If we are not using snapshots we reserve space for the actual |
@@ -5039,8 +5068,12 @@ |
initial_semispace_size_ = Min(initial_semispace_size_, max_semispace_size_); |
external_allocation_limit_ = 10 * max_semispace_size_; |
- // The old generation is paged. |
- max_old_generation_size_ = RoundUp(max_old_generation_size_, Page::kPageSize); |
+ // The old generation is paged and needs at least one page for each space. |
+ int paged_space_count = LAST_PAGED_SPACE - FIRST_PAGED_SPACE + 1; |
+ max_old_generation_size_ = Max(static_cast<intptr_t>(paged_space_count * |
+ Page::kPageSize), |
+ RoundUp(max_old_generation_size_, |
+ Page::kPageSize)); |
configured_ = true; |
return true; |
@@ -5048,9 +5081,9 @@ |
bool Heap::ConfigureHeapDefault() { |
- return ConfigureHeap(FLAG_max_new_space_size / 2 * KB, |
- FLAG_max_old_space_size * MB, |
- FLAG_max_executable_size * MB); |
+ return ConfigureHeap(static_cast<intptr_t>(FLAG_max_new_space_size / 2) * KB, |
+ static_cast<intptr_t>(FLAG_max_old_space_size) * MB, |
+ static_cast<intptr_t>(FLAG_max_executable_size) * MB); |
} |
@@ -5078,7 +5111,7 @@ |
*stats->os_error = OS::GetLastError(); |
isolate()->memory_allocator()->Available(); |
if (take_snapshot) { |
- HeapIterator iterator(HeapIterator::kFilterFreeListNodes); |
+ HeapIterator iterator; |
for (HeapObject* obj = iterator.next(); |
obj != NULL; |
obj = iterator.next()) { |
@@ -5294,31 +5327,21 @@ |
gc_initializer_mutex->Lock(); |
static bool initialized_gc = false; |
if (!initialized_gc) { |
- initialized_gc = true; |
- InitializeScavengingVisitorsTables(); |
- NewSpaceScavenger::Initialize(); |
- MarkCompactCollector::Initialize(); |
+ initialized_gc = true; |
+ InitializeScavengingVisitorsTables(); |
+ NewSpaceScavenger::Initialize(); |
+ MarkCompactCollector::Initialize(); |
} |
gc_initializer_mutex->Unlock(); |
MarkMapPointersAsEncoded(false); |
- // Setup memory allocator and reserve a chunk of memory for new |
- // space. The chunk is double the size of the requested reserved |
- // new space size to ensure that we can find a pair of semispaces that |
- // are contiguous and aligned to their size. |
+ // Setup memory allocator. |
if (!isolate_->memory_allocator()->Setup(MaxReserved(), MaxExecutableSize())) |
return false; |
- void* chunk = |
- isolate_->memory_allocator()->ReserveInitialChunk( |
- 4 * reserved_semispace_size_); |
- if (chunk == NULL) return false; |
- // Align the pair of semispaces to their size, which must be a power |
- // of 2. |
- Address new_space_start = |
- RoundUp(reinterpret_cast<byte*>(chunk), 2 * reserved_semispace_size_); |
- if (!new_space_.Setup(new_space_start, 2 * reserved_semispace_size_)) { |
+ // Setup new space. |
+ if (!new_space_.Setup(reserved_semispace_size_, max_semispace_size_)) { |
return false; |
} |
@@ -5329,7 +5352,7 @@ |
OLD_POINTER_SPACE, |
NOT_EXECUTABLE); |
if (old_pointer_space_ == NULL) return false; |
- if (!old_pointer_space_->Setup(NULL, 0)) return false; |
+ if (!old_pointer_space_->Setup()) return false; |
// Initialize old data space. |
old_data_space_ = |
@@ -5338,7 +5361,7 @@ |
OLD_DATA_SPACE, |
NOT_EXECUTABLE); |
if (old_data_space_ == NULL) return false; |
- if (!old_data_space_->Setup(NULL, 0)) return false; |
+ if (!old_data_space_->Setup()) return false; |
// Initialize the code space, set its maximum capacity to the old |
// generation size. It needs executable memory. |
@@ -5353,21 +5376,20 @@ |
code_space_ = |
new OldSpace(this, max_old_generation_size_, CODE_SPACE, EXECUTABLE); |
if (code_space_ == NULL) return false; |
- if (!code_space_->Setup(NULL, 0)) return false; |
+ if (!code_space_->Setup()) return false; |
// Initialize map space. |
- map_space_ = new MapSpace(this, FLAG_use_big_map_space |
- ? max_old_generation_size_ |
- : MapSpace::kMaxMapPageIndex * Page::kPageSize, |
- FLAG_max_map_space_pages, |
- MAP_SPACE); |
+ map_space_ = new MapSpace(this, |
+ max_old_generation_size_, |
+ FLAG_max_map_space_pages, |
+ MAP_SPACE); |
if (map_space_ == NULL) return false; |
- if (!map_space_->Setup(NULL, 0)) return false; |
+ if (!map_space_->Setup()) return false; |
// Initialize global property cell space. |
cell_space_ = new CellSpace(this, max_old_generation_size_, CELL_SPACE); |
if (cell_space_ == NULL) return false; |
- if (!cell_space_->Setup(NULL, 0)) return false; |
+ if (!cell_space_->Setup()) return false; |
// The large object code space may contain code or data. We set the memory |
// to be non-executable here for safety, but this means we need to enable it |
@@ -5375,7 +5397,6 @@ |
lo_space_ = new LargeObjectSpace(this, LO_SPACE); |
if (lo_space_ == NULL) return false; |
if (!lo_space_->Setup()) return false; |
- |
if (create_heap_objects) { |
// Create initial maps. |
if (!CreateInitialMaps()) return false; |
@@ -5390,6 +5411,8 @@ |
LOG(isolate_, IntPtrTEvent("heap-capacity", Capacity())); |
LOG(isolate_, IntPtrTEvent("heap-available", Available())); |
+ store_buffer()->Setup(); |
+ |
return true; |
} |
@@ -5416,7 +5439,6 @@ |
PrintF("\n\n"); |
PrintF("gc_count=%d ", gc_count_); |
PrintF("mark_sweep_count=%d ", ms_count_); |
- PrintF("mark_compact_count=%d ", mc_count_); |
PrintF("max_gc_pause=%d ", get_max_gc_pause()); |
PrintF("min_in_mutator=%d ", get_min_in_mutator()); |
PrintF("max_alive_after_gc=%" V8_PTR_PREFIX "d ", |
@@ -5466,6 +5488,9 @@ |
lo_space_ = NULL; |
} |
+ store_buffer()->TearDown(); |
+ incremental_marking()->TearDown(); |
+ |
isolate_->memory_allocator()->TearDown(); |
#ifdef DEBUG |
@@ -5682,45 +5707,6 @@ |
}; |
-class FreeListNodesFilter : public HeapObjectsFilter { |
- public: |
- FreeListNodesFilter() { |
- MarkFreeListNodes(); |
- } |
- |
- bool SkipObject(HeapObject* object) { |
- if (object->IsMarked()) { |
- object->ClearMark(); |
- return true; |
- } else { |
- return false; |
- } |
- } |
- |
- private: |
- void MarkFreeListNodes() { |
- Heap* heap = HEAP; |
- heap->old_pointer_space()->MarkFreeListNodes(); |
- heap->old_data_space()->MarkFreeListNodes(); |
- MarkCodeSpaceFreeListNodes(heap); |
- heap->map_space()->MarkFreeListNodes(); |
- heap->cell_space()->MarkFreeListNodes(); |
- } |
- |
- void MarkCodeSpaceFreeListNodes(Heap* heap) { |
- // For code space, using FreeListNode::IsFreeListNode is OK. |
- HeapObjectIterator iter(heap->code_space()); |
- for (HeapObject* obj = iter.next_object(); |
- obj != NULL; |
- obj = iter.next_object()) { |
- if (FreeListNode::IsFreeListNode(obj)) obj->SetMark(); |
- } |
- } |
- |
- AssertNoAllocation no_alloc; |
-}; |
- |
- |
class UnreachableObjectsFilter : public HeapObjectsFilter { |
public: |
UnreachableObjectsFilter() { |
@@ -5728,8 +5714,8 @@ |
} |
bool SkipObject(HeapObject* object) { |
- if (object->IsMarked()) { |
- object->ClearMark(); |
+ if (IntrusiveMarking::IsMarked(object)) { |
+ IntrusiveMarking::ClearMark(object); |
return true; |
} else { |
return false; |
@@ -5745,8 +5731,8 @@ |
for (Object** p = start; p < end; p++) { |
if (!(*p)->IsHeapObject()) continue; |
HeapObject* obj = HeapObject::cast(*p); |
- if (obj->IsMarked()) { |
- obj->ClearMark(); |
+ if (IntrusiveMarking::IsMarked(obj)) { |
+ IntrusiveMarking::ClearMark(obj); |
list_.Add(obj); |
} |
} |
@@ -5768,7 +5754,7 @@ |
for (HeapObject* obj = iterator.next(); |
obj != NULL; |
obj = iterator.next()) { |
- obj->SetMark(); |
+ IntrusiveMarking::SetMark(obj); |
} |
UnmarkingVisitor visitor; |
HEAP->IterateRoots(&visitor, VISIT_ALL); |
@@ -5802,10 +5788,11 @@ |
void HeapIterator::Init() { |
// Start the iteration. |
space_iterator_ = filtering_ == kNoFiltering ? new SpaceIterator : |
- new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject); |
+ new SpaceIterator(Isolate::Current()->heap()-> |
+ GcSafeSizeOfOldObjectFunction()); |
switch (filtering_) { |
case kFilterFreeListNodes: |
- filter_ = new FreeListNodesFilter; |
+ // TODO(gc): Not handled. |
break; |
case kFilterUnreachable: |
filter_ = new UnreachableObjectsFilter; |
@@ -5942,6 +5929,11 @@ |
} |
+static bool SafeIsGlobalContext(HeapObject* obj) { |
+ return obj->map() == obj->GetHeap()->raw_unchecked_global_context_map(); |
+} |
+ |
+ |
void PathTracer::MarkRecursively(Object** p, MarkVisitor* mark_visitor) { |
if (!(*p)->IsHeapObject()) return; |
@@ -5960,7 +5952,7 @@ |
return; |
} |
- bool is_global_context = obj->IsGlobalContext(); |
+ bool is_global_context = SafeIsGlobalContext(obj); |
// not visited yet |
Map* map_p = reinterpret_cast<Map*>(HeapObject::cast(map)); |
@@ -6068,7 +6060,7 @@ |
for (OldSpace* space = spaces.next(); |
space != NULL; |
space = spaces.next()) { |
- holes_size += space->Waste() + space->AvailableFree(); |
+ holes_size += space->Waste() + space->Available(); |
} |
return holes_size; |
} |
@@ -6079,17 +6071,10 @@ |
start_size_(0), |
gc_count_(0), |
full_gc_count_(0), |
- is_compacting_(false), |
- marked_count_(0), |
allocated_since_last_gc_(0), |
spent_in_mutator_(0), |
promoted_objects_size_(0), |
heap_(heap) { |
- // These two fields reflect the state of the previous full collection. |
- // Set them before they are changed by the collector. |
- previous_has_compacted_ = heap_->mark_compact_collector_.HasCompacted(); |
- previous_marked_count_ = |
- heap_->mark_compact_collector_.previous_marked_count(); |
if (!FLAG_trace_gc && !FLAG_print_cumulative_gc_stat) return; |
start_time_ = OS::TimeCurrentMillis(); |
start_size_ = heap_->SizeOfObjects(); |
@@ -6106,6 +6091,14 @@ |
if (heap_->last_gc_end_timestamp_ > 0) { |
spent_in_mutator_ = Max(start_time_ - heap_->last_gc_end_timestamp_, 0.0); |
} |
+ |
+ steps_count_ = heap_->incremental_marking()->steps_count(); |
+ steps_took_ = heap_->incremental_marking()->steps_took(); |
+ longest_step_ = heap_->incremental_marking()->longest_step(); |
+ steps_count_since_last_gc_ = |
+ heap_->incremental_marking()->steps_count_since_last_gc(); |
+ steps_took_since_last_gc_ = |
+ heap_->incremental_marking()->steps_took_since_last_gc(); |
} |
@@ -6140,7 +6133,21 @@ |
SizeOfHeapObjects()); |
if (external_time > 0) PrintF("%d / ", external_time); |
- PrintF("%d ms.\n", time); |
+ PrintF("%d ms", time); |
+ if (steps_count_ > 0) { |
+ if (collector_ == SCAVENGER) { |
+ PrintF(" (+ %d ms in %d steps since last GC)", |
+ static_cast<int>(steps_took_since_last_gc_), |
+ steps_count_since_last_gc_); |
+ } else { |
+ PrintF(" (+ %d ms in %d steps since start of marking, " |
+ "biggest step %f ms)", |
+ static_cast<int>(steps_took_), |
+ steps_count_, |
+ longest_step_); |
+ } |
+ } |
+ PrintF(".\n"); |
} else { |
PrintF("pause=%d ", time); |
PrintF("mutator=%d ", |
@@ -6152,8 +6159,7 @@ |
PrintF("s"); |
break; |
case MARK_COMPACTOR: |
- PrintF("%s", |
- heap_->mark_compact_collector_.HasCompacted() ? "mc" : "ms"); |
+ PrintF("ms"); |
break; |
default: |
UNREACHABLE(); |
@@ -6175,6 +6181,14 @@ |
PrintF("allocated=%" V8_PTR_PREFIX "d ", allocated_since_last_gc_); |
PrintF("promoted=%" V8_PTR_PREFIX "d ", promoted_objects_size_); |
+ if (collector_ == SCAVENGER) { |
+ PrintF("stepscount=%d ", steps_count_since_last_gc_); |
+ PrintF("stepstook=%d ", static_cast<int>(steps_took_since_last_gc_)); |
+ } else { |
+ PrintF("stepscount=%d ", steps_count_); |
+ PrintF("stepstook=%d ", static_cast<int>(steps_took_)); |
+ } |
+ |
PrintF("\n"); |
} |
@@ -6187,8 +6201,7 @@ |
case SCAVENGER: |
return "Scavenge"; |
case MARK_COMPACTOR: |
- return heap_->mark_compact_collector_.HasCompacted() ? "Mark-compact" |
- : "Mark-sweep"; |
+ return "Mark-sweep"; |
} |
return "Unknown GC"; |
} |
@@ -6295,4 +6308,51 @@ |
} |
+void Heap::QueueMemoryChunkForFree(MemoryChunk* chunk) { |
+ chunk->set_next_chunk(chunks_queued_for_free_); |
+ chunks_queued_for_free_ = chunk; |
+} |
+ |
+ |
+void Heap::FreeQueuedChunks() { |
+ if (chunks_queued_for_free_ == NULL) return; |
+ MemoryChunk* next; |
+ MemoryChunk* chunk; |
+ for (chunk = chunks_queued_for_free_; chunk != NULL; chunk = next) { |
+ next = chunk->next_chunk(); |
+ chunk->SetFlag(MemoryChunk::ABOUT_TO_BE_FREED); |
+ |
+ if (chunk->owner()->identity() == LO_SPACE) { |
+ // StoreBuffer::Filter relies on MemoryChunk::FromAnyPointerAddress. |
+ // If FromAnyPointerAddress encounters a slot that belongs to a large |
+ // chunk queued for deletion it will fail to find the chunk because |
+ // it try to perform a search in the list of pages owned by of the large |
+ // object space and queued chunks were detached from that list. |
+ // To work around this we split large chunk into normal kPageSize aligned |
+ // pieces and initialize owner field and flags of every piece. |
+ // If FromAnyPointerAddress encounteres a slot that belongs to one of |
+ // these smaller pieces it will treat it as a slot on a normal Page. |
+ MemoryChunk* inner = MemoryChunk::FromAddress( |
+ chunk->address() + Page::kPageSize); |
+ MemoryChunk* inner_last = MemoryChunk::FromAddress( |
+ chunk->address() + chunk->size() - 1); |
+ while (inner <= inner_last) { |
+ // Size of a large chunk is always a multiple of |
+ // OS::AllocationAlignment() so there is always |
+ // enough space for a fake MemoryChunk header. |
+ inner->set_owner(lo_space()); |
+ inner->SetFlag(MemoryChunk::ABOUT_TO_BE_FREED); |
+ inner = MemoryChunk::FromAddress( |
+ inner->address() + Page::kPageSize); |
+ } |
+ } |
+ } |
+ isolate_->heap()->store_buffer()->Filter(MemoryChunk::ABOUT_TO_BE_FREED); |
+ for (chunk = chunks_queued_for_free_; chunk != NULL; chunk = next) { |
+ next = chunk->next_chunk(); |
+ isolate_->memory_allocator()->Free(chunk); |
+ } |
+ chunks_queued_for_free_ = NULL; |
+} |
+ |
} } // namespace v8::internal |