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 #ifndef V8_HEAP_SCAVENGER_INL_H_ 6 #define V8_HEAP_SCAVENGER_INL_H_ 7 8 #include "src/heap/scavenger.h" 9 10 #include "src/heap/local-allocator-inl.h" 11 #include "src/objects-inl.h" 12 #include "src/objects/map.h" 13 14 namespace v8 { 15 namespace internal { 16 17 // White list for objects that for sure only contain data. ContainsOnlyData(VisitorId visitor_id)18 bool Scavenger::ContainsOnlyData(VisitorId visitor_id) { 19 switch (visitor_id) { 20 case kVisitSeqOneByteString: 21 return true; 22 case kVisitSeqTwoByteString: 23 return true; 24 case kVisitByteArray: 25 return true; 26 case kVisitFixedDoubleArray: 27 return true; 28 case kVisitDataObject: 29 return true; 30 default: 31 break; 32 } 33 return false; 34 } 35 PageMemoryFence(MaybeObject * object)36 void Scavenger::PageMemoryFence(MaybeObject* object) { 37 #ifdef THREAD_SANITIZER 38 // Perform a dummy acquire load to tell TSAN that there is no data race 39 // with page initialization. 40 HeapObject* heap_object; 41 if (object->ToStrongOrWeakHeapObject(&heap_object)) { 42 MemoryChunk* chunk = MemoryChunk::FromAddress(heap_object->address()); 43 CHECK_NOT_NULL(chunk->synchronized_heap()); 44 } 45 #endif 46 } 47 MigrateObject(Map * map,HeapObject * source,HeapObject * target,int size)48 bool Scavenger::MigrateObject(Map* map, HeapObject* source, HeapObject* target, 49 int size) { 50 // Copy the content of source to target. 51 target->set_map_word(MapWord::FromMap(map)); 52 heap()->CopyBlock(target->address() + kPointerSize, 53 source->address() + kPointerSize, size - kPointerSize); 54 55 HeapObject* old = base::AsAtomicPointer::Release_CompareAndSwap( 56 reinterpret_cast<HeapObject**>(source->address()), map, 57 MapWord::FromForwardingAddress(target).ToMap()); 58 if (old != map) { 59 // Other task migrated the object. 60 return false; 61 } 62 63 if (V8_UNLIKELY(is_logging_)) { 64 heap()->OnMoveEvent(target, source, size); 65 } 66 67 if (is_incremental_marking_) { 68 heap()->incremental_marking()->TransferColor(source, target); 69 } 70 heap()->UpdateAllocationSite(map, source, &local_pretenuring_feedback_); 71 return true; 72 } 73 SemiSpaceCopyObject(Map * map,HeapObjectReference ** slot,HeapObject * object,int object_size)74 bool Scavenger::SemiSpaceCopyObject(Map* map, HeapObjectReference** slot, 75 HeapObject* object, int object_size) { 76 DCHECK(heap()->AllowedToBeMigrated(object, NEW_SPACE)); 77 AllocationAlignment alignment = HeapObject::RequiredAlignment(map); 78 AllocationResult allocation = 79 allocator_.Allocate(NEW_SPACE, object_size, alignment); 80 81 HeapObject* target = nullptr; 82 if (allocation.To(&target)) { 83 DCHECK(heap()->incremental_marking()->non_atomic_marking_state()->IsWhite( 84 target)); 85 const bool self_success = MigrateObject(map, object, target, object_size); 86 if (!self_success) { 87 allocator_.FreeLast(NEW_SPACE, target, object_size); 88 MapWord map_word = object->map_word(); 89 HeapObjectReference::Update(slot, map_word.ToForwardingAddress()); 90 return true; 91 } 92 HeapObjectReference::Update(slot, target); 93 94 copied_list_.Push(ObjectAndSize(target, object_size)); 95 copied_size_ += object_size; 96 return true; 97 } 98 return false; 99 } 100 PromoteObject(Map * map,HeapObjectReference ** slot,HeapObject * object,int object_size)101 bool Scavenger::PromoteObject(Map* map, HeapObjectReference** slot, 102 HeapObject* object, int object_size) { 103 AllocationAlignment alignment = HeapObject::RequiredAlignment(map); 104 AllocationResult allocation = 105 allocator_.Allocate(OLD_SPACE, object_size, alignment); 106 107 HeapObject* target = nullptr; 108 if (allocation.To(&target)) { 109 DCHECK(heap()->incremental_marking()->non_atomic_marking_state()->IsWhite( 110 target)); 111 const bool self_success = MigrateObject(map, object, target, object_size); 112 if (!self_success) { 113 allocator_.FreeLast(OLD_SPACE, target, object_size); 114 MapWord map_word = object->map_word(); 115 HeapObjectReference::Update(slot, map_word.ToForwardingAddress()); 116 return true; 117 } 118 HeapObjectReference::Update(slot, target); 119 if (!ContainsOnlyData(map->visitor_id())) { 120 promotion_list_.Push(ObjectAndSize(target, object_size)); 121 } 122 promoted_size_ += object_size; 123 return true; 124 } 125 return false; 126 } 127 EvacuateObjectDefault(Map * map,HeapObjectReference ** slot,HeapObject * object,int object_size)128 void Scavenger::EvacuateObjectDefault(Map* map, HeapObjectReference** slot, 129 HeapObject* object, int object_size) { 130 SLOW_DCHECK(object_size <= Page::kAllocatableMemory); 131 SLOW_DCHECK(object->SizeFromMap(map) == object_size); 132 133 if (!heap()->ShouldBePromoted(object->address())) { 134 // A semi-space copy may fail due to fragmentation. In that case, we 135 // try to promote the object. 136 if (SemiSpaceCopyObject(map, slot, object, object_size)) return; 137 } 138 139 if (PromoteObject(map, slot, object, object_size)) return; 140 141 // If promotion failed, we try to copy the object to the other semi-space 142 if (SemiSpaceCopyObject(map, slot, object, object_size)) return; 143 144 heap()->FatalProcessOutOfMemory("Scavenger: semi-space copy"); 145 } 146 EvacuateThinString(Map * map,HeapObject ** slot,ThinString * object,int object_size)147 void Scavenger::EvacuateThinString(Map* map, HeapObject** slot, 148 ThinString* object, int object_size) { 149 if (!is_incremental_marking_) { 150 // Loading actual is fine in a parallel setting is there is no write. 151 String* actual = object->actual(); 152 object->set_length(0); 153 *slot = actual; 154 // ThinStrings always refer to internalized strings, which are 155 // always in old space. 156 DCHECK(!Heap::InNewSpace(actual)); 157 base::AsAtomicPointer::Relaxed_Store( 158 reinterpret_cast<Map**>(object->address()), 159 MapWord::FromForwardingAddress(actual).ToMap()); 160 return; 161 } 162 163 EvacuateObjectDefault(map, reinterpret_cast<HeapObjectReference**>(slot), 164 object, object_size); 165 } 166 EvacuateShortcutCandidate(Map * map,HeapObject ** slot,ConsString * object,int object_size)167 void Scavenger::EvacuateShortcutCandidate(Map* map, HeapObject** slot, 168 ConsString* object, int object_size) { 169 DCHECK(IsShortcutCandidate(map->instance_type())); 170 if (!is_incremental_marking_ && 171 object->unchecked_second() == ReadOnlyRoots(heap()).empty_string()) { 172 HeapObject* first = HeapObject::cast(object->unchecked_first()); 173 174 *slot = first; 175 176 if (!Heap::InNewSpace(first)) { 177 base::AsAtomicPointer::Relaxed_Store( 178 reinterpret_cast<Map**>(object->address()), 179 MapWord::FromForwardingAddress(first).ToMap()); 180 return; 181 } 182 183 MapWord first_word = first->map_word(); 184 if (first_word.IsForwardingAddress()) { 185 HeapObject* target = first_word.ToForwardingAddress(); 186 187 *slot = target; 188 base::AsAtomicPointer::Relaxed_Store( 189 reinterpret_cast<Map**>(object->address()), 190 MapWord::FromForwardingAddress(target).ToMap()); 191 return; 192 } 193 Map* map = first_word.ToMap(); 194 EvacuateObjectDefault(map, reinterpret_cast<HeapObjectReference**>(slot), 195 first, first->SizeFromMap(map)); 196 base::AsAtomicPointer::Relaxed_Store( 197 reinterpret_cast<Map**>(object->address()), 198 MapWord::FromForwardingAddress(*slot).ToMap()); 199 return; 200 } 201 202 EvacuateObjectDefault(map, reinterpret_cast<HeapObjectReference**>(slot), 203 object, object_size); 204 } 205 EvacuateObject(HeapObjectReference ** slot,Map * map,HeapObject * source)206 void Scavenger::EvacuateObject(HeapObjectReference** slot, Map* map, 207 HeapObject* source) { 208 SLOW_DCHECK(Heap::InFromSpace(source)); 209 SLOW_DCHECK(!MapWord::FromMap(map).IsForwardingAddress()); 210 int size = source->SizeFromMap(map); 211 // Cannot use ::cast() below because that would add checks in debug mode 212 // that require re-reading the map. 213 switch (map->visitor_id()) { 214 case kVisitThinString: 215 // At the moment we don't allow weak pointers to thin strings. 216 DCHECK(!(*slot)->IsWeakHeapObject()); 217 EvacuateThinString(map, reinterpret_cast<HeapObject**>(slot), 218 reinterpret_cast<ThinString*>(source), size); 219 break; 220 case kVisitShortcutCandidate: 221 DCHECK(!(*slot)->IsWeakHeapObject()); 222 // At the moment we don't allow weak pointers to cons strings. 223 EvacuateShortcutCandidate(map, reinterpret_cast<HeapObject**>(slot), 224 reinterpret_cast<ConsString*>(source), size); 225 break; 226 default: 227 EvacuateObjectDefault(map, slot, source, size); 228 break; 229 } 230 } 231 ScavengeObject(HeapObjectReference ** p,HeapObject * object)232 void Scavenger::ScavengeObject(HeapObjectReference** p, HeapObject* object) { 233 DCHECK(Heap::InFromSpace(object)); 234 235 // Synchronized load that consumes the publishing CAS of MigrateObject. 236 MapWord first_word = object->synchronized_map_word(); 237 238 // If the first word is a forwarding address, the object has already been 239 // copied. 240 if (first_word.IsForwardingAddress()) { 241 HeapObject* dest = first_word.ToForwardingAddress(); 242 DCHECK(Heap::InFromSpace(*p)); 243 if ((*p)->IsWeakHeapObject()) { 244 *p = HeapObjectReference::Weak(dest); 245 } else { 246 DCHECK((*p)->IsStrongHeapObject()); 247 *p = HeapObjectReference::Strong(dest); 248 } 249 return; 250 } 251 252 Map* map = first_word.ToMap(); 253 // AllocationMementos are unrooted and shouldn't survive a scavenge 254 DCHECK_NE(ReadOnlyRoots(heap()).allocation_memento_map(), map); 255 // Call the slow part of scavenge object. 256 EvacuateObject(p, map, object); 257 } 258 CheckAndScavengeObject(Heap * heap,Address slot_address)259 SlotCallbackResult Scavenger::CheckAndScavengeObject(Heap* heap, 260 Address slot_address) { 261 MaybeObject** slot = reinterpret_cast<MaybeObject**>(slot_address); 262 MaybeObject* object = *slot; 263 if (Heap::InFromSpace(object)) { 264 HeapObject* heap_object; 265 bool success = object->ToStrongOrWeakHeapObject(&heap_object); 266 USE(success); 267 DCHECK(success); 268 DCHECK(heap_object->IsHeapObject()); 269 270 ScavengeObject(reinterpret_cast<HeapObjectReference**>(slot), heap_object); 271 272 object = *slot; 273 // If the object was in from space before and is after executing the 274 // callback in to space, the object is still live. 275 // Unfortunately, we do not know about the slot. It could be in a 276 // just freed free space object. 277 PageMemoryFence(object); 278 if (Heap::InToSpace(object)) { 279 return KEEP_SLOT; 280 } 281 } else if (Heap::InToSpace(object)) { 282 // Already updated slot. This can happen when processing of the work list 283 // is interleaved with processing roots. 284 return KEEP_SLOT; 285 } 286 // Slots can point to "to" space if the slot has been recorded multiple 287 // times in the remembered set. We remove the redundant slot now. 288 return REMOVE_SLOT; 289 } 290 VisitPointers(HeapObject * host,Object ** start,Object ** end)291 void ScavengeVisitor::VisitPointers(HeapObject* host, Object** start, 292 Object** end) { 293 for (Object** p = start; p < end; p++) { 294 Object* object = *p; 295 if (!Heap::InNewSpace(object)) continue; 296 scavenger_->ScavengeObject(reinterpret_cast<HeapObjectReference**>(p), 297 reinterpret_cast<HeapObject*>(object)); 298 } 299 } 300 VisitPointers(HeapObject * host,MaybeObject ** start,MaybeObject ** end)301 void ScavengeVisitor::VisitPointers(HeapObject* host, MaybeObject** start, 302 MaybeObject** end) { 303 for (MaybeObject** p = start; p < end; p++) { 304 MaybeObject* object = *p; 305 if (!Heap::InNewSpace(object)) continue; 306 // Treat the weak reference as strong. 307 HeapObject* heap_object; 308 if (object->ToStrongOrWeakHeapObject(&heap_object)) { 309 scavenger_->ScavengeObject(reinterpret_cast<HeapObjectReference**>(p), 310 heap_object); 311 } else { 312 UNREACHABLE(); 313 } 314 } 315 } 316 317 } // namespace internal 318 } // namespace v8 319 320 #endif // V8_HEAP_SCAVENGER_INL_H_ 321