Commit f8e3b1d6 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[heap] Various improvements to GC stats.

This CL contains a bunch of different improvements to the existing
object stats, namely:

 - Introduce DEPRECATED_DESCRIPTOR_ARRAY_TYPE virtual instance type to
   also estimate the memory overhead of DescriptorArrays for deprecated
   Maps.
 - Do proper over-allocation computating for inobject fields in JSObjects.
 - Introduce OBJECT_PROPERTY_ARRAY_TYPE virtual instance type and properly
   compute over-allocation for PropertyArrays
 - Compute over-allocation for JSObject/JSArray elements properly.
 - Correctly report JSFunction and JSCollection like the other
   JSObjects, specifically report over-allocation properly for the
   instances itself and for the elements/properties backing stores.
 - Implement correct over-allocation computation for hash tables in
   ObjectStatsCollectorImpl::RecordHashTableVirtualObjectStats().

Bug: v8:7266
Change-Id: I9cadd703266dc90911a8e7420c3b00dcee82b06d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1557139
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60683}
parent 453e86df
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "src/memcopy.h" #include "src/memcopy.h"
#include "src/objects/compilation-cache-inl.h" #include "src/objects/compilation-cache-inl.h"
#include "src/objects/heap-object.h" #include "src/objects/heap-object.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-collection-inl.h" #include "src/objects/js-collection-inl.h"
#include "src/objects/literal-objects-inl.h" #include "src/objects/literal-objects-inl.h"
#include "src/objects/slots.h" #include "src/objects/slots.h"
...@@ -310,11 +311,14 @@ int ObjectStats::HistogramIndexFromSize(size_t size) { ...@@ -310,11 +311,14 @@ int ObjectStats::HistogramIndexFromSize(size_t size) {
kLastValueBucketIndex); kLastValueBucketIndex);
} }
void ObjectStats::RecordObjectStats(InstanceType type, size_t size) { void ObjectStats::RecordObjectStats(InstanceType type, size_t size,
size_t over_allocated) {
DCHECK_LE(type, LAST_TYPE); DCHECK_LE(type, LAST_TYPE);
object_counts_[type]++; object_counts_[type]++;
object_sizes_[type] += size; object_sizes_[type] += size;
size_histogram_[type][HistogramIndexFromSize(size)]++; size_histogram_[type][HistogramIndexFromSize(size)]++;
over_allocated_[type] += over_allocated;
over_allocated_histogram_[type][HistogramIndexFromSize(size)]++;
} }
void ObjectStats::RecordVirtualObjectStats(VirtualInstanceType type, void ObjectStats::RecordVirtualObjectStats(VirtualInstanceType type,
...@@ -366,7 +370,7 @@ class ObjectStatsCollectorImpl { ...@@ -366,7 +370,7 @@ class ObjectStatsCollectorImpl {
ObjectStats::VirtualInstanceType type); ObjectStats::VirtualInstanceType type);
// For HashTable it is possible to compute over allocated memory. // For HashTable it is possible to compute over allocated memory.
void RecordHashTableVirtualObjectStats(HeapObject parent, void RecordHashTableVirtualObjectStats(HeapObject parent,
FixedArray hash_table, HashTableBase hash_table,
ObjectStats::VirtualInstanceType type); ObjectStats::VirtualInstanceType type);
bool SameLiveness(HeapObject obj1, HeapObject obj2); bool SameLiveness(HeapObject obj1, HeapObject obj2);
...@@ -378,7 +382,9 @@ class ObjectStatsCollectorImpl { ...@@ -378,7 +382,9 @@ class ObjectStatsCollectorImpl {
// objects dispatch to the low level ObjectStats::RecordObjectStats manually. // objects dispatch to the low level ObjectStats::RecordObjectStats manually.
bool ShouldRecordObject(HeapObject object, CowMode check_cow_array); bool ShouldRecordObject(HeapObject object, CowMode check_cow_array);
void RecordObjectStats(HeapObject obj, InstanceType type, size_t size); void RecordObjectStats(
HeapObject obj, InstanceType type, size_t size,
size_t over_allocated = ObjectStats::kNoOverAllocation);
// Specific recursion into constant pool or embedded code objects. Records // Specific recursion into constant pool or embedded code objects. Records
// FixedArrays and Tuple2. // FixedArrays and Tuple2.
...@@ -395,13 +401,11 @@ class ObjectStatsCollectorImpl { ...@@ -395,13 +401,11 @@ class ObjectStatsCollectorImpl {
void RecordVirtualFixedArrayDetails(FixedArray array); void RecordVirtualFixedArrayDetails(FixedArray array);
void RecordVirtualFunctionTemplateInfoDetails(FunctionTemplateInfo fti); void RecordVirtualFunctionTemplateInfoDetails(FunctionTemplateInfo fti);
void RecordVirtualJSGlobalObjectDetails(JSGlobalObject object); void RecordVirtualJSGlobalObjectDetails(JSGlobalObject object);
void RecordVirtualJSCollectionDetails(JSObject object);
void RecordVirtualJSObjectDetails(JSObject object); void RecordVirtualJSObjectDetails(JSObject object);
void RecordVirtualMapDetails(Map map); void RecordVirtualMapDetails(Map map);
void RecordVirtualScriptDetails(Script script); void RecordVirtualScriptDetails(Script script);
void RecordVirtualExternalStringDetails(ExternalString script); void RecordVirtualExternalStringDetails(ExternalString script);
void RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo info); void RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo info);
void RecordVirtualJSFunctionDetails(JSFunction function);
void RecordVirtualArrayBoilerplateDescription( void RecordVirtualArrayBoilerplateDescription(
ArrayBoilerplateDescription description); ArrayBoilerplateDescription description);
...@@ -435,12 +439,19 @@ bool ObjectStatsCollectorImpl::ShouldRecordObject(HeapObject obj, ...@@ -435,12 +439,19 @@ bool ObjectStatsCollectorImpl::ShouldRecordObject(HeapObject obj,
} }
void ObjectStatsCollectorImpl::RecordHashTableVirtualObjectStats( void ObjectStatsCollectorImpl::RecordHashTableVirtualObjectStats(
HeapObject parent, FixedArray hash_table, HeapObject parent, HashTableBase hash_table,
ObjectStats::VirtualInstanceType type) { ObjectStats::VirtualInstanceType type) {
CHECK(hash_table->IsHashTable()); size_t entry_size =
// TODO(mlippautz): Implement over allocation for hash tables. ((hash_table->length() - HashTableBase::kPrefixStartIndex) /
hash_table->Capacity()) *
kTaggedSize;
size_t over_allocated =
(hash_table->length() -
(HashTableBase::kPrefixStartIndex + hash_table->NumberOfElements() +
hash_table->NumberOfDeletedElements())) *
entry_size;
RecordVirtualObjectStats(parent, hash_table, type, hash_table->Size(), RecordVirtualObjectStats(parent, hash_table, type, hash_table->Size(),
ObjectStats::kNoOverAllocation); over_allocated);
} }
bool ObjectStatsCollectorImpl::RecordSimpleVirtualObjectStats( bool ObjectStatsCollectorImpl::RecordSimpleVirtualObjectStats(
...@@ -529,36 +540,58 @@ void ObjectStatsCollectorImpl::RecordVirtualJSGlobalObjectDetails( ...@@ -529,36 +540,58 @@ void ObjectStatsCollectorImpl::RecordVirtualJSGlobalObjectDetails(
ObjectStats::GLOBAL_ELEMENTS_TYPE); ObjectStats::GLOBAL_ELEMENTS_TYPE);
} }
void ObjectStatsCollectorImpl::RecordVirtualJSCollectionDetails(
JSObject object) {
if (object->IsJSMap()) {
RecordSimpleVirtualObjectStats(
object, FixedArray::cast(JSMap::cast(object)->table()),
ObjectStats::JS_COLLECTION_TABLE_TYPE);
}
if (object->IsJSSet()) {
RecordSimpleVirtualObjectStats(
object, FixedArray::cast(JSSet::cast(object)->table()),
ObjectStats::JS_COLLECTION_TABLE_TYPE);
}
}
void ObjectStatsCollectorImpl::RecordVirtualJSObjectDetails(JSObject object) { void ObjectStatsCollectorImpl::RecordVirtualJSObjectDetails(JSObject object) {
// JSGlobalObject is recorded separately. // JSGlobalObject is recorded separately.
if (object->IsJSGlobalObject()) return; if (object->IsJSGlobalObject()) return;
// Uncompiled JSFunction has a separate type.
if (object->IsJSFunction() && !JSFunction::cast(object)->is_compiled()) {
RecordSimpleVirtualObjectStats(HeapObject(), object,
ObjectStats::JS_UNCOMPILED_FUNCTION_TYPE);
}
// Properties. // Properties.
if (object->HasFastProperties()) { if (object->HasFastProperties()) {
PropertyArray properties = object->property_array(); PropertyArray properties = object->property_array();
CHECK_EQ(PROPERTY_ARRAY_TYPE, properties->map()->instance_type()); size_t over_allocated = ObjectStats::kNoOverAllocation;
if (properties != ReadOnlyRoots(heap_).empty_property_array()) {
over_allocated += object->map()->UnusedPropertyFields() * kTaggedSize;
}
RecordVirtualObjectStats(object, properties,
ObjectStats::OBJECT_PROPERTY_ARRAY_TYPE,
properties->Size(), over_allocated);
} else { } else {
NameDictionary properties = object->property_dictionary(); NameDictionary properties = object->property_dictionary();
RecordHashTableVirtualObjectStats( RecordHashTableVirtualObjectStats(
object, properties, ObjectStats::OBJECT_PROPERTY_DICTIONARY_TYPE); object, properties, ObjectStats::OBJECT_PROPERTY_DICTIONARY_TYPE);
} }
// Elements. // Elements.
FixedArrayBase elements = object->elements(); FixedArrayBase elements = object->elements();
RecordSimpleVirtualObjectStats(object, elements, ObjectStats::ELEMENTS_TYPE); if (object->HasDictionaryElements()) {
RecordHashTableVirtualObjectStats(
object, NumberDictionary::cast(elements),
object->IsJSArray() ? ObjectStats::ARRAY_DICTIONARY_ELEMENTS_TYPE
: ObjectStats::OBJECT_DICTIONARY_ELEMENTS_TYPE);
} else if (object->IsJSArray()) {
size_t element_size =
(elements->Size() - FixedArrayBase::kHeaderSize) / elements->length();
uint32_t length = JSArray::cast(object)->length()->Number();
size_t over_allocated = (elements->length() - length) * element_size;
RecordVirtualObjectStats(object, elements, ObjectStats::ARRAY_ELEMENTS_TYPE,
elements->Size(), over_allocated);
} else {
RecordSimpleVirtualObjectStats(object, elements,
ObjectStats::OBJECT_ELEMENTS_TYPE);
}
// JSCollections.
if (object->IsJSCollection()) {
// TODO(bmeurer): Properly compute over-allocation here.
RecordSimpleVirtualObjectStats(
object, FixedArray::cast(JSCollection::cast(object)->table()),
ObjectStats::JS_COLLECTION_TABLE_TYPE);
}
} }
static ObjectStats::VirtualInstanceType GetFeedbackSlotType( static ObjectStats::VirtualInstanceType GetFeedbackSlotType(
...@@ -676,16 +709,12 @@ void ObjectStatsCollectorImpl::CollectStatistics( ...@@ -676,16 +709,12 @@ void ObjectStatsCollectorImpl::CollectStatistics(
} else if (obj->IsFunctionTemplateInfo()) { } else if (obj->IsFunctionTemplateInfo()) {
RecordVirtualFunctionTemplateInfoDetails( RecordVirtualFunctionTemplateInfoDetails(
FunctionTemplateInfo::cast(obj)); FunctionTemplateInfo::cast(obj));
} else if (obj->IsJSFunction()) {
RecordVirtualJSFunctionDetails(JSFunction::cast(obj));
} else if (obj->IsJSGlobalObject()) { } else if (obj->IsJSGlobalObject()) {
RecordVirtualJSGlobalObjectDetails(JSGlobalObject::cast(obj)); RecordVirtualJSGlobalObjectDetails(JSGlobalObject::cast(obj));
} else if (obj->IsJSObject()) { } else if (obj->IsJSObject()) {
// This phase needs to come after RecordVirtualAllocationSiteDetails // This phase needs to come after RecordVirtualAllocationSiteDetails
// to properly split among boilerplates. // to properly split among boilerplates.
RecordVirtualJSObjectDetails(JSObject::cast(obj)); RecordVirtualJSObjectDetails(JSObject::cast(obj));
} else if (obj->IsJSCollection()) {
RecordVirtualJSCollectionDetails(JSObject::cast(obj));
} else if (obj->IsSharedFunctionInfo()) { } else if (obj->IsSharedFunctionInfo()) {
RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj)); RecordVirtualSharedFunctionInfoDetails(SharedFunctionInfo::cast(obj));
} else if (obj->IsContext()) { } else if (obj->IsContext()) {
...@@ -706,7 +735,11 @@ void ObjectStatsCollectorImpl::CollectStatistics( ...@@ -706,7 +735,11 @@ void ObjectStatsCollectorImpl::CollectStatistics(
// sources. We still want to run RecordObjectStats after though. // sources. We still want to run RecordObjectStats after though.
RecordVirtualExternalStringDetails(ExternalString::cast(obj)); RecordVirtualExternalStringDetails(ExternalString::cast(obj));
} }
RecordObjectStats(obj, map->instance_type(), obj->Size()); size_t over_allocated = ObjectStats::kNoOverAllocation;
if (obj->IsJSObject()) {
over_allocated = map->instance_size() - map->UsedInstanceSize();
}
RecordObjectStats(obj, map->instance_type(), obj->Size(), over_allocated);
if (collect_field_stats == CollectFieldStats::kYes) { if (collect_field_stats == CollectFieldStats::kYes) {
field_stats_collector_.RecordStats(obj); field_stats_collector_.RecordStats(obj);
} }
...@@ -749,10 +782,10 @@ void ObjectStatsCollectorImpl::CollectGlobalStatistics() { ...@@ -749,10 +782,10 @@ void ObjectStatsCollectorImpl::CollectGlobalStatistics() {
} }
void ObjectStatsCollectorImpl::RecordObjectStats(HeapObject obj, void ObjectStatsCollectorImpl::RecordObjectStats(HeapObject obj,
InstanceType type, InstanceType type, size_t size,
size_t size) { size_t over_allocated) {
if (virtual_objects_.find(obj) == virtual_objects_.end()) { if (virtual_objects_.find(obj) == virtual_objects_.end()) {
stats_->RecordObjectStats(type, size); stats_->RecordObjectStats(type, size, over_allocated);
} }
} }
...@@ -809,10 +842,14 @@ void ObjectStatsCollectorImpl::RecordVirtualMapDetails(Map map) { ...@@ -809,10 +842,14 @@ void ObjectStatsCollectorImpl::RecordVirtualMapDetails(Map map) {
array != ReadOnlyRoots(heap_).empty_descriptor_array()) { array != ReadOnlyRoots(heap_).empty_descriptor_array()) {
// Generally DescriptorArrays have their own instance type already // Generally DescriptorArrays have their own instance type already
// (DESCRIPTOR_ARRAY_TYPE), but we'd like to be able to tell which // (DESCRIPTOR_ARRAY_TYPE), but we'd like to be able to tell which
// of those are for (abandoned) prototypes. // of those are for (abandoned) prototypes, and which of those are
// owned by deprecated maps.
if (map->is_prototype_map()) { if (map->is_prototype_map()) {
RecordSimpleVirtualObjectStats( RecordSimpleVirtualObjectStats(
map, array, ObjectStats::PROTOTYPE_DESCRIPTOR_ARRAY_TYPE); map, array, ObjectStats::PROTOTYPE_DESCRIPTOR_ARRAY_TYPE);
} else if (map->is_deprecated()) {
RecordSimpleVirtualObjectStats(
map, array, ObjectStats::DEPRECATED_DESCRIPTOR_ARRAY_TYPE);
} }
EnumCache enum_cache = array->enum_cache(); EnumCache enum_cache = array->enum_cache();
...@@ -887,14 +924,6 @@ void ObjectStatsCollectorImpl::RecordVirtualSharedFunctionInfoDetails( ...@@ -887,14 +924,6 @@ void ObjectStatsCollectorImpl::RecordVirtualSharedFunctionInfoDetails(
} }
} }
void ObjectStatsCollectorImpl::RecordVirtualJSFunctionDetails(
JSFunction function) {
// Uncompiled JSFunctions get their own category.
if (!function->is_compiled()) {
RecordSimpleVirtualObjectStats(HeapObject(), function,
ObjectStats::UNCOMPILED_JS_FUNCTION_TYPE);
}
}
void ObjectStatsCollectorImpl::RecordVirtualArrayBoilerplateDescription( void ObjectStatsCollectorImpl::RecordVirtualArrayBoilerplateDescription(
ArrayBoilerplateDescription description) { ArrayBoilerplateDescription description) {
RecordVirtualObjectsForConstantPoolOrEmbeddedObjects( RecordVirtualObjectsForConstantPoolOrEmbeddedObjects(
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#define VIRTUAL_INSTANCE_TYPE_LIST(V) \ #define VIRTUAL_INSTANCE_TYPE_LIST(V) \
CODE_KIND_LIST(V) \ CODE_KIND_LIST(V) \
V(ARRAY_BOILERPLATE_DESCRIPTION_ELEMENTS_TYPE) \ V(ARRAY_BOILERPLATE_DESCRIPTION_ELEMENTS_TYPE) \
V(ARRAY_DICTIONARY_ELEMENTS_TYPE) \
V(ARRAY_ELEMENTS_TYPE) \
V(BOILERPLATE_ELEMENTS_TYPE) \ V(BOILERPLATE_ELEMENTS_TYPE) \
V(BOILERPLATE_PROPERTY_ARRAY_TYPE) \ V(BOILERPLATE_PROPERTY_ARRAY_TYPE) \
V(BOILERPLATE_PROPERTY_DICTIONARY_TYPE) \ V(BOILERPLATE_PROPERTY_DICTIONARY_TYPE) \
...@@ -25,7 +27,7 @@ ...@@ -25,7 +27,7 @@
V(COW_ARRAY_TYPE) \ V(COW_ARRAY_TYPE) \
V(DEOPTIMIZATION_DATA_TYPE) \ V(DEOPTIMIZATION_DATA_TYPE) \
V(DEPENDENT_CODE_TYPE) \ V(DEPENDENT_CODE_TYPE) \
V(ELEMENTS_TYPE) \ V(DEPRECATED_DESCRIPTOR_ARRAY_TYPE) \
V(EMBEDDED_OBJECT_TYPE) \ V(EMBEDDED_OBJECT_TYPE) \
V(ENUM_CACHE_TYPE) \ V(ENUM_CACHE_TYPE) \
V(ENUM_INDICES_CACHE_TYPE) \ V(ENUM_INDICES_CACHE_TYPE) \
...@@ -45,6 +47,7 @@ ...@@ -45,6 +47,7 @@
V(JS_ARRAY_BOILERPLATE_TYPE) \ V(JS_ARRAY_BOILERPLATE_TYPE) \
V(JS_COLLECTION_TABLE_TYPE) \ V(JS_COLLECTION_TABLE_TYPE) \
V(JS_OBJECT_BOILERPLATE_TYPE) \ V(JS_OBJECT_BOILERPLATE_TYPE) \
V(JS_UNCOMPILED_FUNCTION_TYPE) \
V(MAP_ABANDONED_PROTOTYPE_TYPE) \ V(MAP_ABANDONED_PROTOTYPE_TYPE) \
V(MAP_DEPRECATED_TYPE) \ V(MAP_DEPRECATED_TYPE) \
V(MAP_DICTIONARY_TYPE) \ V(MAP_DICTIONARY_TYPE) \
...@@ -53,6 +56,9 @@ ...@@ -53,6 +56,9 @@
V(MAP_STABLE_TYPE) \ V(MAP_STABLE_TYPE) \
V(NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE) \ V(NOSCRIPT_SHARED_FUNCTION_INFOS_TYPE) \
V(NUMBER_STRING_CACHE_TYPE) \ V(NUMBER_STRING_CACHE_TYPE) \
V(OBJECT_DICTIONARY_ELEMENTS_TYPE) \
V(OBJECT_ELEMENTS_TYPE) \
V(OBJECT_PROPERTY_ARRAY_TYPE) \
V(OBJECT_PROPERTY_DICTIONARY_TYPE) \ V(OBJECT_PROPERTY_DICTIONARY_TYPE) \
V(OBJECT_TO_CODE_TYPE) \ V(OBJECT_TO_CODE_TYPE) \
V(OPTIMIZED_CODE_LITERALS_TYPE) \ V(OPTIMIZED_CODE_LITERALS_TYPE) \
...@@ -74,7 +80,6 @@ ...@@ -74,7 +80,6 @@
V(STRING_EXTERNAL_RESOURCE_ONE_BYTE_TYPE) \ V(STRING_EXTERNAL_RESOURCE_ONE_BYTE_TYPE) \
V(STRING_EXTERNAL_RESOURCE_TWO_BYTE_TYPE) \ V(STRING_EXTERNAL_RESOURCE_TWO_BYTE_TYPE) \
V(SOURCE_POSITION_TABLE_TYPE) \ V(SOURCE_POSITION_TABLE_TYPE) \
V(UNCOMPILED_JS_FUNCTION_TYPE) \
V(UNCOMPILED_SHARED_FUNCTION_INFO_TYPE) \ V(UNCOMPILED_SHARED_FUNCTION_INFO_TYPE) \
V(WEAK_NEW_SPACE_OBJECT_TO_CODE_TYPE) V(WEAK_NEW_SPACE_OBJECT_TO_CODE_TYPE)
...@@ -112,7 +117,8 @@ class ObjectStats { ...@@ -112,7 +117,8 @@ class ObjectStats {
void Dump(std::stringstream& stream); void Dump(std::stringstream& stream);
void CheckpointObjectStats(); void CheckpointObjectStats();
void RecordObjectStats(InstanceType type, size_t size); void RecordObjectStats(InstanceType type, size_t size,
size_t over_allocated = kNoOverAllocation);
void RecordVirtualObjectStats(VirtualInstanceType type, size_t size, void RecordVirtualObjectStats(VirtualInstanceType type, size_t size,
size_t over_allocated); size_t over_allocated);
......
...@@ -51,6 +51,7 @@ ACCESSORS(JSCollectionIterator, index, Object, kIndexOffset) ...@@ -51,6 +51,7 @@ ACCESSORS(JSCollectionIterator, index, Object, kIndexOffset)
ACCESSORS(JSWeakCollection, table, Object, kTableOffset) ACCESSORS(JSWeakCollection, table, Object, kTableOffset)
CAST_ACCESSOR(JSCollection)
CAST_ACCESSOR(JSSet) CAST_ACCESSOR(JSSet)
CAST_ACCESSOR(JSSetIterator) CAST_ACCESSOR(JSSetIterator)
CAST_ACCESSOR(JSMap) CAST_ACCESSOR(JSMap)
......
...@@ -19,6 +19,8 @@ class OrderedHashMap; ...@@ -19,6 +19,8 @@ class OrderedHashMap;
class JSCollection : public JSObject { class JSCollection : public JSObject {
public: public:
DECL_CAST(JSCollection)
// [table]: the backing hash table // [table]: the backing hash table
DECL_ACCESSORS(table, Object) DECL_ACCESSORS(table, Object)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment