Home | History | Annotate | Download | only in heap
      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