OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/heap/object-stats.h" | 5 #include "src/heap/object-stats.h" |
6 | 6 |
7 #include "src/counters.h" | 7 #include "src/counters.h" |
8 #include "src/heap/heap-inl.h" | 8 #include "src/heap/heap-inl.h" |
9 #include "src/isolate.h" | 9 #include "src/isolate.h" |
10 #include "src/utils.h" | 10 #include "src/utils.h" |
11 | 11 |
12 namespace v8 { | 12 namespace v8 { |
13 namespace internal { | 13 namespace internal { |
14 | 14 |
15 static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER; | 15 static base::LazyMutex object_stats_mutex = LAZY_MUTEX_INITIALIZER; |
16 | 16 |
17 | 17 |
18 void ObjectStats::ClearObjectStats(bool clear_last_time_stats) { | 18 void ObjectStats::ClearObjectStats(bool clear_last_time_stats) { |
19 memset(object_counts_, 0, sizeof(object_counts_)); | 19 memset(object_counts_, 0, sizeof(object_counts_)); |
20 memset(object_sizes_, 0, sizeof(object_sizes_)); | 20 memset(object_sizes_, 0, sizeof(object_sizes_)); |
21 memset(over_allocated_, 0, sizeof(over_allocated_)); | 21 memset(over_allocated_, 0, sizeof(over_allocated_)); |
22 memset(size_histogram_, 0, sizeof(size_histogram_)); | 22 memset(size_histogram_, 0, sizeof(size_histogram_)); |
23 memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_)); | 23 memset(over_allocated_histogram_, 0, sizeof(over_allocated_histogram_)); |
24 if (clear_last_time_stats) { | 24 if (clear_last_time_stats) { |
25 memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_)); | 25 memset(object_counts_last_time_, 0, sizeof(object_counts_last_time_)); |
26 memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_)); | 26 memset(object_sizes_last_time_, 0, sizeof(object_sizes_last_time_)); |
27 } | 27 } |
| 28 visited_fixed_array_sub_types_.clear(); |
28 } | 29 } |
29 | 30 |
30 static void PrintJSONArray(size_t* array, const int len) { | 31 static void PrintJSONArray(size_t* array, const int len) { |
31 PrintF("[ "); | 32 PrintF("[ "); |
32 for (int i = 0; i < len; i++) { | 33 for (int i = 0; i < len; i++) { |
33 PrintF("%zu", array[i]); | 34 PrintF("%zu", array[i]); |
34 if (i != (len - 1)) PrintF(", "); | 35 if (i != (len - 1)) PrintF(", "); |
35 } | 36 } |
36 PrintF(" ]"); | 37 PrintF(" ]"); |
37 } | 38 } |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
183 } | 184 } |
184 if (obj->IsJSObject()) { | 185 if (obj->IsJSObject()) { |
185 RecordJSObjectDetails(stats, heap, JSObject::cast(obj)); | 186 RecordJSObjectDetails(stats, heap, JSObject::cast(obj)); |
186 } | 187 } |
187 if (obj->IsJSWeakCollection()) { | 188 if (obj->IsJSWeakCollection()) { |
188 RecordJSWeakCollectionDetails(stats, heap, JSWeakCollection::cast(obj)); | 189 RecordJSWeakCollectionDetails(stats, heap, JSWeakCollection::cast(obj)); |
189 } | 190 } |
190 } | 191 } |
191 | 192 |
192 static bool CanRecordFixedArray(Heap* heap, FixedArrayBase* array) { | 193 static bool CanRecordFixedArray(Heap* heap, FixedArrayBase* array) { |
193 return array->map() != heap->fixed_cow_array_map() && | 194 return array->map()->instance_type() == FIXED_ARRAY_TYPE && |
| 195 array->map() != heap->fixed_cow_array_map() && |
194 array->map() != heap->fixed_double_array_map() && | 196 array->map() != heap->fixed_double_array_map() && |
195 array != heap->empty_fixed_array(); | 197 array != heap->empty_fixed_array() && |
| 198 array != heap->empty_byte_array() && |
| 199 array != heap->empty_literals_array() && |
| 200 array != heap->empty_sloppy_arguments_elements() && |
| 201 array != heap->empty_slow_element_dictionary() && |
| 202 array != heap->empty_descriptor_array() && |
| 203 array != heap->empty_properties_dictionary(); |
| 204 } |
| 205 |
| 206 static bool SameLiveness(HeapObject* obj1, HeapObject* obj2) { |
| 207 return ObjectMarking::Color(obj1) == ObjectMarking::Color(obj2); |
| 208 } |
| 209 |
| 210 void ObjectStatsCollector::RecordFixedArrayHelper( |
| 211 ObjectStats* stats, Heap* heap, HeapObject* parent, FixedArray* array, |
| 212 int subtype, size_t overhead) { |
| 213 if (SameLiveness(parent, array) && CanRecordFixedArray(heap, array)) { |
| 214 stats->RecordFixedArraySubTypeStats(array, subtype, array->Size(), |
| 215 overhead); |
| 216 } |
196 } | 217 } |
197 | 218 |
198 void ObjectStatsCollector::RecordJSObjectDetails(ObjectStats* stats, Heap* heap, | 219 void ObjectStatsCollector::RecordJSObjectDetails(ObjectStats* stats, Heap* heap, |
199 JSObject* object) { | 220 JSObject* object) { |
200 DCHECK(object->IsJSObject()); | 221 DCHECK(object->IsJSObject()); |
201 | 222 |
202 size_t overhead = 0; | 223 size_t overhead = 0; |
203 FixedArrayBase* elements = object->elements(); | 224 FixedArrayBase* elements = object->elements(); |
204 if (CanRecordFixedArray(heap, elements)) { | 225 if (CanRecordFixedArray(heap, elements)) { |
205 if (elements->IsDictionary()) { | 226 if (elements->IsDictionary() && SameLiveness(object, elements)) { |
206 SeededNumberDictionary* dict = object->element_dictionary(); | 227 SeededNumberDictionary* dict = SeededNumberDictionary::cast(elements); |
207 int used = dict->NumberOfElements() * SeededNumberDictionary::kEntrySize; | 228 int used = dict->NumberOfElements() * SeededNumberDictionary::kEntrySize; |
208 CHECK_GE(elements->Size(), used); | 229 CHECK_GE(elements->Size(), used); |
209 overhead = elements->Size() - used; | 230 overhead = elements->Size() - used; |
210 stats->RecordFixedArraySubTypeStats(DICTIONARY_ELEMENTS_SUB_TYPE, | 231 stats->RecordFixedArraySubTypeStats( |
211 elements->Size(), overhead); | 232 elements, DICTIONARY_ELEMENTS_SUB_TYPE, elements->Size(), overhead); |
212 } else { | 233 } else { |
213 if (IsFastHoleyElementsKind(object->GetElementsKind())) { | 234 if (IsFastHoleyElementsKind(object->GetElementsKind())) { |
214 int used = object->GetFastElementsUsage() * kPointerSize; | 235 int used = object->GetFastElementsUsage() * kPointerSize; |
215 if (object->GetElementsKind() == FAST_HOLEY_DOUBLE_ELEMENTS) used *= 2; | 236 if (object->GetElementsKind() == FAST_HOLEY_DOUBLE_ELEMENTS) used *= 2; |
216 CHECK_GE(elements->Size(), used); | 237 CHECK_GE(elements->Size(), used); |
217 overhead = elements->Size() - used; | 238 overhead = elements->Size() - used; |
218 } | 239 } |
219 stats->RecordFixedArraySubTypeStats(FAST_ELEMENTS_SUB_TYPE, | 240 stats->RecordFixedArraySubTypeStats(elements, FAST_ELEMENTS_SUB_TYPE, |
220 elements->Size(), overhead); | 241 elements->Size(), overhead); |
221 } | 242 } |
222 } | 243 } |
223 | 244 |
224 overhead = 0; | 245 overhead = 0; |
225 FixedArrayBase* properties = object->properties(); | 246 FixedArrayBase* properties = object->properties(); |
226 if (CanRecordFixedArray(heap, properties)) { | 247 if (CanRecordFixedArray(heap, properties) && |
| 248 SameLiveness(object, properties)) { |
227 if (properties->IsDictionary()) { | 249 if (properties->IsDictionary()) { |
228 NameDictionary* dict = object->property_dictionary(); | 250 NameDictionary* dict = NameDictionary::cast(properties); |
229 int used = dict->NumberOfElements() * NameDictionary::kEntrySize; | 251 int used = dict->NumberOfElements() * NameDictionary::kEntrySize; |
230 CHECK_GE(properties->Size(), used); | 252 CHECK_GE(properties->Size(), used); |
231 overhead = properties->Size() - used; | 253 overhead = properties->Size() - used; |
232 stats->RecordFixedArraySubTypeStats(DICTIONARY_PROPERTIES_SUB_TYPE, | 254 stats->RecordFixedArraySubTypeStats(properties, |
| 255 DICTIONARY_PROPERTIES_SUB_TYPE, |
233 properties->Size(), overhead); | 256 properties->Size(), overhead); |
234 } else { | 257 } else { |
235 stats->RecordFixedArraySubTypeStats(FAST_PROPERTIES_SUB_TYPE, | 258 stats->RecordFixedArraySubTypeStats(properties, FAST_PROPERTIES_SUB_TYPE, |
236 properties->Size(), overhead); | 259 properties->Size(), overhead); |
237 } | 260 } |
238 } | 261 } |
239 } | 262 } |
240 | 263 |
241 void ObjectStatsCollector::RecordJSWeakCollectionDetails( | 264 void ObjectStatsCollector::RecordJSWeakCollectionDetails( |
242 ObjectStats* stats, Heap* heap, JSWeakCollection* obj) { | 265 ObjectStats* stats, Heap* heap, JSWeakCollection* obj) { |
243 if (obj->table()->IsHashTable()) { | 266 if (obj->table()->IsHashTable()) { |
244 ObjectHashTable* table = ObjectHashTable::cast(obj->table()); | 267 ObjectHashTable* table = ObjectHashTable::cast(obj->table()); |
245 int used = table->NumberOfElements() * ObjectHashTable::kEntrySize; | 268 int used = table->NumberOfElements() * ObjectHashTable::kEntrySize; |
246 size_t overhead = table->Size() - used; | 269 size_t overhead = table->Size() - used; |
247 stats->RecordFixedArraySubTypeStats(WEAK_COLLECTION_SUB_TYPE, table->Size(), | 270 RecordFixedArrayHelper(stats, heap, obj, table, WEAK_COLLECTION_SUB_TYPE, |
248 overhead); | 271 overhead); |
249 } | 272 } |
250 } | 273 } |
251 | 274 |
252 void ObjectStatsCollector::RecordMapDetails(ObjectStats* stats, Heap* heap, | 275 void ObjectStatsCollector::RecordMapDetails(ObjectStats* stats, Heap* heap, |
253 HeapObject* obj) { | 276 HeapObject* obj) { |
254 Map* map_obj = Map::cast(obj); | 277 Map* map_obj = Map::cast(obj); |
255 DCHECK(obj->map()->instance_type() == MAP_TYPE); | 278 DCHECK(obj->map()->instance_type() == MAP_TYPE); |
256 DescriptorArray* array = map_obj->instance_descriptors(); | 279 DescriptorArray* array = map_obj->instance_descriptors(); |
257 if (map_obj->owns_descriptors() && array != heap->empty_descriptor_array()) { | 280 if (map_obj->owns_descriptors() && array != heap->empty_descriptor_array() && |
258 int fixed_array_size = array->Size(); | 281 SameLiveness(map_obj, array)) { |
259 stats->RecordFixedArraySubTypeStats(DESCRIPTOR_ARRAY_SUB_TYPE, | 282 RecordFixedArrayHelper(stats, heap, map_obj, array, |
260 fixed_array_size, 0); | 283 DESCRIPTOR_ARRAY_SUB_TYPE, 0); |
261 if (array->HasEnumCache()) { | 284 if (array->HasEnumCache()) { |
262 stats->RecordFixedArraySubTypeStats(ENUM_CACHE_SUB_TYPE, | 285 RecordFixedArrayHelper(stats, heap, array, array->GetEnumCache(), |
263 array->GetEnumCache()->Size(), 0); | 286 ENUM_CACHE_SUB_TYPE, 0); |
264 } | 287 } |
265 if (array->HasEnumIndicesCache()) { | 288 if (array->HasEnumIndicesCache()) { |
266 stats->RecordFixedArraySubTypeStats( | 289 RecordFixedArrayHelper(stats, heap, array, array->GetEnumIndicesCache(), |
267 ENUM_INDICES_CACHE_SUB_TYPE, array->GetEnumIndicesCache()->Size(), 0); | 290 ENUM_INDICES_CACHE_SUB_TYPE, 0); |
268 } | 291 } |
269 } | 292 } |
270 | 293 |
271 if (map_obj->has_code_cache()) { | 294 if (map_obj->has_code_cache()) { |
272 FixedArray* cache = map_obj->code_cache(); | 295 RecordFixedArrayHelper(stats, heap, map_obj, map_obj->code_cache(), |
273 stats->RecordFixedArraySubTypeStats(MAP_CODE_CACHE_SUB_TYPE, cache->Size(), | 296 MAP_CODE_CACHE_SUB_TYPE, 0); |
274 0); | |
275 } | 297 } |
276 } | 298 } |
277 | 299 |
278 void ObjectStatsCollector::RecordCodeDetails(ObjectStats* stats, Heap* heap, | 300 void ObjectStatsCollector::RecordCodeDetails(ObjectStats* stats, Heap* heap, |
279 HeapObject* obj) { | 301 HeapObject* obj) { |
280 int object_size = obj->Size(); | 302 int object_size = obj->Size(); |
281 DCHECK(obj->map()->instance_type() == CODE_TYPE); | 303 DCHECK(obj->map()->instance_type() == CODE_TYPE); |
282 Code* code_obj = Code::cast(obj); | 304 Code* code_obj = Code::cast(obj); |
283 stats->RecordCodeSubTypeStats(code_obj->kind(), code_obj->GetAge(), | 305 stats->RecordCodeSubTypeStats(code_obj->kind(), code_obj->GetAge(), |
284 object_size); | 306 object_size); |
285 Code* code = Code::cast(obj); | 307 Code* code = Code::cast(obj); |
286 if (code->deoptimization_data() != heap->empty_fixed_array()) { | 308 RecordFixedArrayHelper(stats, heap, code, code->deoptimization_data(), |
287 stats->RecordFixedArraySubTypeStats(DEOPTIMIZATION_DATA_SUB_TYPE, | 309 DEOPTIMIZATION_DATA_SUB_TYPE, 0); |
288 code->deoptimization_data()->Size(), 0); | |
289 } | |
290 FixedArrayBase* reloc_info = | |
291 reinterpret_cast<FixedArrayBase*>(code->unchecked_relocation_info()); | |
292 if (reloc_info != heap->empty_fixed_array()) { | |
293 stats->RecordFixedArraySubTypeStats(RELOC_INFO_SUB_TYPE, | |
294 code->relocation_info()->Size(), 0); | |
295 } | |
296 FixedArrayBase* source_pos_table = | |
297 reinterpret_cast<FixedArrayBase*>(code->source_position_table()); | |
298 if (source_pos_table != heap->empty_fixed_array()) { | |
299 stats->RecordFixedArraySubTypeStats(SOURCE_POS_SUB_TYPE, | |
300 source_pos_table->Size(), 0); | |
301 } | |
302 } | 310 } |
303 | 311 |
304 void ObjectStatsCollector::RecordSharedFunctionInfoDetails(ObjectStats* stats, | 312 void ObjectStatsCollector::RecordSharedFunctionInfoDetails(ObjectStats* stats, |
305 Heap* heap, | 313 Heap* heap, |
306 HeapObject* obj) { | 314 HeapObject* obj) { |
307 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); | 315 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj); |
308 if (sfi->scope_info() != heap->empty_fixed_array()) { | 316 FixedArray* scope_info = sfi->scope_info(); |
309 stats->RecordFixedArraySubTypeStats(SCOPE_INFO_SUB_TYPE, | 317 RecordFixedArrayHelper(stats, heap, sfi, scope_info, SCOPE_INFO_SUB_TYPE, 0); |
310 sfi->scope_info()->Size(), 0); | 318 FixedArray* feedback_metadata = sfi->feedback_metadata(); |
311 } | 319 RecordFixedArrayHelper(stats, heap, sfi, feedback_metadata, |
312 if (sfi->feedback_metadata() != heap->empty_fixed_array()) { | 320 TYPE_FEEDBACK_METADATA_SUB_TYPE, 0); |
313 stats->RecordFixedArraySubTypeStats(TYPE_FEEDBACK_METADATA_SUB_TYPE, | 321 |
314 sfi->feedback_metadata()->Size(), 0); | |
315 } | |
316 if (!sfi->OptimizedCodeMapIsCleared()) { | 322 if (!sfi->OptimizedCodeMapIsCleared()) { |
317 FixedArray* optimized_code_map = sfi->optimized_code_map(); | 323 FixedArray* optimized_code_map = sfi->optimized_code_map(); |
318 // Optimized code map should be small, so skip accounting. | 324 // Optimized code map should be small, so skip accounting. |
319 int len = optimized_code_map->length(); | 325 int len = optimized_code_map->length(); |
320 for (int i = SharedFunctionInfo::kEntriesStart; i < len; | 326 for (int i = SharedFunctionInfo::kEntriesStart; i < len; |
321 i += SharedFunctionInfo::kEntryLength) { | 327 i += SharedFunctionInfo::kEntryLength) { |
322 Object* slot = | 328 Object* slot = |
323 optimized_code_map->get(i + SharedFunctionInfo::kLiteralsOffset); | 329 optimized_code_map->get(i + SharedFunctionInfo::kLiteralsOffset); |
324 LiteralsArray* literals = nullptr; | 330 LiteralsArray* literals = nullptr; |
325 if (slot->IsWeakCell()) { | 331 if (slot->IsWeakCell()) { |
326 WeakCell* cell = WeakCell::cast(slot); | 332 WeakCell* cell = WeakCell::cast(slot); |
327 if (!cell->cleared()) { | 333 if (!cell->cleared()) { |
328 literals = LiteralsArray::cast(cell->value()); | 334 literals = LiteralsArray::cast(cell->value()); |
329 } | 335 } |
330 } else { | 336 } else { |
331 literals = LiteralsArray::cast(slot); | 337 literals = LiteralsArray::cast(slot); |
332 } | 338 } |
333 if (literals != nullptr) { | 339 if (literals != nullptr) { |
334 stats->RecordFixedArraySubTypeStats(LITERALS_ARRAY_SUB_TYPE, | 340 RecordFixedArrayHelper(stats, heap, sfi, literals, |
335 literals->Size(), 0); | 341 LITERALS_ARRAY_SUB_TYPE, 0); |
336 TypeFeedbackVector* tfv = literals->feedback_vector(); | 342 RecordFixedArrayHelper(stats, heap, sfi, literals->feedback_vector(), |
337 | 343 TYPE_FEEDBACK_VECTOR_SUB_TYPE, 0); |
338 stats->RecordFixedArraySubTypeStats(TYPE_FEEDBACK_VECTOR_SUB_TYPE, | |
339 tfv->Size(), 0); | |
340 } | 344 } |
341 } | 345 } |
342 } | 346 } |
343 } | 347 } |
344 | 348 |
345 void ObjectStatsCollector::RecordFixedArrayDetails(ObjectStats* stats, | 349 void ObjectStatsCollector::RecordFixedArrayDetails(ObjectStats* stats, |
346 Heap* heap, | 350 Heap* heap, |
347 HeapObject* obj) { | 351 HeapObject* obj) { |
348 FixedArray* fixed_array = FixedArray::cast(obj); | 352 FixedArray* fixed_array = FixedArray::cast(obj); |
349 if (fixed_array == heap->string_table()) { | |
350 stats->RecordFixedArraySubTypeStats(STRING_TABLE_SUB_TYPE, | |
351 fixed_array->Size(), 0); | |
352 } | |
353 if (fixed_array == heap->weak_object_to_code_table()) { | 353 if (fixed_array == heap->weak_object_to_code_table()) { |
354 WeakHashTable* table = reinterpret_cast<WeakHashTable*>(fixed_array); | 354 WeakHashTable* table = reinterpret_cast<WeakHashTable*>(fixed_array); |
355 int used = table->NumberOfElements() * WeakHashTable::kEntrySize; | 355 int used = table->NumberOfElements() * WeakHashTable::kEntrySize; |
356 CHECK_GE(fixed_array->Size(), used); | 356 CHECK_GE(fixed_array->Size(), used); |
357 size_t overhead = fixed_array->Size() - used; | 357 size_t overhead = fixed_array->Size() - used; |
358 stats->RecordFixedArraySubTypeStats(OBJECT_TO_CODE_SUB_TYPE, | 358 stats->RecordFixedArraySubTypeStats(table, OBJECT_TO_CODE_SUB_TYPE, |
359 fixed_array->Size(), overhead); | 359 fixed_array->Size(), overhead); |
360 } | 360 } |
361 if (obj->IsContext()) { | 361 |
362 stats->RecordFixedArraySubTypeStats(CONTEXT_SUB_TYPE, fixed_array->Size(), | 362 int subtype = -1; |
363 0); | 363 if (fixed_array == heap->string_table()) subtype = STRING_TABLE_SUB_TYPE; |
364 } | 364 if (fixed_array->IsContext()) subtype = CONTEXT_SUB_TYPE; |
365 if (fixed_array->map() == heap->fixed_cow_array_map()) { | 365 if (fixed_array->map() == heap->fixed_cow_array_map()) |
366 stats->RecordFixedArraySubTypeStats(COPY_ON_WRITE_SUB_TYPE, | 366 subtype = COPY_ON_WRITE_SUB_TYPE; |
| 367 if (subtype != -1) { |
| 368 stats->RecordFixedArraySubTypeStats(fixed_array, subtype, |
367 fixed_array->Size(), 0); | 369 fixed_array->Size(), 0); |
368 } | 370 } |
369 } | 371 } |
370 | 372 |
371 } // namespace internal | 373 } // namespace internal |
372 } // namespace v8 | 374 } // namespace v8 |
OLD | NEW |