• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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