blob: e7cbf3c041e4e20d89623baed5d07184d71508aa [file] [log] [blame]
// Copyright 2006-2008 Google Inc. All Rights Reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "execution.h"
#include "global-handles.h"
#include "ic-inl.h"
#include "mark-compact.h"
#include "stub-cache.h"
namespace v8 { namespace internal {
#ifdef DEBUG
// The verification code used between phases of the m-c collector does not
// currently work.
//
// TODO(1240833): Fix the heap verification code and turn this into a real
// flag.
static const bool FLAG_verify_global_gc = false;
DECLARE_bool(gc_verbose);
#endif // DEBUG
DEFINE_bool(always_compact, false, "Perform compaction on every full GC");
DEFINE_bool(never_compact, false,
"Never perform compaction on full GC - testing only");
DEFINE_bool(cleanup_ics_at_gc, true,
"Flush inline caches prior to mark compact collection.");
DEFINE_bool(cleanup_caches_in_maps_at_gc, true,
"Flush code caches in maps during mark compact cycle.");
DECLARE_bool(gc_global);
// ----------------------------------------------------------------------------
// MarkCompactCollector
bool MarkCompactCollector::compacting_collection_ = false;
#ifdef DEBUG
MarkCompactCollector::CollectorState MarkCompactCollector::state_ = IDLE;
// Counters used for debugging the marking phase of mark-compact or mark-sweep
// collection.
int MarkCompactCollector::live_bytes_ = 0;
int MarkCompactCollector::live_young_objects_ = 0;
int MarkCompactCollector::live_old_objects_ = 0;
int MarkCompactCollector::live_immutable_objects_ = 0;
int MarkCompactCollector::live_map_objects_ = 0;
int MarkCompactCollector::live_lo_objects_ = 0;
#endif
void MarkCompactCollector::CollectGarbage() {
Prepare();
MarkLiveObjects();
SweepLargeObjectSpace();
if (compacting_collection_) {
EncodeForwardingAddresses();
UpdatePointers();
RelocateObjects();
RebuildRSets();
} else {
SweepSpaces();
}
Finish();
}
void MarkCompactCollector::Prepare() {
static const int kFragmentationLimit = 50; // Percent.
#ifdef DEBUG
ASSERT(state_ == IDLE);
state_ = PREPARE_GC;
#endif
ASSERT(!FLAG_always_compact || !FLAG_never_compact);
compacting_collection_ = FLAG_always_compact;
// We compact the old generation if it gets too fragmented (ie, we could
// recover an expected amount of space by reclaiming the waste and free
// list blocks). We always compact when the flag --gc-global is true
// because objects do not get promoted out of new space on non-compacting
// GCs.
if (!compacting_collection_) {
int old_gen_recoverable = Heap::old_space()->Waste()
+ Heap::old_space()->AvailableFree()
+ Heap::code_space()->Waste()
+ Heap::code_space()->AvailableFree();
int old_gen_used = old_gen_recoverable
+ Heap::old_space()->Size()
+ Heap::code_space()->Size();
int old_gen_fragmentation = (old_gen_recoverable * 100) / old_gen_used;
if (old_gen_fragmentation > kFragmentationLimit) {
compacting_collection_ = true;
}
}
if (FLAG_never_compact) compacting_collection_ = false;
#ifdef DEBUG
if (compacting_collection_) {
// We will write bookkeeping information to the remembered set area
// starting now.
Page::set_rset_state(Page::NOT_IN_USE);
}
#endif
Heap::map_space()->PrepareForMarkCompact(compacting_collection_);
Heap::old_space()->PrepareForMarkCompact(compacting_collection_);
Heap::code_space()->PrepareForMarkCompact(compacting_collection_);
Counters::global_objects.Set(0);
#ifdef DEBUG
live_bytes_ = 0;
live_young_objects_ = 0;
live_old_objects_ = 0;
live_immutable_objects_ = 0;
live_map_objects_ = 0;
live_lo_objects_ = 0;
#endif
}
void MarkCompactCollector::Finish() {
#ifdef DEBUG
ASSERT(state_ == SWEEP_SPACES || state_ == REBUILD_RSETS);
state_ = IDLE;
#endif
// The stub cache is not traversed during GC; clear the cache to
// force lazy re-initialization of it. This must be done after the
// GC, because it relies on the new address of certain old space
// objects (empty string, illegal builtin).
StubCache::Clear();
}
// ---------------------------------------------------------------------------
// Forwarding pointers and map pointer encoding
// | 11 bits | offset to the live object in the page
// | 11 bits | offset in a map page
// | 10 bits | map table index
static const int kMapPageIndexBits = 10;
static const int kMapPageOffsetBits = 11;
static const int kForwardingOffsetBits = 11;
static const int kAlignmentBits = 1;
static const int kMapPageIndexShift = 0;
static const int kMapPageOffsetShift =
kMapPageIndexShift + kMapPageIndexBits;
static const int kForwardingOffsetShift =
kMapPageOffsetShift + kMapPageOffsetBits;
// 0x000003FF
static const uint32_t kMapPageIndexMask =
(1 << kMapPageOffsetShift) - 1;
// 0x001FFC00
static const uint32_t kMapPageOffsetMask =
((1 << kForwardingOffsetShift) - 1) & ~kMapPageIndexMask;
// 0xFFE00000
static const uint32_t kForwardingOffsetMask =
~(kMapPageIndexMask | kMapPageOffsetMask);
static uint32_t EncodePointers(Address map_addr, int offset) {
// Offset is the distance to the first alive object in the same
// page. The offset between two objects in the same page should not
// exceed the object area size of a page.
ASSERT(0 <= offset && offset < Page::kObjectAreaSize);
int compact_offset = offset >> kObjectAlignmentBits;
ASSERT(compact_offset < (1 << kForwardingOffsetBits));
Page* map_page = Page::FromAddress(map_addr);
int map_page_index = map_page->mc_page_index;
ASSERT_MAP_PAGE_INDEX(map_page_index);
int map_page_offset = map_page->Offset(map_addr) >> kObjectAlignmentBits;
return (compact_offset << kForwardingOffsetShift)
| (map_page_offset << kMapPageOffsetShift)
| (map_page_index << kMapPageIndexShift);
}
static int DecodeOffset(uint32_t encoded) {
// The offset field is represented in the MSB.
int offset = (encoded >> kForwardingOffsetShift) << kObjectAlignmentBits;
ASSERT(0 <= offset && offset < Page::kObjectAreaSize);
return offset;
}
static Address DecodeMapPointer(uint32_t encoded, MapSpace* map_space) {
int map_page_index = (encoded & kMapPageIndexMask) >> kMapPageIndexShift;
ASSERT_MAP_PAGE_INDEX(map_page_index);
int map_page_offset = ((encoded & kMapPageOffsetMask) >> kMapPageOffsetShift)
<< kObjectAlignmentBits;
return (map_space->PageAddress(map_page_index) + map_page_offset);
}
// ----------------------------------------------------------------------------
// Phase 1: tracing and marking live objects.
// before: all objects are in normal state.
// after: a live object's map pointer is marked as '00'.
// Marking all live objects in the heap as part of mark-sweep or mark-compact
// collection. Before marking, all objects are in their normal state. After
// marking, live objects' map pointers are marked indicating that the object
// has been found reachable.
//
// The marking algorithm is a (mostly) depth-first (because of possible stack
// overflow) traversal of the graph of objects reachable from the roots. It
// uses an explicit stack of pointers rather than recursion. The young
// generation's inactive ('from') space is used as a marking stack. The
// objects in the marking stack are the ones that have been reached and marked
// but their children have not yet been visited.
//
// The marking stack can overflow during traversal. In that case, we set an
// overflow flag. When the overflow flag is set, we continue marking objects
// reachable from the objects on the marking stack, but no longer push them on
// the marking stack. Instead, we mark them as both marked and overflowed.
// When the stack is in the overflowed state, objects marked as overflowed
// have been reached and marked but their children have not been visited yet.
// After emptying the marking stack, we clear the overflow flag and traverse
// the heap looking for objects marked as overflowed, push them on the stack,
// and continue with marking. This process repeats until all reachable
// objects have been marked.
static MarkingStack marking_stack;
// Helper class for marking pointers in HeapObjects.
class MarkingVisitor : public ObjectVisitor {
public:
void VisitPointer(Object** p) {
MarkObjectByPointer(p);
}
void VisitPointers(Object** start, Object** end) {
// Mark all objects pointed to in [start, end).
const int kMinRangeForMarkingRecursion = 64;
if (end - start >= kMinRangeForMarkingRecursion) {
if (VisitUnmarkedObjects(start, end)) return;
// We are close to a stack overflow, so just mark the objects.
}
for (Object** p = start; p < end; p++) MarkObjectByPointer(p);
}
void BeginCodeIteration(Code* code) {
// When iterating over a code object during marking
// ic targets are derived pointers.
ASSERT(code->ic_flag() == Code::IC_TARGET_IS_ADDRESS);
}
void EndCodeIteration(Code* code) {
// If this is a compacting collection, set ic targets
// are pointing to object headers.
if (IsCompacting()) code->set_ic_flag(Code::IC_TARGET_IS_OBJECT);
}
void VisitCodeTarget(RelocInfo* rinfo) {
ASSERT(is_code_target(rinfo->rmode()));
Code* code = CodeFromDerivedPointer(rinfo->target_address());
if (FLAG_cleanup_ics_at_gc && code->is_inline_cache_stub()) {
IC::Clear(rinfo->pc());
// Please note targets for cleared inline cached do not have to be
// marked since they are contained in Heap::non_monomorphic_cache().
} else {
MarkCompactCollector::MarkObject(code);
}
if (IsCompacting()) {
// When compacting we convert the target to a real object pointer.
code = CodeFromDerivedPointer(rinfo->target_address());
rinfo->set_target_object(code);
}
}
void VisitDebugTarget(RelocInfo* rinfo) {
ASSERT(is_js_return(rinfo->rmode()) && rinfo->is_call_instruction());
HeapObject* code = CodeFromDerivedPointer(rinfo->call_address());
MarkCompactCollector::MarkObject(code);
// When compacting we convert the call to a real object pointer.
if (IsCompacting()) rinfo->set_call_object(code);
}
private:
// Mark obj if needed.
void MarkObject(Object* obj) {
if (!obj->IsHeapObject()) return;
MarkCompactCollector::MarkObject(HeapObject::cast(obj));
}
// Mark object pointed to by p.
void MarkObjectByPointer(Object** p) {
Object* obj = *p;
if (!obj->IsHeapObject()) return;
// Optimization: Bypass ConsString object where right size is
// Heap::empty_string().
// Please note this checks performed equals:
// object->IsConsString() &&
// (ConsString::cast(object)->second() == Heap::empty_string())
// except the map for the object might be marked.
intptr_t map_word =
reinterpret_cast<intptr_t>(HeapObject::cast(obj)->map());
uint32_t tag =
(reinterpret_cast<Map*>(clear_mark_bit(map_word)))->instance_type();
if ((tag < FIRST_NONSTRING_TYPE) &&
(kConsStringTag ==
static_cast<StringRepresentationTag>(tag &
kStringRepresentationMask)) &&
(Heap::empty_string() ==
reinterpret_cast<String*>(
reinterpret_cast<ConsString*>(obj)->second()))) {
// Since we don't have the object start it is impossible to update the
// remeber set quickly. Therefore this optimization only is taking
// place when we can avoid changing.
Object* first = reinterpret_cast<ConsString*>(obj)->first();
if (Heap::InNewSpace(obj) || !Heap::InNewSpace(first)) {
obj = first;
*p = obj;
}
}
MarkCompactCollector::MarkObject(HeapObject::cast(obj));
}
// Tells whether the mark sweep collection will perform compaction.
bool IsCompacting() { return MarkCompactCollector::IsCompacting(); }
// Retrieves the Code pointer from derived code entry.
Code* CodeFromDerivedPointer(Address addr) {
ASSERT(addr != NULL);
return reinterpret_cast<Code*>(
HeapObject::FromAddress(addr - Code::kHeaderSize));
}
// Visit an unmarked object.
void VisitUnmarkedObject(HeapObject* obj) {
ASSERT(Heap::Contains(obj));
#ifdef DEBUG
MarkCompactCollector::UpdateLiveObjectCount(obj);
#endif
Map* map = obj->map();
set_mark(obj);
// Mark the map pointer and the body.
MarkCompactCollector::MarkObject(map);
obj->IterateBody(map->instance_type(), obj->SizeFromMap(map), this);
}
// Visit all unmarked objects pointed to by [start, end).
// Returns false if the operation fails (lack of stack space).
inline bool VisitUnmarkedObjects(Object** start, Object** end) {
// Return false is we are close to the stack limit.
StackLimitCheck check;
if (check.HasOverflowed()) return false;
// Visit the unmarked objects.
for (Object** p = start; p < end; p++) {
if (!(*p)->IsHeapObject()) continue;
HeapObject* obj = HeapObject::cast(*p);
if (is_marked(obj)) continue;
VisitUnmarkedObject(obj);
}
return true;
}
};
// Helper class for pruning the symbol table.
class SymbolTableCleaner : public ObjectVisitor {
public:
SymbolTableCleaner() : pointers_removed_(0) { }
void VisitPointers(Object** start, Object** end) {
// Visit all HeapObject pointers in [start, end).
for (Object** p = start; p < end; p++) {
if ((*p)->IsHeapObject() && !is_marked(HeapObject::cast(*p))) {
// Set the entry to null_value (as deleted).
*p = Heap::null_value();
pointers_removed_++;
}
}
}
int PointersRemoved() {
return pointers_removed_;
}
private:
int pointers_removed_;
};
static void MarkObjectGroups(MarkingVisitor* marker) {
List<ObjectGroup*>& object_groups = GlobalHandles::ObjectGroups();
for (int i = 0; i < object_groups.length(); i++) {
ObjectGroup* entry = object_groups[i];
bool group_marked = false;
List<Object**>& objects = entry->objects_;
for (int j = 0; j < objects.length(); j++) {
Object* obj = *objects[j];
if (obj->IsHeapObject() && is_marked(HeapObject::cast(obj))) {
group_marked = true;
break;
}
}
if (!group_marked) continue;
for (int j = 0; j < objects.length(); j++) {
marker->VisitPointer(objects[j]);
}
}
}
void MarkCompactCollector::MarkUnmarkedObject(HeapObject* obj) {
#ifdef DEBUG
if (!is_marked(obj)) UpdateLiveObjectCount(obj);
#endif
ASSERT(!is_marked(obj));
if (obj->IsJSGlobalObject()) Counters::global_objects.Increment();
if (FLAG_cleanup_caches_in_maps_at_gc && obj->IsMap()) {
Map::cast(obj)->ClearCodeCache();
}
set_mark(obj);
if (!marking_stack.overflowed()) {
ASSERT(Heap::Contains(obj));
marking_stack.Push(obj);
} else {
// Set object's stack overflow bit, wait for rescan.
set_overflow(obj);
}
}
void MarkCompactCollector::MarkObjectsReachableFromTopFrame() {
MarkingVisitor marking_visitor;
do {
while (!marking_stack.is_empty()) {
HeapObject* obj = marking_stack.Pop();
ASSERT(Heap::Contains(obj));
ASSERT(is_marked(obj) && !is_overflowed(obj));
// Because the object is marked, the map pointer is not tagged as a
// normal HeapObject pointer, we need to recover the map pointer,
// then use the map pointer to mark the object body.
intptr_t map_word = reinterpret_cast<intptr_t>(obj->map());
Map* map = reinterpret_cast<Map*>(clear_mark_bit(map_word));
MarkObject(map);
obj->IterateBody(map->instance_type(), obj->SizeFromMap(map),
&marking_visitor);
};
// Check objects in object groups.
MarkObjectGroups(&marking_visitor);
} while (!marking_stack.is_empty());
}
static int OverflowObjectSize(HeapObject* obj) {
// Recover the normal map pointer, it might be marked as live and
// overflowed.
intptr_t map_word = reinterpret_cast<intptr_t>(obj->map());
map_word = clear_mark_bit(map_word);
map_word = clear_overflow_bit(map_word);
return obj->SizeFromMap(reinterpret_cast<Map*>(map_word));
}
static bool VisitOverflowedObject(HeapObject* obj) {
if (!is_overflowed(obj)) return true;
ASSERT(is_marked(obj));
if (marking_stack.overflowed()) return false;
clear_overflow(obj); // clear overflow bit
ASSERT(Heap::Contains(obj));
marking_stack.Push(obj);
return true;
}
template<class T>
static void ScanOverflowedObjects(T* it) {
while (it->has_next()) {
HeapObject* obj = it->next();
if (!VisitOverflowedObject(obj)) {
ASSERT(marking_stack.overflowed());
break;
}
}
}
bool MarkCompactCollector::MustBeMarked(Object** p) {
// Check whether *p is a HeapObject pointer.
if (!(*p)->IsHeapObject()) return false;
return !is_marked(HeapObject::cast(*p));
}
void MarkCompactCollector::MarkLiveObjects() {
#ifdef DEBUG
ASSERT(state_ == PREPARE_GC);
state_ = MARK_LIVE_OBJECTS;
#endif
// The to space contains live objects, the from space is used as a marking
// stack.
marking_stack.Initialize(Heap::new_space()->FromSpaceLow(),
Heap::new_space()->FromSpaceHigh());
ASSERT(!marking_stack.overflowed());
// Mark the heap roots, including global variables, stack variables, etc.
MarkingVisitor marking_visitor;
Heap::IterateStrongRoots(&marking_visitor);
// Take care of the symbol table specially.
SymbolTable* symbol_table = SymbolTable::cast(Heap::symbol_table());
#ifdef DEBUG
UpdateLiveObjectCount(symbol_table);
#endif
// 1. mark the prefix of the symbol table and push the objects on
// the stack.
symbol_table->IteratePrefix(&marking_visitor);
// 2. mark the symbol table without pushing it on the stack.
set_mark(symbol_table); // map word is changed.
bool has_processed_weak_pointers = false;
// Mark objects reachable from the roots.
while (true) {
MarkObjectsReachableFromTopFrame();
if (!marking_stack.overflowed()) {
if (has_processed_weak_pointers) break;
// First we mark weak pointers not yet reachable.
GlobalHandles::MarkWeakRoots(&MustBeMarked);
// Then we process weak pointers and process the transitive closure.
GlobalHandles::IterateWeakRoots(&marking_visitor);
has_processed_weak_pointers = true;
continue;
}
// The marking stack overflowed, we need to rebuild it by scanning the
// whole heap.
marking_stack.clear_overflowed();
// We have early stops if the stack overflowed again while scanning
// overflowed objects in a space.
SemiSpaceIterator new_it(Heap::new_space(), &OverflowObjectSize);
ScanOverflowedObjects(&new_it);
if (marking_stack.overflowed()) continue;
HeapObjectIterator old_it(Heap::old_space(), &OverflowObjectSize);
ScanOverflowedObjects(&old_it);
if (marking_stack.overflowed()) continue;
HeapObjectIterator code_it(Heap::code_space(), &OverflowObjectSize);
ScanOverflowedObjects(&code_it);
if (marking_stack.overflowed()) continue;
HeapObjectIterator map_it(Heap::map_space(), &OverflowObjectSize);
ScanOverflowedObjects(&map_it);
if (marking_stack.overflowed()) continue;
LargeObjectIterator lo_it(Heap::lo_space(), &OverflowObjectSize);
ScanOverflowedObjects(&lo_it);
}
// Prune the symbol table removing all symbols only pointed to by
// the symbol table.
SymbolTableCleaner v;
symbol_table->IterateElements(&v);
symbol_table->ElementsRemoved(v.PointersRemoved());
#ifdef DEBUG
if (FLAG_verify_global_gc) VerifyHeapAfterMarkingPhase();
#endif
// Remove object groups after marking phase.
GlobalHandles::RemoveObjectGroups();
// Objects in the active semispace of the young generation will be relocated
// to the inactive semispace. Set the relocation info to the beginning of
// the inactive semispace.
Heap::new_space()->MCResetRelocationInfo();
}
#ifdef DEBUG
void MarkCompactCollector::UpdateLiveObjectCount(HeapObject* obj) {
live_bytes_ += obj->Size();
if (Heap::new_space()->Contains(obj)) {
live_young_objects_++;
} else if (Heap::map_space()->Contains(obj)) {
ASSERT(obj->IsMap());
live_map_objects_++;
} else if (Heap::old_space()->Contains(obj)) {
live_old_objects_++;
} else if (Heap::code_space()->Contains(obj)) {
live_immutable_objects_++;
} else if (Heap::lo_space()->Contains(obj)) {
live_lo_objects_++;
} else {
UNREACHABLE();
}
}
static int CountMarkedCallback(HeapObject* obj) {
if (!is_marked(obj)) return obj->Size();
clear_mark(obj);
int obj_size = obj->Size();
set_mark(obj);
return obj_size;
}
void MarkCompactCollector::VerifyHeapAfterMarkingPhase() {
Heap::new_space()->Verify();
Heap::old_space()->Verify();
Heap::code_space()->Verify();
Heap::map_space()->Verify();
int live_objects;
#define CHECK_LIVE_OBJECTS(it, expected) \
live_objects = 0; \
while (it.has_next()) { \
HeapObject* obj = HeapObject::cast(it.next()); \
if (is_marked(obj)) live_objects++; \
} \
ASSERT(live_objects == expected);
SemiSpaceIterator new_it(Heap::new_space(), &CountMarkedCallback);
CHECK_LIVE_OBJECTS(new_it, live_young_objects_);
HeapObjectIterator old_it(Heap::old_space(), &CountMarkedCallback);
CHECK_LIVE_OBJECTS(old_it, live_old_objects_);
HeapObjectIterator code_it(Heap::code_space(), &CountMarkedCallback);
CHECK_LIVE_OBJECTS(code_it, live_immutable_objects_);
HeapObjectIterator map_it(Heap::map_space(), &CountMarkedCallback);
CHECK_LIVE_OBJECTS(map_it, live_map_objects_);
LargeObjectIterator lo_it(Heap::lo_space(), &CountMarkedCallback);
CHECK_LIVE_OBJECTS(lo_it, live_lo_objects_);
#undef CHECK_LIVE_OBJECTS
}
#endif // DEBUG
void MarkCompactCollector::SweepLargeObjectSpace() {
#ifdef DEBUG
ASSERT(state_ == MARK_LIVE_OBJECTS);
state_ =
compacting_collection_ ? ENCODE_FORWARDING_ADDRESSES : SWEEP_SPACES;
#endif
// Deallocate unmarked objects and clear marked bits for marked objects.
Heap::lo_space()->FreeUnmarkedObjects();
}
// -------------------------------------------------------------------------
// Phase 2: Encode forwarding addresses.
// When compacting, forwarding addresses for objects in old space and map
// space are encoded in their map pointer word (along with an encoding of
// their map pointers).
//
// 31 21 20 10 9 0
// +-----------------+------------------+-----------------+
// |forwarding offset|page offset of map|page index of map|
// +-----------------+------------------+-----------------+
// 11 bits 11 bits 10 bits
//
// An address range [start, end) can have both live and non-live objects.
// Maximal non-live regions are marked so they can be skipped on subsequent
// sweeps of the heap. A distinguished map-pointer encoding is used to mark
// free regions of one-word size (in which case the next word is the start
// of a live object). A second distinguished map-pointer encoding is used
// to mark free regions larger than one word, and the size of the free
// region (including the first word) is written to the second word of the
// region.
//
// Any valid map page offset must lie in the object area of the page, so map
// page offsets less than Page::kObjectStartOffset are invalid. We use a
// pair of distinguished invalid map encodings (for single word and multiple
// words) to indicate free regions in the page found during computation of
// forwarding addresses and skipped over in subsequent sweeps.
static const uint32_t kSingleFreeEncoding = 0;
static const uint32_t kMultiFreeEncoding = 1;
// Encode a free region, defined by the given start address and size, in the
// first word or two of the region.
void EncodeFreeRegion(Address free_start, int free_size) {
ASSERT(free_size >= kIntSize);
if (free_size == kIntSize) {
Memory::uint32_at(free_start) = kSingleFreeEncoding;
} else {
ASSERT(free_size >= 2 * kIntSize);
Memory::uint32_at(free_start) = kMultiFreeEncoding;
Memory::int_at(free_start + kIntSize) = free_size;
}
#ifdef DEBUG
// Zap the body of the free region.
if (FLAG_enable_slow_asserts) {
for (int offset = 2 * kIntSize;
offset < free_size;
offset += kPointerSize) {
Memory::Address_at(free_start + offset) = kZapValue;
}
}
#endif
}
// Try to promote all objects in new space. Heap numbers and sequential
// strings are promoted to the code space, all others to the old space.
inline Object* MCAllocateFromNewSpace(HeapObject* object, int object_size) {
bool has_pointers = !object->IsHeapNumber() && !object->IsSeqString();
Object* forwarded = has_pointers ?
Heap::old_space()->MCAllocateRaw(object_size) :
Heap::code_space()->MCAllocateRaw(object_size);
if (forwarded->IsFailure()) {
forwarded = Heap::new_space()->MCAllocateRaw(object_size);
}
return forwarded;
}
// Allocation functions for the paged spaces call the space's MCAllocateRaw.
inline Object* MCAllocateFromOldSpace(HeapObject* object, int object_size) {
return Heap::old_space()->MCAllocateRaw(object_size);
}
inline Object* MCAllocateFromCodeSpace(HeapObject* object, int object_size) {
return Heap::code_space()->MCAllocateRaw(object_size);
}
inline Object* MCAllocateFromMapSpace(HeapObject* object, int object_size) {
return Heap::map_space()->MCAllocateRaw(object_size);
}
// The forwarding address is encoded at the same offset as the current
// to-space object, but in from space.
inline void EncodeForwardingAddressInNewSpace(HeapObject* old_object,
int object_size,
Object* new_object,
int* ignored) {
int offset =
Heap::new_space()->ToSpaceOffsetForAddress(old_object->address());
Memory::Address_at(Heap::new_space()->FromSpaceLow() + offset) =
HeapObject::cast(new_object)->address();
}
// The forwarding address is encoded in the map pointer of the object as an
// offset (in terms of live bytes) from the address of the first live object
// in the page.
inline void EncodeForwardingAddressInPagedSpace(HeapObject* old_object,
int object_size,
Object* new_object,
int* offset) {
// Record the forwarding address of the first live object if necessary.
if (*offset == 0) {
Page::FromAddress(old_object->address())->mc_first_forwarded =
HeapObject::cast(new_object)->address();
}
uint32_t encoded = EncodePointers(old_object->map()->address(), *offset);
old_object->set_map(reinterpret_cast<Map*>(encoded));
*offset += object_size;
ASSERT(*offset <= Page::kObjectAreaSize);
}
// Most non-live objects are ignored.
inline void IgnoreNonLiveObject(HeapObject* object) {}
// A code deletion event is logged for non-live code objects.
inline void LogNonLiveCodeObject(HeapObject* object) {
if (object->IsCode()) LOG(CodeDeleteEvent(object->address()));
}
// Function template that, given a range of addresses (eg, a semispace or a
// paged space page), iterates through the objects in the range to clear
// mark bits and compute and encode forwarding addresses. As a side effect,
// maximal free chunks are marked so that they can be skipped on subsequent
// sweeps.
//
// The template parameters are an allocation function, a forwarding address
// encoding function, and a function to process non-live objects.
template<MarkCompactCollector::AllocationFunction Alloc,
MarkCompactCollector::EncodingFunction Encode,
MarkCompactCollector::ProcessNonLiveFunction ProcessNonLive>
inline void EncodeForwardingAddressesInRange(Address start,
Address end,
int* offset) {
// The start address of the current free region while sweeping the space.
// This address is set when a transition from live to non-live objects is
// encountered. A value (an encoding of the 'next free region' pointer)
// is written to memory at this address when a transition from non-live to
// live objects is encountered.
Address free_start = NULL;
// A flag giving the state of the previously swept object. Initially true
// to ensure that free_start is initialized to a proper address before
// trying to write to it.
bool is_prev_alive = true;
int object_size; // Will be set on each iteration of the loop.
for (Address current = start; current < end; current += object_size) {
HeapObject* object = HeapObject::FromAddress(current);
if (is_marked(object)) {
clear_mark(object);
object_size = object->Size();
Object* forwarded = Alloc(object, object_size);
// Allocation cannot fail, because we are compacting the space.
ASSERT(!forwarded->IsFailure());
Encode(object, object_size, forwarded, offset);
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("forward %p -> %p.\n", object->address(),
HeapObject::cast(forwarded)->address());
}
#endif
if (!is_prev_alive) { // Transition from non-live to live.
EncodeFreeRegion(free_start, current - free_start);
is_prev_alive = true;
}
} else { // Non-live object.
object_size = object->Size();
ProcessNonLive(object);
if (is_prev_alive) { // Transition from live to non-live.
free_start = current;
is_prev_alive = false;
}
}
}
// If we ended on a free region, mark it.
if (!is_prev_alive) EncodeFreeRegion(free_start, end - free_start);
}
// Functions to encode the forwarding pointers in each compactable space.
void MarkCompactCollector::EncodeForwardingAddressesInNewSpace() {
int ignored;
EncodeForwardingAddressesInRange<MCAllocateFromNewSpace,
EncodeForwardingAddressInNewSpace,
IgnoreNonLiveObject>(
Heap::new_space()->bottom(),
Heap::new_space()->top(),
&ignored);
}
template<MarkCompactCollector::AllocationFunction Alloc,
MarkCompactCollector::ProcessNonLiveFunction ProcessNonLive>
void MarkCompactCollector::EncodeForwardingAddressesInPagedSpace(
PagedSpace* space) {
PageIterator it(space, PageIterator::PAGES_IN_USE);
while (it.has_next()) {
Page* p = it.next();
// The offset of each live object in the page from the first live object
// in the page.
int offset = 0;
EncodeForwardingAddressesInRange<Alloc,
EncodeForwardingAddressInPagedSpace,
ProcessNonLive>(
p->ObjectAreaStart(),
p->AllocationTop(),
&offset);
}
}
static void SweepSpace(NewSpace* space) {
HeapObject* object;
for (Address current = space->bottom();
current < space->top();
current += object->Size()) {
object = HeapObject::FromAddress(current);
if (is_marked(object)) {
clear_mark(object);
} else {
// We give non-live objects a map that will correctly give their size,
// since their existing map might not be live after the collection.
int size = object->Size();
if (size >= Array::kHeaderSize) {
object->set_map(Heap::byte_array_map());
ByteArray::cast(object)->set_length(ByteArray::LengthFor(size));
} else {
ASSERT(size == kPointerSize);
object->set_map(Heap::one_word_filler_map());
}
ASSERT(object->Size() == size);
}
// The object is now unmarked for the call to Size() at the top of the
// loop.
}
}
static void SweepSpace(PagedSpace* space, DeallocateFunction dealloc) {
PageIterator it(space, PageIterator::PAGES_IN_USE);
while (it.has_next()) {
Page* p = it.next();
bool is_previous_alive = true;
Address free_start = NULL;
HeapObject* object;
for (Address current = p->ObjectAreaStart();
current < p->AllocationTop();
current += object->Size()) {
object = HeapObject::FromAddress(current);
if (is_marked(object)) {
clear_mark(object);
if (MarkCompactCollector::IsCompacting() && object->IsCode()) {
// If this is compacting collection marked code objects have had
// their IC targets converted to objects.
// They need to be converted back to addresses.
Code::cast(object)->ConvertICTargetsFromObjectToAddress();
}
if (!is_previous_alive) { // Transition from free to live.
dealloc(free_start, current - free_start);
is_previous_alive = true;
}
} else {
if (object->IsCode()) {
LOG(CodeDeleteEvent(Code::cast(object)->address()));
}
if (is_previous_alive) { // Transition from live to free.
free_start = current;
is_previous_alive = false;
}
}
// The object is now unmarked for the call to Size() at the top of the
// loop.
}
// If the last region was not live we need to from free_start to the
// allocation top in the page.
if (!is_previous_alive) {
int free_size = p->AllocationTop() - free_start;
if (free_size > 0) {
dealloc(free_start, free_size);
}
}
}
}
void MarkCompactCollector::DeallocateOldBlock(Address start,
int size_in_bytes) {
Heap::ClearRSetRange(start, size_in_bytes);
Heap::old_space()->Free(start, size_in_bytes);
}
void MarkCompactCollector::DeallocateCodeBlock(Address start,
int size_in_bytes) {
Heap::code_space()->Free(start, size_in_bytes);
}
void MarkCompactCollector::DeallocateMapBlock(Address start,
int size_in_bytes) {
// Objects in map space are frequently assumed to have size Map::kSize and a
// valid map in their first word. Thus, we break the free block up into
// chunks and free them separately.
ASSERT(size_in_bytes % Map::kSize == 0);
Heap::ClearRSetRange(start, size_in_bytes);
Address end = start + size_in_bytes;
for (Address a = start; a < end; a += Map::kSize) {
Heap::map_space()->Free(a);
}
}
void MarkCompactCollector::EncodeForwardingAddresses() {
ASSERT(state_ == ENCODE_FORWARDING_ADDRESSES);
// Compute the forwarding pointers in each space.
EncodeForwardingAddressesInPagedSpace<MCAllocateFromOldSpace,
IgnoreNonLiveObject>(
Heap::old_space());
EncodeForwardingAddressesInPagedSpace<MCAllocateFromCodeSpace,
LogNonLiveCodeObject>(
Heap::code_space());
// Compute new space next to last after the old and code spaces have been
// compacted. Objects in new space can be promoted to old or code space.
EncodeForwardingAddressesInNewSpace();
// Compute map space last because computing forwarding addresses
// overwrites non-live objects. Objects in the other spaces rely on
// non-live map pointers to get the sizes of non-live objects.
EncodeForwardingAddressesInPagedSpace<MCAllocateFromMapSpace,
IgnoreNonLiveObject>(
Heap::map_space());
// Write relocation info to the top page, so we can use it later. This is
// done after promoting objects from the new space so we get the correct
// allocation top.
Heap::old_space()->MCWriteRelocationInfoToPage();
Heap::code_space()->MCWriteRelocationInfoToPage();
Heap::map_space()->MCWriteRelocationInfoToPage();
}
void MarkCompactCollector::SweepSpaces() {
ASSERT(state_ == SWEEP_SPACES);
ASSERT(!IsCompacting());
// Noncompacting collections simply sweep the spaces to clear the mark
// bits and free the nonlive blocks (for old and map spaces). We sweep
// the map space last because freeing non-live maps overwrites them and
// the other spaces rely on possibly non-live maps to get the sizes for
// non-live objects.
SweepSpace(Heap::old_space(), &DeallocateOldBlock);
SweepSpace(Heap::code_space(), &DeallocateCodeBlock);
SweepSpace(Heap::new_space());
SweepSpace(Heap::map_space(), &DeallocateMapBlock);
}
// Iterate the live objects in a range of addresses (eg, a page or a
// semispace). The live regions of the range have been linked into a list.
// The first live region is [first_live_start, first_live_end), and the last
// address in the range is top. The callback function is used to get the
// size of each live object.
int MarkCompactCollector::IterateLiveObjectsInRange(
Address start,
Address end,
HeapObjectCallback size_func) {
int live_objects = 0;
Address current = start;
while (current < end) {
uint32_t encoded_map = Memory::uint32_at(current);
if (encoded_map == kSingleFreeEncoding) {
current += kPointerSize;
} else if (encoded_map == kMultiFreeEncoding) {
current += Memory::int_at(current + kIntSize);
} else {
live_objects++;
current += size_func(HeapObject::FromAddress(current));
}
}
return live_objects;
}
int MarkCompactCollector::IterateLiveObjects(NewSpace* space,
HeapObjectCallback size_f) {
ASSERT(MARK_LIVE_OBJECTS < state_ && state_ <= RELOCATE_OBJECTS);
return IterateLiveObjectsInRange(space->bottom(), space->top(), size_f);
}
int MarkCompactCollector::IterateLiveObjects(PagedSpace* space,
HeapObjectCallback size_f) {
ASSERT(MARK_LIVE_OBJECTS < state_ && state_ <= RELOCATE_OBJECTS);
int total = 0;
PageIterator it(space, PageIterator::PAGES_IN_USE);
while (it.has_next()) {
Page* p = it.next();
total += IterateLiveObjectsInRange(p->ObjectAreaStart(),
p->AllocationTop(),
size_f);
}
return total;
}
#ifdef DEBUG
static int VerifyMapObject(HeapObject* obj) {
InstanceType type = reinterpret_cast<Map*>(obj)->instance_type();
ASSERT(FIRST_TYPE <= type && type <= LAST_TYPE);
return Map::kSize;
}
void MarkCompactCollector::VerifyHeapAfterEncodingForwardingAddresses() {
Heap::new_space()->Verify();
Heap::old_space()->Verify();
Heap::code_space()->Verify();
Heap::map_space()->Verify();
ASSERT(state_ == ENCODE_FORWARDING_ADDRESSES);
int live_maps = IterateLiveObjects(Heap::map_space(), &VerifyMapObject);
ASSERT(live_maps == live_map_objects_);
// Verify page headers in paged spaces.
VerifyPageHeaders(Heap::old_space());
VerifyPageHeaders(Heap::code_space());
VerifyPageHeaders(Heap::map_space());
}
void MarkCompactCollector::VerifyPageHeaders(PagedSpace* space) {
PageIterator mc_it(space, PageIterator::PAGES_USED_BY_MC);
while (mc_it.has_next()) {
Page* p = mc_it.next();
Address mc_alloc_top = p->mc_relocation_top;
ASSERT(p->ObjectAreaStart() <= mc_alloc_top &&
mc_alloc_top <= p->ObjectAreaEnd());
}
int page_count = 0;
PageIterator it(space, PageIterator::PAGES_IN_USE);
while (it.has_next()) {
Page* p = it.next();
ASSERT(p->mc_page_index == page_count);
page_count++;
// first_forwarded could be 'deadbeed' if no live objects in this page
Address first_forwarded = p->mc_first_forwarded;
ASSERT(first_forwarded == kZapValue ||
space->Contains(first_forwarded));
}
}
#endif
// ----------------------------------------------------------------------------
// Phase 3: Update pointers
// Helper class for updating pointers in HeapObjects.
class UpdatingVisitor: public ObjectVisitor {
public:
void VisitPointer(Object** p) {
MarkCompactCollector::UpdatePointer(p);
}
void VisitPointers(Object** start, Object** end) {
// Mark all HeapObject pointers in [start, end)
for (Object** p = start; p < end; p++) {
MarkCompactCollector::UpdatePointer(p);
}
}
};
void MarkCompactCollector::UpdatePointers() {
#ifdef DEBUG
ASSERT(state_ == ENCODE_FORWARDING_ADDRESSES);
state_ = UPDATE_POINTERS;
#endif
UpdatingVisitor updating_visitor;
Heap::IterateRoots(&updating_visitor);
GlobalHandles::IterateWeakRoots(&updating_visitor);
int live_maps = IterateLiveObjects(Heap::map_space(),
&UpdatePointersInOldObject);
int live_olds = IterateLiveObjects(Heap::old_space(),
&UpdatePointersInOldObject);
int live_immutables = IterateLiveObjects(Heap::code_space(),
&UpdatePointersInOldObject);
int live_news = IterateLiveObjects(Heap::new_space(),
&UpdatePointersInNewObject);
// Large objects do not move, the map word can be updated directly.
LargeObjectIterator it(Heap::lo_space());
while (it.has_next()) UpdatePointersInNewObject(it.next());
USE(live_maps);
USE(live_olds);
USE(live_immutables);
USE(live_news);
#ifdef DEBUG
ASSERT(live_maps == live_map_objects_);
ASSERT(live_olds == live_old_objects_);
ASSERT(live_immutables == live_immutable_objects_);
ASSERT(live_news == live_young_objects_);
if (FLAG_verify_global_gc) VerifyHeapAfterUpdatingPointers();
#endif
}
int MarkCompactCollector::UpdatePointersInNewObject(HeapObject* obj) {
// Keep old map pointers
Map* old_map = obj->map();
ASSERT(old_map->IsHeapObject());
Address forwarded = GetForwardingAddressInOldSpace(old_map);
ASSERT(Heap::map_space()->Contains(old_map));
ASSERT(Heap::map_space()->Contains(forwarded));
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("update %p : %p -> %p\n", obj->address(), old_map->address(),
forwarded);
}
#endif
// Update the map pointer.
obj->set_map(reinterpret_cast<Map*>(HeapObject::FromAddress(forwarded)));
// We have to compute the object size relying on the old map because
// map objects are not relocated yet.
int obj_size = obj->SizeFromMap(old_map);
// Update pointers in the object body.
UpdatingVisitor updating_visitor;
obj->IterateBody(old_map->instance_type(), obj_size, &updating_visitor);
return obj_size;
}
int MarkCompactCollector::UpdatePointersInOldObject(HeapObject* obj) {
// Decode the map pointer.
uint32_t encoded = reinterpret_cast<uint32_t>(obj->map());
Address map_addr = DecodeMapPointer(encoded, Heap::map_space());
ASSERT(Heap::map_space()->Contains(HeapObject::FromAddress(map_addr)));
// At this point, the first word of map_addr is also encoded, cannot
// cast it to Map* using Map::cast.
Map* map = reinterpret_cast<Map*>(HeapObject::FromAddress(map_addr));
int obj_size = obj->SizeFromMap(map);
InstanceType type = map->instance_type();
// Update map pointer.
Address new_map_addr = GetForwardingAddressInOldSpace(map);
int offset = DecodeOffset(encoded);
encoded = EncodePointers(new_map_addr, offset);
obj->set_map(reinterpret_cast<Map*>(encoded));
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("update %p : %p -> %p\n", obj->address(),
map_addr, new_map_addr);
}
#endif
// Update pointers in the object body.
UpdatingVisitor updating_visitor;
obj->IterateBody(type, obj_size, &updating_visitor);
return obj_size;
}
Address MarkCompactCollector::GetForwardingAddressInOldSpace(HeapObject* obj) {
// Object should either in old or map space.
uint32_t encoded = reinterpret_cast<uint32_t>(obj->map());
// Offset to the first live object's forwarding address.
int offset = DecodeOffset(encoded);
Address obj_addr = obj->address();
// Find the first live object's forwarding address.
Page* p = Page::FromAddress(obj_addr);
Address first_forwarded = p->mc_first_forwarded;
// Page start address of forwarded address.
Page* forwarded_page = Page::FromAddress(first_forwarded);
int forwarded_offset = forwarded_page->Offset(first_forwarded);
// Find end of allocation of in the page of first_forwarded.
Address mc_top = forwarded_page->mc_relocation_top;
int mc_top_offset = forwarded_page->Offset(mc_top);
// Check if current object's forward pointer is in the same page
// as the first live object's forwarding pointer
if (forwarded_offset + offset < mc_top_offset) {
// In the same page.
return first_forwarded + offset;
}
// Must be in the next page, NOTE: this may cross chunks.
Page* next_page = forwarded_page->next_page();
ASSERT(next_page->is_valid());
offset -= (mc_top_offset - forwarded_offset);
offset += Page::kObjectStartOffset;
ASSERT_PAGE_OFFSET(offset);
ASSERT(next_page->OffsetToAddress(offset) < next_page->mc_relocation_top);
return next_page->OffsetToAddress(offset);
}
void MarkCompactCollector::UpdatePointer(Object** p) {
// We need to check if p is in to_space.
if (!(*p)->IsHeapObject()) return;
HeapObject* obj = HeapObject::cast(*p);
Address old_addr = obj->address();
Address new_addr;
ASSERT(!Heap::InFromSpace(obj));
if (Heap::new_space()->Contains(obj)) {
Address f_addr = Heap::new_space()->FromSpaceLow() +
Heap::new_space()->ToSpaceOffsetForAddress(old_addr);
new_addr = Memory::Address_at(f_addr);
#ifdef DEBUG
ASSERT(Heap::old_space()->Contains(new_addr) ||
Heap::code_space()->Contains(new_addr) ||
Heap::new_space()->FromSpaceContains(new_addr));
if (Heap::new_space()->FromSpaceContains(new_addr)) {
ASSERT(Heap::new_space()->FromSpaceOffsetForAddress(new_addr) <=
Heap::new_space()->ToSpaceOffsetForAddress(old_addr));
}
#endif
} else if (Heap::lo_space()->Contains(obj)) {
// Don't move objects in the large object space.
new_addr = obj->address();
} else {
ASSERT(Heap::old_space()->Contains(obj) ||
Heap::code_space()->Contains(obj) ||
Heap::map_space()->Contains(obj));
new_addr = GetForwardingAddressInOldSpace(obj);
ASSERT(Heap::old_space()->Contains(new_addr) ||
Heap::code_space()->Contains(new_addr) ||
Heap::map_space()->Contains(new_addr));
#ifdef DEBUG
if (Heap::old_space()->Contains(obj)) {
ASSERT(Heap::old_space()->MCSpaceOffsetForAddress(new_addr) <=
Heap::old_space()->MCSpaceOffsetForAddress(old_addr));
} else if (Heap::code_space()->Contains(obj)) {
ASSERT(Heap::code_space()->MCSpaceOffsetForAddress(new_addr) <=
Heap::code_space()->MCSpaceOffsetForAddress(old_addr));
} else {
ASSERT(Heap::map_space()->MCSpaceOffsetForAddress(new_addr) <=
Heap::map_space()->MCSpaceOffsetForAddress(old_addr));
}
#endif
}
*p = HeapObject::FromAddress(new_addr);
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("update %p : %p -> %p\n",
reinterpret_cast<Address>(p), old_addr, new_addr);
}
#endif
}
#ifdef DEBUG
void MarkCompactCollector::VerifyHeapAfterUpdatingPointers() {
ASSERT(state_ == UPDATE_POINTERS);
Heap::new_space()->Verify();
Heap::old_space()->Verify();
Heap::code_space()->Verify();
Heap::map_space()->Verify();
// We don't have object size info after updating pointers, not much we can
// do here.
VerifyPageHeaders(Heap::old_space());
VerifyPageHeaders(Heap::code_space());
VerifyPageHeaders(Heap::map_space());
}
#endif
// ----------------------------------------------------------------------------
// Phase 4: Relocate objects
void MarkCompactCollector::RelocateObjects() {
#ifdef DEBUG
ASSERT(state_ == UPDATE_POINTERS);
state_ = RELOCATE_OBJECTS;
#endif
// Relocates objects, always relocate map objects first. Relocating
// objects in other space relies on map objects to get object size.
int live_maps = IterateLiveObjects(Heap::map_space(), &RelocateMapObject);
int live_olds = IterateLiveObjects(Heap::old_space(), &RelocateOldObject);
int live_immutables =
IterateLiveObjects(Heap::code_space(), &RelocateCodeObject);
int live_news = IterateLiveObjects(Heap::new_space(), &RelocateNewObject);
USE(live_maps);
USE(live_olds);
USE(live_immutables);
USE(live_news);
#ifdef DEBUG
ASSERT(live_maps == live_map_objects_);
ASSERT(live_olds == live_old_objects_);
ASSERT(live_immutables == live_immutable_objects_);
ASSERT(live_news == live_young_objects_);
#endif
// Notify code object in LO to convert IC target to address
// This must happen after lo_space_->Compact
LargeObjectIterator it(Heap::lo_space());
while (it.has_next()) { ConvertCodeICTargetToAddress(it.next()); }
// Flips from and to spaces
Heap::new_space()->Flip();
// Sets age_mark to bottom in to space
Address mark = Heap::new_space()->bottom();
Heap::new_space()->set_age_mark(mark);
Heap::new_space()->MCCommitRelocationInfo();
#ifdef DEBUG
// It is safe to write to the remembered sets as remembered sets on a
// page-by-page basis after committing the m-c forwarding pointer.
Page::set_rset_state(Page::IN_USE);
#endif
Heap::map_space()->MCCommitRelocationInfo();
Heap::old_space()->MCCommitRelocationInfo();
Heap::code_space()->MCCommitRelocationInfo();
#ifdef DEBUG
if (FLAG_verify_global_gc) VerifyHeapAfterRelocatingObjects();
#endif
}
int MarkCompactCollector::ConvertCodeICTargetToAddress(HeapObject* obj) {
if (obj->IsCode()) {
Code::cast(obj)->ConvertICTargetsFromObjectToAddress();
}
return obj->Size();
}
int MarkCompactCollector::RelocateMapObject(HeapObject* obj) {
// decode map pointer (forwarded address)
uint32_t encoded = reinterpret_cast<uint32_t>(obj->map());
Address map_addr = DecodeMapPointer(encoded, Heap::map_space());
ASSERT(Heap::map_space()->Contains(HeapObject::FromAddress(map_addr)));
// Get forwarding address before resetting map pointer
Address new_addr = GetForwardingAddressInOldSpace(obj);
// recover map pointer
obj->set_map(reinterpret_cast<Map*>(HeapObject::FromAddress(map_addr)));
// The meta map object may not be copied yet.
Address old_addr = obj->address();
if (new_addr != old_addr) {
memmove(new_addr, old_addr, Map::kSize); // copy contents
}
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("relocate %p -> %p\n", old_addr, new_addr);
}
#endif
return Map::kSize;
}
int MarkCompactCollector::RelocateOldObject(HeapObject* obj) {
// decode map pointer (forwarded address)
uint32_t encoded = reinterpret_cast<uint32_t>(obj->map());
Address map_addr = DecodeMapPointer(encoded, Heap::map_space());
ASSERT(Heap::map_space()->Contains(HeapObject::FromAddress(map_addr)));
// Get forwarding address before resetting map pointer
Address new_addr = GetForwardingAddressInOldSpace(obj);
// recover map pointer
obj->set_map(reinterpret_cast<Map*>(HeapObject::FromAddress(map_addr)));
// This is a non-map object, it relies on the assumption that the Map space
// is compacted before the Old space (see RelocateObjects).
int obj_size = obj->Size();
ASSERT_OBJECT_SIZE(obj_size);
Address old_addr = obj->address();
ASSERT(Heap::old_space()->MCSpaceOffsetForAddress(new_addr) <=
Heap::old_space()->MCSpaceOffsetForAddress(old_addr));
Heap::old_space()->MCAdjustRelocationEnd(new_addr, obj_size);
if (new_addr != old_addr) {
memmove(new_addr, old_addr, obj_size); // copy contents
}
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsCode()) {
// may also update inline cache target.
Code::cast(copied_to)->Relocate(new_addr - old_addr);
// Notify the logger that compile code has moved.
LOG(CodeMoveEvent(old_addr, new_addr));
}
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("relocate %p -> %p\n", old_addr, new_addr);
}
#endif
return obj_size;
}
int MarkCompactCollector::RelocateCodeObject(HeapObject* obj) {
// decode map pointer (forwarded address)
uint32_t encoded = reinterpret_cast<uint32_t>(obj->map());
Address map_addr = DecodeMapPointer(encoded, Heap::map_space());
ASSERT(Heap::map_space()->Contains(HeapObject::FromAddress(map_addr)));
// Get forwarding address before resetting map pointer
Address new_addr = GetForwardingAddressInOldSpace(obj);
// recover map pointer
obj->set_map(reinterpret_cast<Map*>(HeapObject::FromAddress(map_addr)));
// This is a non-map object, it relies on the assumption that the Map space
// is compacted before the other spaces (see RelocateObjects).
int obj_size = obj->Size();
ASSERT_OBJECT_SIZE(obj_size);
Address old_addr = obj->address();
ASSERT(Heap::code_space()->MCSpaceOffsetForAddress(new_addr) <=
Heap::code_space()->MCSpaceOffsetForAddress(old_addr));
Heap::code_space()->MCAdjustRelocationEnd(new_addr, obj_size);
// convert inline cache target to address using old address
if (obj->IsCode()) {
// convert target to address first related to old_address
Code::cast(obj)->ConvertICTargetsFromObjectToAddress();
}
if (new_addr != old_addr) {
memmove(new_addr, old_addr, obj_size); // copy contents
}
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
if (copied_to->IsCode()) {
// may also update inline cache target.
Code::cast(copied_to)->Relocate(new_addr - old_addr);
// Notify the logger that compile code has moved.
LOG(CodeMoveEvent(old_addr, new_addr));
}
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("relocate %p -> %p\n", old_addr, new_addr);
}
#endif
return obj_size;
}
#ifdef DEBUG
class VerifyCopyingVisitor: public ObjectVisitor {
public:
void VisitPointers(Object** start, Object** end) {
for (Object** p = start; p < end; p++) {
MarkCompactCollector::VerifyCopyingObjects(p);
}
}
};
#endif
int MarkCompactCollector::RelocateNewObject(HeapObject* obj) {
int obj_size = obj->Size();
// Get forwarding address
Address old_addr = obj->address();
int offset = Heap::new_space()->ToSpaceOffsetForAddress(old_addr);
Address new_addr =
Memory::Address_at(Heap::new_space()->FromSpaceLow() + offset);
if (Heap::new_space()->FromSpaceContains(new_addr)) {
ASSERT(Heap::new_space()->FromSpaceOffsetForAddress(new_addr) <=
Heap::new_space()->ToSpaceOffsetForAddress(old_addr));
} else {
bool has_pointers = !obj->IsHeapNumber() && !obj->IsSeqString();
if (has_pointers) {
Heap::old_space()->MCAdjustRelocationEnd(new_addr, obj_size);
} else {
Heap::code_space()->MCAdjustRelocationEnd(new_addr, obj_size);
}
}
// New and old addresses cannot overlap.
memcpy(reinterpret_cast<void*>(new_addr),
reinterpret_cast<void*>(old_addr),
obj_size);
#ifdef DEBUG
if (FLAG_gc_verbose) {
PrintF("relocate %p -> %p\n", old_addr, new_addr);
}
if (FLAG_verify_global_gc) {
VerifyCopyingVisitor v;
HeapObject* copied_to = HeapObject::FromAddress(new_addr);
copied_to->Iterate(&v);
}
#endif
return obj_size;
}
#ifdef DEBUG
void MarkCompactCollector::VerifyHeapAfterRelocatingObjects() {
ASSERT(state_ == RELOCATE_OBJECTS);
Heap::new_space()->Verify();
Heap::old_space()->Verify();
Heap::code_space()->Verify();
Heap::map_space()->Verify();
PageIterator old_it(Heap::old_space(), PageIterator::PAGES_IN_USE);
while (old_it.has_next()) {
Page* p = old_it.next();
ASSERT_PAGE_OFFSET(p->Offset(p->AllocationTop()));
}
PageIterator code_it(Heap::code_space(), PageIterator::PAGES_IN_USE);
while (code_it.has_next()) {
Page* p = code_it.next();
ASSERT_PAGE_OFFSET(p->Offset(p->AllocationTop()));
}
PageIterator map_it(Heap::map_space(), PageIterator::PAGES_IN_USE);
while (map_it.has_next()) {
Page* p = map_it.next();
ASSERT_PAGE_OFFSET(p->Offset(p->AllocationTop()));
}
}
#endif
#ifdef DEBUG
void MarkCompactCollector::VerifyCopyingObjects(Object** p) {
if (!(*p)->IsHeapObject()) return;
ASSERT(!Heap::InToSpace(*p));
}
#endif // DEBUG
// -----------------------------------------------------------------------------
// Phase 5: rebuild remembered sets
void MarkCompactCollector::RebuildRSets() {
#ifdef DEBUG
ASSERT(state_ == RELOCATE_OBJECTS);
state_ = REBUILD_RSETS;
#endif
Heap::RebuildRSets();
}
} } // namespace v8::internal