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