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 3 // found in the LICENSE file. 4 5 #include "src/heap/scavenger.h" 6 7 #include "src/contexts.h" 8 #include "src/heap/heap.h" 9 #include "src/heap/objects-visiting-inl.h" 10 #include "src/heap/scavenger-inl.h" 11 #include "src/isolate.h" 12 #include "src/log.h" 13 #include "src/profiler/cpu-profiler.h" 14 15 namespace v8 { 16 namespace internal { 17 18 enum LoggingAndProfiling { 19 LOGGING_AND_PROFILING_ENABLED, 20 LOGGING_AND_PROFILING_DISABLED 21 }; 22 23 24 enum MarksHandling { TRANSFER_MARKS, IGNORE_MARKS }; 25 26 27 template <MarksHandling marks_handling, 28 LoggingAndProfiling logging_and_profiling_mode> 29 class ScavengingVisitor : public StaticVisitorBase { 30 public: 31 static void Initialize() { 32 table_.Register(kVisitSeqOneByteString, &EvacuateSeqOneByteString); 33 table_.Register(kVisitSeqTwoByteString, &EvacuateSeqTwoByteString); 34 table_.Register(kVisitShortcutCandidate, &EvacuateShortcutCandidate); 35 table_.Register(kVisitByteArray, &EvacuateByteArray); 36 table_.Register(kVisitFixedArray, &EvacuateFixedArray); 37 table_.Register(kVisitFixedDoubleArray, &EvacuateFixedDoubleArray); 38 table_.Register(kVisitFixedTypedArray, &EvacuateFixedTypedArray); 39 table_.Register(kVisitFixedFloat64Array, &EvacuateFixedFloat64Array); 40 table_.Register(kVisitJSArrayBuffer, &EvacuateJSArrayBuffer); 41 42 table_.Register( 43 kVisitNativeContext, 44 &ObjectEvacuationStrategy<POINTER_OBJECT>::template VisitSpecialized< 45 Context::kSize>); 46 47 table_.Register( 48 kVisitConsString, 49 &ObjectEvacuationStrategy<POINTER_OBJECT>::template VisitSpecialized< 50 ConsString::kSize>); 51 52 table_.Register( 53 kVisitSlicedString, 54 &ObjectEvacuationStrategy<POINTER_OBJECT>::template VisitSpecialized< 55 SlicedString::kSize>); 56 57 table_.Register( 58 kVisitSymbol, 59 &ObjectEvacuationStrategy<POINTER_OBJECT>::template VisitSpecialized< 60 Symbol::kSize>); 61 62 table_.Register( 63 kVisitSharedFunctionInfo, 64 &ObjectEvacuationStrategy<POINTER_OBJECT>::template VisitSpecialized< 65 SharedFunctionInfo::kSize>); 66 67 table_.Register(kVisitJSWeakCollection, 68 &ObjectEvacuationStrategy<POINTER_OBJECT>::Visit); 69 70 table_.Register(kVisitJSRegExp, 71 &ObjectEvacuationStrategy<POINTER_OBJECT>::Visit); 72 73 table_.Register(kVisitJSFunction, &EvacuateJSFunction); 74 75 table_.RegisterSpecializations<ObjectEvacuationStrategy<DATA_OBJECT>, 76 kVisitDataObject, kVisitDataObjectGeneric>(); 77 78 table_.RegisterSpecializations<ObjectEvacuationStrategy<POINTER_OBJECT>, 79 kVisitJSObject, kVisitJSObjectGeneric>(); 80 81 table_.RegisterSpecializations<ObjectEvacuationStrategy<POINTER_OBJECT>, 82 kVisitStruct, kVisitStructGeneric>(); 83 } 84 85 static VisitorDispatchTable<ScavengingCallback>* GetTable() { 86 return &table_; 87 } 88 89 private: 90 enum ObjectContents { DATA_OBJECT, POINTER_OBJECT }; 91 92 static void RecordCopiedObject(Heap* heap, HeapObject* obj) { 93 bool should_record = false; 94 #ifdef DEBUG 95 should_record = FLAG_heap_stats; 96 #endif 97 should_record = should_record || FLAG_log_gc; 98 if (should_record) { 99 if (heap->new_space()->Contains(obj)) { 100 heap->new_space()->RecordAllocation(obj); 101 } else { 102 heap->new_space()->RecordPromotion(obj); 103 } 104 } 105 } 106 107 // Helper function used by CopyObject to copy a source object to an 108 // allocated target object and update the forwarding pointer in the source 109 // object. Returns the target object. 110 INLINE(static void MigrateObject(Heap* heap, HeapObject* source, 111 HeapObject* target, int size)) { 112 // If we migrate into to-space, then the to-space top pointer should be 113 // right after the target object. Incorporate double alignment 114 // over-allocation. 115 DCHECK(!heap->InToSpace(target) || 116 target->address() + size == heap->new_space()->top() || 117 target->address() + size + kPointerSize == heap->new_space()->top()); 118 119 // Make sure that we do not overwrite the promotion queue which is at 120 // the end of to-space. 121 DCHECK(!heap->InToSpace(target) || 122 heap->promotion_queue()->IsBelowPromotionQueue( 123 heap->new_space()->top())); 124 125 // Copy the content of source to target. 126 heap->CopyBlock(target->address(), source->address(), size); 127 128 // Set the forwarding address. 129 source->set_map_word(MapWord::FromForwardingAddress(target)); 130 131 if (logging_and_profiling_mode == LOGGING_AND_PROFILING_ENABLED) { 132 // Update NewSpace stats if necessary. 133 RecordCopiedObject(heap, target); 134 heap->OnMoveEvent(target, source, size); 135 } 136 137 if (marks_handling == TRANSFER_MARKS) { 138 if (Marking::TransferColor(source, target)) { 139 MemoryChunk::IncrementLiveBytesFromGC(target, size); 140 } 141 } 142 } 143 144 template <AllocationAlignment alignment> 145 static inline bool SemiSpaceCopyObject(Map* map, HeapObject** slot, 146 HeapObject* object, int object_size) { 147 Heap* heap = map->GetHeap(); 148 149 DCHECK(heap->AllowedToBeMigrated(object, NEW_SPACE)); 150 AllocationResult allocation = 151 heap->new_space()->AllocateRaw(object_size, alignment); 152 153 HeapObject* target = NULL; // Initialization to please compiler. 154 if (allocation.To(&target)) { 155 // Order is important here: Set the promotion limit before storing a 156 // filler for double alignment or migrating the object. Otherwise we 157 // may end up overwriting promotion queue entries when we migrate the 158 // object. 159 heap->promotion_queue()->SetNewLimit(heap->new_space()->top()); 160 161 MigrateObject(heap, object, target, object_size); 162 163 // Update slot to new target. 164 *slot = target; 165 166 heap->IncrementSemiSpaceCopiedObjectSize(object_size); 167 return true; 168 } 169 return false; 170 } 171 172 173 template <ObjectContents object_contents, AllocationAlignment alignment> 174 static inline bool PromoteObject(Map* map, HeapObject** slot, 175 HeapObject* object, int object_size) { 176 Heap* heap = map->GetHeap(); 177 178 AllocationResult allocation = 179 heap->old_space()->AllocateRaw(object_size, alignment); 180 181 HeapObject* target = NULL; // Initialization to please compiler. 182 if (allocation.To(&target)) { 183 MigrateObject(heap, object, target, object_size); 184 185 // Update slot to new target. 186 *slot = target; 187 188 if (object_contents == POINTER_OBJECT) { 189 heap->promotion_queue()->insert(target, object_size); 190 } 191 heap->IncrementPromotedObjectsSize(object_size); 192 return true; 193 } 194 return false; 195 } 196 197 198 template <ObjectContents object_contents, AllocationAlignment alignment> 199 static inline void EvacuateObject(Map* map, HeapObject** slot, 200 HeapObject* object, int object_size) { 201 SLOW_DCHECK(object_size <= Page::kAllocatableMemory); 202 SLOW_DCHECK(object->Size() == object_size); 203 Heap* heap = map->GetHeap(); 204 205 if (!heap->ShouldBePromoted(object->address(), object_size)) { 206 // A semi-space copy may fail due to fragmentation. In that case, we 207 // try to promote the object. 208 if (SemiSpaceCopyObject<alignment>(map, slot, object, object_size)) { 209 return; 210 } 211 } 212 213 if (PromoteObject<object_contents, alignment>(map, slot, object, 214 object_size)) { 215 return; 216 } 217 218 // If promotion failed, we try to copy the object to the other semi-space 219 if (SemiSpaceCopyObject<alignment>(map, slot, object, object_size)) return; 220 221 FatalProcessOutOfMemory("Scavenger: semi-space copy\n"); 222 } 223 224 225 static inline void EvacuateJSFunction(Map* map, HeapObject** slot, 226 HeapObject* object) { 227 ObjectEvacuationStrategy<POINTER_OBJECT>::Visit(map, slot, object); 228 229 if (marks_handling == IGNORE_MARKS) return; 230 231 MapWord map_word = object->map_word(); 232 DCHECK(map_word.IsForwardingAddress()); 233 HeapObject* target = map_word.ToForwardingAddress(); 234 235 MarkBit mark_bit = Marking::MarkBitFrom(target); 236 if (Marking::IsBlack(mark_bit)) { 237 // This object is black and it might not be rescanned by marker. 238 // We should explicitly record code entry slot for compaction because 239 // promotion queue processing (IterateAndMarkPointersToFromSpace) will 240 // miss it as it is not HeapObject-tagged. 241 Address code_entry_slot = 242 target->address() + JSFunction::kCodeEntryOffset; 243 Code* code = Code::cast(Code::GetObjectFromEntryAddress(code_entry_slot)); 244 map->GetHeap()->mark_compact_collector()->RecordCodeEntrySlot( 245 target, code_entry_slot, code); 246 } 247 } 248 249 250 static inline void EvacuateFixedArray(Map* map, HeapObject** slot, 251 HeapObject* object) { 252 int length = reinterpret_cast<FixedArray*>(object)->synchronized_length(); 253 int object_size = FixedArray::SizeFor(length); 254 EvacuateObject<POINTER_OBJECT, kWordAligned>(map, slot, object, 255 object_size); 256 } 257 258 259 static inline void EvacuateFixedDoubleArray(Map* map, HeapObject** slot, 260 HeapObject* object) { 261 int length = reinterpret_cast<FixedDoubleArray*>(object)->length(); 262 int object_size = FixedDoubleArray::SizeFor(length); 263 EvacuateObject<DATA_OBJECT, kDoubleAligned>(map, slot, object, object_size); 264 } 265 266 267 static inline void EvacuateFixedTypedArray(Map* map, HeapObject** slot, 268 HeapObject* object) { 269 int object_size = reinterpret_cast<FixedTypedArrayBase*>(object)->size(); 270 EvacuateObject<POINTER_OBJECT, kWordAligned>(map, slot, object, 271 object_size); 272 } 273 274 275 static inline void EvacuateFixedFloat64Array(Map* map, HeapObject** slot, 276 HeapObject* object) { 277 int object_size = reinterpret_cast<FixedFloat64Array*>(object)->size(); 278 EvacuateObject<POINTER_OBJECT, kDoubleAligned>(map, slot, object, 279 object_size); 280 } 281 282 283 static inline void EvacuateJSArrayBuffer(Map* map, HeapObject** slot, 284 HeapObject* object) { 285 ObjectEvacuationStrategy<POINTER_OBJECT>::Visit(map, slot, object); 286 287 Heap* heap = map->GetHeap(); 288 MapWord map_word = object->map_word(); 289 DCHECK(map_word.IsForwardingAddress()); 290 HeapObject* target = map_word.ToForwardingAddress(); 291 if (!heap->InNewSpace(target)) { 292 heap->array_buffer_tracker()->Promote(JSArrayBuffer::cast(target)); 293 } 294 } 295 296 297 static inline void EvacuateByteArray(Map* map, HeapObject** slot, 298 HeapObject* object) { 299 int object_size = reinterpret_cast<ByteArray*>(object)->ByteArraySize(); 300 EvacuateObject<DATA_OBJECT, kWordAligned>(map, slot, object, object_size); 301 } 302 303 304 static inline void EvacuateSeqOneByteString(Map* map, HeapObject** slot, 305 HeapObject* object) { 306 int object_size = SeqOneByteString::cast(object) 307 ->SeqOneByteStringSize(map->instance_type()); 308 EvacuateObject<DATA_OBJECT, kWordAligned>(map, slot, object, object_size); 309 } 310 311 312 static inline void EvacuateSeqTwoByteString(Map* map, HeapObject** slot, 313 HeapObject* object) { 314 int object_size = SeqTwoByteString::cast(object) 315 ->SeqTwoByteStringSize(map->instance_type()); 316 EvacuateObject<DATA_OBJECT, kWordAligned>(map, slot, object, object_size); 317 } 318 319 320 static inline void EvacuateShortcutCandidate(Map* map, HeapObject** slot, 321 HeapObject* object) { 322 DCHECK(IsShortcutCandidate(map->instance_type())); 323 324 Heap* heap = map->GetHeap(); 325 326 if (marks_handling == IGNORE_MARKS && 327 ConsString::cast(object)->unchecked_second() == heap->empty_string()) { 328 HeapObject* first = 329 HeapObject::cast(ConsString::cast(object)->unchecked_first()); 330 331 *slot = first; 332 333 if (!heap->InNewSpace(first)) { 334 object->set_map_word(MapWord::FromForwardingAddress(first)); 335 return; 336 } 337 338 MapWord first_word = first->map_word(); 339 if (first_word.IsForwardingAddress()) { 340 HeapObject* target = first_word.ToForwardingAddress(); 341 342 *slot = target; 343 object->set_map_word(MapWord::FromForwardingAddress(target)); 344 return; 345 } 346 347 Scavenger::ScavengeObjectSlow(slot, first); 348 object->set_map_word(MapWord::FromForwardingAddress(*slot)); 349 return; 350 } 351 352 int object_size = ConsString::kSize; 353 EvacuateObject<POINTER_OBJECT, kWordAligned>(map, slot, object, 354 object_size); 355 } 356 357 template <ObjectContents object_contents> 358 class ObjectEvacuationStrategy { 359 public: 360 template <int object_size> 361 static inline void VisitSpecialized(Map* map, HeapObject** slot, 362 HeapObject* object) { 363 EvacuateObject<object_contents, kWordAligned>(map, slot, object, 364 object_size); 365 } 366 367 static inline void Visit(Map* map, HeapObject** slot, HeapObject* object) { 368 int object_size = map->instance_size(); 369 EvacuateObject<object_contents, kWordAligned>(map, slot, object, 370 object_size); 371 } 372 }; 373 374 static VisitorDispatchTable<ScavengingCallback> table_; 375 }; 376 377 378 template <MarksHandling marks_handling, 379 LoggingAndProfiling logging_and_profiling_mode> 380 VisitorDispatchTable<ScavengingCallback> 381 ScavengingVisitor<marks_handling, logging_and_profiling_mode>::table_; 382 383 384 // static 385 void Scavenger::Initialize() { 386 ScavengingVisitor<TRANSFER_MARKS, 387 LOGGING_AND_PROFILING_DISABLED>::Initialize(); 388 ScavengingVisitor<IGNORE_MARKS, LOGGING_AND_PROFILING_DISABLED>::Initialize(); 389 ScavengingVisitor<TRANSFER_MARKS, 390 LOGGING_AND_PROFILING_ENABLED>::Initialize(); 391 ScavengingVisitor<IGNORE_MARKS, LOGGING_AND_PROFILING_ENABLED>::Initialize(); 392 } 393 394 395 // static 396 void Scavenger::ScavengeObjectSlow(HeapObject** p, HeapObject* object) { 397 SLOW_DCHECK(object->GetIsolate()->heap()->InFromSpace(object)); 398 MapWord first_word = object->map_word(); 399 SLOW_DCHECK(!first_word.IsForwardingAddress()); 400 Map* map = first_word.ToMap(); 401 Scavenger* scavenger = map->GetHeap()->scavenge_collector_; 402 scavenger->scavenging_visitors_table_.GetVisitor(map)(map, p, object); 403 } 404 405 406 void Scavenger::SelectScavengingVisitorsTable() { 407 bool logging_and_profiling = 408 FLAG_verify_predictable || isolate()->logger()->is_logging() || 409 isolate()->cpu_profiler()->is_profiling() || 410 (isolate()->heap_profiler() != NULL && 411 isolate()->heap_profiler()->is_tracking_object_moves()); 412 413 if (!heap()->incremental_marking()->IsMarking()) { 414 if (!logging_and_profiling) { 415 scavenging_visitors_table_.CopyFrom( 416 ScavengingVisitor<IGNORE_MARKS, 417 LOGGING_AND_PROFILING_DISABLED>::GetTable()); 418 } else { 419 scavenging_visitors_table_.CopyFrom( 420 ScavengingVisitor<IGNORE_MARKS, 421 LOGGING_AND_PROFILING_ENABLED>::GetTable()); 422 } 423 } else { 424 if (!logging_and_profiling) { 425 scavenging_visitors_table_.CopyFrom( 426 ScavengingVisitor<TRANSFER_MARKS, 427 LOGGING_AND_PROFILING_DISABLED>::GetTable()); 428 } else { 429 scavenging_visitors_table_.CopyFrom( 430 ScavengingVisitor<TRANSFER_MARKS, 431 LOGGING_AND_PROFILING_ENABLED>::GetTable()); 432 } 433 434 if (heap()->incremental_marking()->IsCompacting()) { 435 // When compacting forbid short-circuiting of cons-strings. 436 // Scavenging code relies on the fact that new space object 437 // can't be evacuated into evacuation candidate but 438 // short-circuiting violates this assumption. 439 scavenging_visitors_table_.Register( 440 StaticVisitorBase::kVisitShortcutCandidate, 441 scavenging_visitors_table_.GetVisitorById( 442 StaticVisitorBase::kVisitConsString)); 443 } 444 } 445 } 446 447 448 Isolate* Scavenger::isolate() { return heap()->isolate(); } 449 450 451 void ScavengeVisitor::VisitPointer(Object** p) { ScavengePointer(p); } 452 453 454 void ScavengeVisitor::VisitPointers(Object** start, Object** end) { 455 // Copy all HeapObject pointers in [start, end) 456 for (Object** p = start; p < end; p++) ScavengePointer(p); 457 } 458 459 460 void ScavengeVisitor::ScavengePointer(Object** p) { 461 Object* object = *p; 462 if (!heap_->InNewSpace(object)) return; 463 Scavenger::ScavengeObject(reinterpret_cast<HeapObject**>(p), 464 reinterpret_cast<HeapObject*>(object)); 465 } 466 467 } // namespace internal 468 } // namespace v8 469