void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) {
InstanceType type = obj->map()->instance_type();
ASSERT(0 <= type && type <= LAST_TYPE);
- if (!FreeListNode::IsFreeListNode(obj)) {
- agg_snapshot_->info()[type].increment_number(1);
- agg_snapshot_->info()[type].increment_bytes(obj->Size());
- }
+ agg_snapshot_->info()[type].increment_number(1);
+ agg_snapshot_->info()[type].increment_bytes(obj->Size());
}
void AggregatedHeapSnapshotGenerator::GenerateSnapshot() {
- HeapIterator iterator;
+ HeapIterator iterator(HeapIterator::kPreciseFiltering);
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
CollectStats(obj);
agg_snapshot_->js_cons_profile()->CollectStats(obj);
intptr_t total = 0;
AllSpaces spaces;
for (Space* space = spaces.next(); space != NULL; space = spaces.next()) {
- total += space->Size();
+ total += space->SizeOfObjects();
}
return total;
}
MemoryAllocator::Size() + MemoryAllocator::Available();
*stats->os_error = OS::GetLastError();
if (take_snapshot) {
- HeapIterator iterator;
+ HeapIterator iterator(HeapIterator::kPreciseFiltering);
for (HeapObject* obj = iterator.next();
obj != NULL;
obj = iterator.next()) {
- // Note: snapshot won't be precise because IsFreeListNode returns true
- // for any bytearray.
- if (FreeListNode::IsFreeListNode(obj)) continue;
InstanceType type = obj->map()->instance_type();
ASSERT(0 <= type && type <= LAST_TYPE);
stats->objects_per_type[type]++;
}
-SpaceIterator::SpaceIterator() : current_space_(FIRST_SPACE), iterator_(NULL) {
+SpaceIterator::SpaceIterator()
+ : current_space_(FIRST_SPACE),
+ iterator_(NULL),
+ size_func_(NULL) {
+}
+
+
+SpaceIterator::SpaceIterator(HeapObjectCallback size_func)
+ : current_space_(FIRST_SPACE),
+ iterator_(NULL),
+ size_func_(size_func) {
}
switch (current_space_) {
case NEW_SPACE:
- iterator_ = new SemiSpaceIterator(Heap::new_space());
+ iterator_ = new SemiSpaceIterator(Heap::new_space(), size_func_);
break;
case OLD_POINTER_SPACE:
- iterator_ = new HeapObjectIterator(Heap::old_pointer_space());
+ iterator_ = new HeapObjectIterator(Heap::old_pointer_space(), size_func_);
break;
case OLD_DATA_SPACE:
- iterator_ = new HeapObjectIterator(Heap::old_data_space());
+ iterator_ = new HeapObjectIterator(Heap::old_data_space(), size_func_);
break;
case CODE_SPACE:
- iterator_ = new HeapObjectIterator(Heap::code_space());
+ iterator_ = new HeapObjectIterator(Heap::code_space(), size_func_);
break;
case MAP_SPACE:
- iterator_ = new HeapObjectIterator(Heap::map_space());
+ iterator_ = new HeapObjectIterator(Heap::map_space(), size_func_);
break;
case CELL_SPACE:
- iterator_ = new HeapObjectIterator(Heap::cell_space());
+ iterator_ = new HeapObjectIterator(Heap::cell_space(), size_func_);
break;
case LO_SPACE:
- iterator_ = new LargeObjectIterator(Heap::lo_space());
+ iterator_ = new LargeObjectIterator(Heap::lo_space(), size_func_);
break;
}
}
-HeapIterator::HeapIterator() {
+class FreeListNodesFilter {
+ public:
+ FreeListNodesFilter() {
+ MarkFreeListNodes();
+ }
+
+ inline bool IsFreeListNode(HeapObject* object) {
+ if (object->IsMarked()) {
+ object->ClearMark();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private:
+ void MarkFreeListNodes() {
+ Heap::old_pointer_space()->MarkFreeListNodes();
+ Heap::old_data_space()->MarkFreeListNodes();
+ MarkCodeSpaceFreeListNodes();
+ Heap::map_space()->MarkFreeListNodes();
+ Heap::cell_space()->MarkFreeListNodes();
+ }
+
+ void MarkCodeSpaceFreeListNodes() {
+ // 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;
+};
+
+
+HeapIterator::HeapIterator()
+ : filtering_(HeapIterator::kNoFiltering),
+ filter_(NULL) {
+ Init();
+}
+
+
+HeapIterator::HeapIterator(HeapIterator::FreeListNodesFiltering filtering)
+ : filtering_(filtering),
+ filter_(NULL) {
Init();
}
void HeapIterator::Init() {
// Start the iteration.
- space_iterator_ = new SpaceIterator();
+ if (filtering_ == kPreciseFiltering) {
+ filter_ = new FreeListNodesFilter;
+ space_iterator_ =
+ new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject);
+ } else {
+ space_iterator_ = new SpaceIterator;
+ }
object_iterator_ = space_iterator_->next();
}
void HeapIterator::Shutdown() {
+#ifdef DEBUG
+ // Assert that in precise mode we have iterated through all
+ // objects. Otherwise, heap will be left in an inconsistent state.
+ if (filtering_ == kPreciseFiltering) {
+ ASSERT(object_iterator_ == NULL);
+ }
+#endif
// Make sure the last iterator is deallocated.
delete space_iterator_;
space_iterator_ = NULL;
object_iterator_ = NULL;
+ delete filter_;
+ filter_ = NULL;
}
HeapObject* HeapIterator::next() {
+ if (filter_ == NULL) return NextObject();
+
+ HeapObject* obj = NextObject();
+ while (obj != NULL && filter_->IsFreeListNode(obj)) obj = NextObject();
+ return obj;
+}
+
+
+HeapObject* HeapIterator::NextObject() {
// No iterator means we are done.
if (object_iterator_ == NULL) return NULL;
class SpaceIterator : public Malloced {
public:
SpaceIterator();
+ explicit SpaceIterator(HeapObjectCallback size_func);
virtual ~SpaceIterator();
bool has_next();
int current_space_; // from enum AllocationSpace.
ObjectIterator* iterator_; // object iterator for the current space.
+ HeapObjectCallback size_func_;
};
-// A HeapIterator provides iteration over the whole heap It aggregates a the
-// specific iterators for the different spaces as these can only iterate over
-// one space only.
+// A HeapIterator provides iteration over the whole heap. It
+// aggregates the specific iterators for the different spaces as
+// these can only iterate over one space only.
+//
+// HeapIterator can skip free list nodes (that is, de-allocated heap
+// objects that still remain in the heap). As implementation of free
+// nodes filtering uses GC marks, it can't be used during MS/MC GC
+// phases. Also, it is forbidden to interrupt iteration in this mode,
+// as this will leave heap objects marked (and thus, unusable).
+class FreeListNodesFilter;
class HeapIterator BASE_EMBEDDED {
public:
- explicit HeapIterator();
- virtual ~HeapIterator();
+ enum FreeListNodesFiltering {
+ kNoFiltering,
+ kPreciseFiltering
+ };
+
+ HeapIterator();
+ explicit HeapIterator(FreeListNodesFiltering filtering);
+ ~HeapIterator();
HeapObject* next();
void reset();
private:
// Perform the initialization.
void Init();
-
// Perform all necessary shutdown (destruction) work.
void Shutdown();
+ HeapObject* NextObject();
+ FreeListNodesFiltering filtering_;
+ FreeListNodesFilter* filter_;
// Space iterator for iterating all the spaces.
SpaceIterator* space_iterator_;
// Object iterator for the space currently being iterated.
}
-static int CountMarkedCallback(HeapObject* obj) {
- MapWord map_word = obj->map_word();
- map_word.ClearMark();
- return obj->SizeFromMap(map_word.ToMap());
-}
-
-
#ifdef DEBUG
void MarkCompactCollector::UpdateLiveObjectCount(HeapObject* obj) {
live_bytes_ += obj->Size();
void MarkCompactCollector::ClearNonLiveTransitions() {
- HeapObjectIterator map_iterator(Heap::map_space(), &CountMarkedCallback);
+ HeapObjectIterator map_iterator(Heap::map_space(), &SizeOfMarkedObject);
// Iterate over the map space, setting map transitions that go from
// a marked map to an unmarked map to null transitions. At the same time,
// set all the prototype fields of maps back to their original value,
}
+int MarkCompactCollector::SizeOfMarkedObject(HeapObject* obj) {
+ MapWord map_word = obj->map_word();
+ map_word.ClearMark();
+ return obj->SizeFromMap(map_word.ToMap());
+}
+
+
void MarkCompactCollector::Initialize() {
StaticPointersToNewGenUpdatingVisitor::Initialize();
StaticMarkingVisitor::Initialize();
// Determine type of object and emit deletion log event.
static void ReportDeleteIfNeeded(HeapObject* obj);
+ // Returns size of a possibly marked object.
+ static int SizeOfMarkedObject(HeapObject* obj);
+
// Distinguishable invalid map encodings (for single word and multiple words)
// that indicate free regions.
static const uint32_t kSingleFreeEncoding = 0;
}
+void OldSpaceFreeList::MarkNodes() {
+ for (int i = 0; i < kFreeListsLength; i++) {
+ Address cur_addr = free_[i].head_node_;
+ while (cur_addr != NULL) {
+ FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr);
+ cur_addr = cur_node->next();
+ cur_node->SetMark();
+ }
+ }
+}
+
+
#ifdef DEBUG
bool OldSpaceFreeList::Contains(FreeListNode* node) {
for (int i = 0; i < kFreeListsLength; i++) {
}
+void FixedSizeFreeList::MarkNodes() {
+ Address cur_addr = head_;
+ while (cur_addr != NULL && cur_addr != tail_) {
+ FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr);
+ cur_addr = cur_node->next();
+ cur_node->SetMark();
+ }
+}
+
+
// -----------------------------------------------------------------------------
// OldSpace implementation
: Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis
first_chunk_(NULL),
size_(0),
- page_count_(0) {}
+ page_count_(0),
+ objects_size_(0) {}
bool LargeObjectSpace::Setup() {
first_chunk_ = NULL;
size_ = 0;
page_count_ = 0;
+ objects_size_ = 0;
return true;
}
size_ = 0;
page_count_ = 0;
+ objects_size_ = 0;
}
}
size_ += static_cast<int>(chunk_size);
+ objects_size_ += requested_size;
page_count_++;
chunk->set_next(first_chunk_);
chunk->set_size(chunk_size);
// Free the chunk.
MarkCompactCollector::ReportDeleteIfNeeded(object);
size_ -= static_cast<int>(chunk_size);
+ objects_size_ -= object->Size();
page_count_--;
ObjectSpace space = kObjectSpaceLoSpace;
if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace;
CollectHistogramInfo(obj);
}
- PrintF(" number of objects %d\n", num_objects);
+ PrintF(" number of objects %d, "
+ "size of objects %" V8_PTR_PREFIX "d\n", num_objects, objects_size_);
if (num_objects > 0) ReportHistogram(false);
}
// Identity used in error reporting.
AllocationSpace identity() { return id_; }
+ // Returns allocated size.
virtual intptr_t Size() = 0;
+ // Returns size of objects. Can differ from the allocated size
+ // (e.g. see LargeObjectSpace).
+ virtual intptr_t SizeOfObjects() { return Size(); }
+
#ifdef ENABLE_HEAP_PROTECTION
// Protect/unprotect the space by marking it read-only/writable.
virtual void Protect() = 0;
// 'wasted_bytes'. The size should be a non-zero multiple of the word size.
MUST_USE_RESULT MaybeObject* Allocate(int size_in_bytes, int* wasted_bytes);
+ void MarkNodes();
+
private:
// The size range of blocks, in bytes. (Smaller allocations are allowed, but
// will always result in waste.)
// A failure is returned if no block is available.
MUST_USE_RESULT MaybeObject* Allocate();
+ void MarkNodes();
+
private:
// Available bytes on the free list.
intptr_t available_;
virtual void PutRestOfCurrentPageOnFreeList(Page* current_page);
+ void MarkFreeListNodes() { free_list_.MarkNodes(); }
+
#ifdef DEBUG
// Reports statistics for the space
void ReportStatistics();
virtual void DeallocateBlock(Address start,
int size_in_bytes,
bool add_to_freelist);
+
+ void MarkFreeListNodes() { free_list_.MarkNodes(); }
+
#ifdef DEBUG
// Reports statistic info of the space
void ReportStatistics();
return size_;
}
+ virtual intptr_t SizeOfObjects() {
+ return objects_size_;
+ }
+
int PageCount() {
return page_count_;
}
LargeObjectChunk* first_chunk_;
intptr_t size_; // allocated bytes
int page_count_; // number of chunks
-
+ intptr_t objects_size_; // size of objects
// Shared implementation of AllocateRaw, AllocateRawCode and
// AllocateRawFixedArray.
ctx[i]->Exit();
}
}
+
+
+TEST(TestSizeOfObjectsVsHeapIteratorPrecision) {
+ InitializeVM();
+ intptr_t size_of_objects_1 = Heap::SizeOfObjects();
+ HeapIterator iterator(HeapIterator::kPreciseFiltering);
+ intptr_t size_of_objects_2 = 0;
+ for (HeapObject* obj = iterator.next();
+ obj != NULL;
+ obj = iterator.next()) {
+ size_of_objects_2 += obj->Size();
+ }
+ // Delta must be within 1% of the larger result.
+ if (size_of_objects_1 > size_of_objects_2) {
+ intptr_t delta = size_of_objects_1 - size_of_objects_2;
+ PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, "
+ "Iterator: %" V8_PTR_PREFIX "d, "
+ "delta: %" V8_PTR_PREFIX "d\n",
+ size_of_objects_1, size_of_objects_2, delta);
+ CHECK_GT(size_of_objects_1 / 100, delta);
+ } else {
+ intptr_t delta = size_of_objects_2 - size_of_objects_1;
+ PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, "
+ "Iterator: %" V8_PTR_PREFIX "d, "
+ "delta: %" V8_PTR_PREFIX "d\n",
+ size_of_objects_1, size_of_objects_2, delta);
+ CHECK_GT(size_of_objects_2 / 100, delta);
+ }
+}