heap-inl.h revision b0fe1620dcb4135ac3ab2d66ff93072373911299
1// Copyright 2006-2010 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#ifndef V8_HEAP_INL_H_
29#define V8_HEAP_INL_H_
30
31#include "heap.h"
32#include "objects.h"
33#include "v8-counters.h"
34
35namespace v8 {
36namespace internal {
37
38int Heap::MaxObjectSizeInPagedSpace() {
39  return Page::kMaxHeapObjectSize;
40}
41
42
43MaybeObject* Heap::AllocateSymbol(Vector<const char> str,
44                                  int chars,
45                                  uint32_t hash_field) {
46  unibrow::Utf8InputBuffer<> buffer(str.start(),
47                                    static_cast<unsigned>(str.length()));
48  return AllocateInternalSymbol(&buffer, chars, hash_field);
49}
50
51
52MaybeObject* Heap::CopyFixedArray(FixedArray* src) {
53  return CopyFixedArrayWithMap(src, src->map());
54}
55
56
57MaybeObject* Heap::AllocateRaw(int size_in_bytes,
58                               AllocationSpace space,
59                               AllocationSpace retry_space) {
60  ASSERT(allocation_allowed_ && gc_state_ == NOT_IN_GC);
61  ASSERT(space != NEW_SPACE ||
62         retry_space == OLD_POINTER_SPACE ||
63         retry_space == OLD_DATA_SPACE ||
64         retry_space == LO_SPACE);
65#ifdef DEBUG
66  if (FLAG_gc_interval >= 0 &&
67      !disallow_allocation_failure_ &&
68      Heap::allocation_timeout_-- <= 0) {
69    return Failure::RetryAfterGC(space);
70  }
71  Counters::objs_since_last_full.Increment();
72  Counters::objs_since_last_young.Increment();
73#endif
74  MaybeObject* result;
75  if (NEW_SPACE == space) {
76    result = new_space_.AllocateRaw(size_in_bytes);
77    if (always_allocate() && result->IsFailure()) {
78      space = retry_space;
79    } else {
80      return result;
81    }
82  }
83
84  if (OLD_POINTER_SPACE == space) {
85    result = old_pointer_space_->AllocateRaw(size_in_bytes);
86  } else if (OLD_DATA_SPACE == space) {
87    result = old_data_space_->AllocateRaw(size_in_bytes);
88  } else if (CODE_SPACE == space) {
89    result = code_space_->AllocateRaw(size_in_bytes);
90  } else if (LO_SPACE == space) {
91    result = lo_space_->AllocateRaw(size_in_bytes);
92  } else if (CELL_SPACE == space) {
93    result = cell_space_->AllocateRaw(size_in_bytes);
94  } else {
95    ASSERT(MAP_SPACE == space);
96    result = map_space_->AllocateRaw(size_in_bytes);
97  }
98  if (result->IsFailure()) old_gen_exhausted_ = true;
99  return result;
100}
101
102
103MaybeObject* Heap::NumberFromInt32(int32_t value) {
104  if (Smi::IsValid(value)) return Smi::FromInt(value);
105  // Bypass NumberFromDouble to avoid various redundant checks.
106  return AllocateHeapNumber(FastI2D(value));
107}
108
109
110MaybeObject* Heap::NumberFromUint32(uint32_t value) {
111  if ((int32_t)value >= 0 && Smi::IsValid((int32_t)value)) {
112    return Smi::FromInt((int32_t)value);
113  }
114  // Bypass NumberFromDouble to avoid various redundant checks.
115  return AllocateHeapNumber(FastUI2D(value));
116}
117
118
119void Heap::FinalizeExternalString(String* string) {
120  ASSERT(string->IsExternalString());
121  v8::String::ExternalStringResourceBase** resource_addr =
122      reinterpret_cast<v8::String::ExternalStringResourceBase**>(
123          reinterpret_cast<byte*>(string) +
124          ExternalString::kResourceOffset -
125          kHeapObjectTag);
126
127  // Dispose of the C++ object if it has not already been disposed.
128  if (*resource_addr != NULL) {
129    (*resource_addr)->Dispose();
130  }
131
132  // Clear the resource pointer in the string.
133  *resource_addr = NULL;
134}
135
136
137MaybeObject* Heap::AllocateRawMap() {
138#ifdef DEBUG
139  Counters::objs_since_last_full.Increment();
140  Counters::objs_since_last_young.Increment();
141#endif
142  MaybeObject* result = map_space_->AllocateRaw(Map::kSize);
143  if (result->IsFailure()) old_gen_exhausted_ = true;
144#ifdef DEBUG
145  if (!result->IsFailure()) {
146    // Maps have their own alignment.
147    CHECK((reinterpret_cast<intptr_t>(result) & kMapAlignmentMask) ==
148          static_cast<intptr_t>(kHeapObjectTag));
149  }
150#endif
151  return result;
152}
153
154
155MaybeObject* Heap::AllocateRawCell() {
156#ifdef DEBUG
157  Counters::objs_since_last_full.Increment();
158  Counters::objs_since_last_young.Increment();
159#endif
160  MaybeObject* result = cell_space_->AllocateRaw(JSGlobalPropertyCell::kSize);
161  if (result->IsFailure()) old_gen_exhausted_ = true;
162  return result;
163}
164
165
166bool Heap::InNewSpace(Object* object) {
167  bool result = new_space_.Contains(object);
168  ASSERT(!result ||                  // Either not in new space
169         gc_state_ != NOT_IN_GC ||   // ... or in the middle of GC
170         InToSpace(object));         // ... or in to-space (where we allocate).
171  return result;
172}
173
174
175bool Heap::InFromSpace(Object* object) {
176  return new_space_.FromSpaceContains(object);
177}
178
179
180bool Heap::InToSpace(Object* object) {
181  return new_space_.ToSpaceContains(object);
182}
183
184
185bool Heap::ShouldBePromoted(Address old_address, int object_size) {
186  // An object should be promoted if:
187  // - the object has survived a scavenge operation or
188  // - to space is already 25% full.
189  return old_address < new_space_.age_mark()
190      || (new_space_.Size() + object_size) >= (new_space_.Capacity() >> 2);
191}
192
193
194void Heap::RecordWrite(Address address, int offset) {
195  if (new_space_.Contains(address)) return;
196  ASSERT(!new_space_.FromSpaceContains(address));
197  SLOW_ASSERT(Contains(address + offset));
198  Page::FromAddress(address)->MarkRegionDirty(address + offset);
199}
200
201
202void Heap::RecordWrites(Address address, int start, int len) {
203  if (new_space_.Contains(address)) return;
204  ASSERT(!new_space_.FromSpaceContains(address));
205  Page* page = Page::FromAddress(address);
206  page->SetRegionMarks(page->GetRegionMarks() |
207      page->GetRegionMaskForSpan(address + start, len * kPointerSize));
208}
209
210
211OldSpace* Heap::TargetSpace(HeapObject* object) {
212  InstanceType type = object->map()->instance_type();
213  AllocationSpace space = TargetSpaceId(type);
214  return (space == OLD_POINTER_SPACE)
215      ? old_pointer_space_
216      : old_data_space_;
217}
218
219
220AllocationSpace Heap::TargetSpaceId(InstanceType type) {
221  // Heap numbers and sequential strings are promoted to old data space, all
222  // other object types are promoted to old pointer space.  We do not use
223  // object->IsHeapNumber() and object->IsSeqString() because we already
224  // know that object has the heap object tag.
225
226  // These objects are never allocated in new space.
227  ASSERT(type != MAP_TYPE);
228  ASSERT(type != CODE_TYPE);
229  ASSERT(type != ODDBALL_TYPE);
230  ASSERT(type != JS_GLOBAL_PROPERTY_CELL_TYPE);
231
232  if (type < FIRST_NONSTRING_TYPE) {
233    // There are three string representations: sequential strings, cons
234    // strings, and external strings.  Only cons strings contain
235    // non-map-word pointers to heap objects.
236    return ((type & kStringRepresentationMask) == kConsStringTag)
237        ? OLD_POINTER_SPACE
238        : OLD_DATA_SPACE;
239  } else {
240    return (type <= LAST_DATA_TYPE) ? OLD_DATA_SPACE : OLD_POINTER_SPACE;
241  }
242}
243
244
245void Heap::CopyBlock(Address dst, Address src, int byte_size) {
246  ASSERT(IsAligned(byte_size, kPointerSize));
247  CopyWords(reinterpret_cast<Object**>(dst),
248            reinterpret_cast<Object**>(src),
249            byte_size / kPointerSize);
250}
251
252
253void Heap::CopyBlockToOldSpaceAndUpdateRegionMarks(Address dst,
254                                                   Address src,
255                                                   int byte_size) {
256  ASSERT(IsAligned(byte_size, kPointerSize));
257
258  Page* page = Page::FromAddress(dst);
259  uint32_t marks = page->GetRegionMarks();
260
261  for (int remaining = byte_size / kPointerSize;
262       remaining > 0;
263       remaining--) {
264    Memory::Object_at(dst) = Memory::Object_at(src);
265
266    if (Heap::InNewSpace(Memory::Object_at(dst))) {
267      marks |= page->GetRegionMaskForAddress(dst);
268    }
269
270    dst += kPointerSize;
271    src += kPointerSize;
272  }
273
274  page->SetRegionMarks(marks);
275}
276
277
278void Heap::MoveBlock(Address dst, Address src, int byte_size) {
279  ASSERT(IsAligned(byte_size, kPointerSize));
280
281  int size_in_words = byte_size / kPointerSize;
282
283  if ((dst < src) || (dst >= (src + size_in_words))) {
284    ASSERT((dst >= (src + size_in_words)) ||
285           ((OffsetFrom(reinterpret_cast<Address>(src)) -
286             OffsetFrom(reinterpret_cast<Address>(dst))) >= kPointerSize));
287
288    Object** src_slot = reinterpret_cast<Object**>(src);
289    Object** dst_slot = reinterpret_cast<Object**>(dst);
290    Object** end_slot = src_slot + size_in_words;
291
292    while (src_slot != end_slot) {
293      *dst_slot++ = *src_slot++;
294    }
295  } else {
296    memmove(dst, src, byte_size);
297  }
298}
299
300
301void Heap::MoveBlockToOldSpaceAndUpdateRegionMarks(Address dst,
302                                                   Address src,
303                                                   int byte_size) {
304  ASSERT(IsAligned(byte_size, kPointerSize));
305  ASSERT((dst >= (src + byte_size)) ||
306         ((OffsetFrom(src) - OffsetFrom(dst)) >= kPointerSize));
307
308  CopyBlockToOldSpaceAndUpdateRegionMarks(dst, src, byte_size);
309}
310
311
312void Heap::ScavengeObject(HeapObject** p, HeapObject* object) {
313  ASSERT(InFromSpace(object));
314
315  // We use the first word (where the map pointer usually is) of a heap
316  // object to record the forwarding pointer.  A forwarding pointer can
317  // point to an old space, the code space, or the to space of the new
318  // generation.
319  MapWord first_word = object->map_word();
320
321  // If the first word is a forwarding address, the object has already been
322  // copied.
323  if (first_word.IsForwardingAddress()) {
324    *p = first_word.ToForwardingAddress();
325    return;
326  }
327
328  // Call the slow part of scavenge object.
329  return ScavengeObjectSlow(p, object);
330}
331
332
333bool Heap::CollectGarbage(AllocationSpace space) {
334  return CollectGarbage(space, SelectGarbageCollector(space));
335}
336
337
338MaybeObject* Heap::PrepareForCompare(String* str) {
339  // Always flatten small strings and force flattening of long strings
340  // after we have accumulated a certain amount we failed to flatten.
341  static const int kMaxAlwaysFlattenLength = 32;
342  static const int kFlattenLongThreshold = 16*KB;
343
344  const int length = str->length();
345  MaybeObject* obj = str->TryFlatten();
346  if (length <= kMaxAlwaysFlattenLength ||
347      unflattened_strings_length_ >= kFlattenLongThreshold) {
348    return obj;
349  }
350  if (obj->IsFailure()) {
351    unflattened_strings_length_ += length;
352  }
353  return str;
354}
355
356
357int Heap::AdjustAmountOfExternalAllocatedMemory(int change_in_bytes) {
358  ASSERT(HasBeenSetup());
359  int amount = amount_of_external_allocated_memory_ + change_in_bytes;
360  if (change_in_bytes >= 0) {
361    // Avoid overflow.
362    if (amount > amount_of_external_allocated_memory_) {
363      amount_of_external_allocated_memory_ = amount;
364    }
365    int amount_since_last_global_gc =
366        amount_of_external_allocated_memory_ -
367        amount_of_external_allocated_memory_at_last_global_gc_;
368    if (amount_since_last_global_gc > external_allocation_limit_) {
369      CollectAllGarbage(false);
370    }
371  } else {
372    // Avoid underflow.
373    if (amount >= 0) {
374      amount_of_external_allocated_memory_ = amount;
375    }
376  }
377  ASSERT(amount_of_external_allocated_memory_ >= 0);
378  return amount_of_external_allocated_memory_;
379}
380
381
382void Heap::SetLastScriptId(Object* last_script_id) {
383  roots_[kLastScriptIdRootIndex] = last_script_id;
384}
385
386
387#ifdef DEBUG
388#define GC_GREEDY_CHECK() \
389  if (FLAG_gc_greedy) v8::internal::Heap::GarbageCollectionGreedyCheck()
390#else
391#define GC_GREEDY_CHECK() { }
392#endif
393
394
395// Calls the FUNCTION_CALL function and retries it up to three times
396// to guarantee that any allocations performed during the call will
397// succeed if there's enough memory.
398
399// Warning: Do not use the identifiers __object__, __maybe_object__ or
400// __scope__ in a call to this macro.
401
402#define CALL_AND_RETRY(FUNCTION_CALL, RETURN_VALUE, RETURN_EMPTY)         \
403  do {                                                                    \
404    GC_GREEDY_CHECK();                                                    \
405    MaybeObject* __maybe_object__ = FUNCTION_CALL;                        \
406    Object* __object__ = NULL;                                            \
407    if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE;            \
408    if (__maybe_object__->IsOutOfMemory()) {                              \
409      v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY_0", true);\
410    }                                                                     \
411    if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY;                \
412    Heap::CollectGarbage(                                                 \
413        Failure::cast(__maybe_object__)->allocation_space());             \
414    __maybe_object__ = FUNCTION_CALL;                                     \
415    if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE;            \
416    if (__maybe_object__->IsOutOfMemory()) {                              \
417      v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY_1", true);\
418    }                                                                     \
419    if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY;                \
420    Counters::gc_last_resort_from_handles.Increment();                    \
421    Heap::CollectAllAvailableGarbage();                                   \
422    {                                                                     \
423      AlwaysAllocateScope __scope__;                                      \
424      __maybe_object__ = FUNCTION_CALL;                                   \
425    }                                                                     \
426    if (__maybe_object__->ToObject(&__object__)) RETURN_VALUE;            \
427    if (__maybe_object__->IsOutOfMemory() ||                              \
428        __maybe_object__->IsRetryAfterGC()) {                             \
429      /* TODO(1181417): Fix this. */                                      \
430      v8::internal::V8::FatalProcessOutOfMemory("CALL_AND_RETRY_2", true);\
431    }                                                                     \
432    RETURN_EMPTY;                                                         \
433  } while (false)
434
435
436#define CALL_HEAP_FUNCTION(FUNCTION_CALL, TYPE)                \
437  CALL_AND_RETRY(FUNCTION_CALL,                                \
438                 return Handle<TYPE>(TYPE::cast(__object__)),  \
439                 return Handle<TYPE>())
440
441
442#define CALL_HEAP_FUNCTION_VOID(FUNCTION_CALL) \
443  CALL_AND_RETRY(FUNCTION_CALL, return, return)
444
445
446#ifdef DEBUG
447
448inline bool Heap::allow_allocation(bool new_state) {
449  bool old = allocation_allowed_;
450  allocation_allowed_ = new_state;
451  return old;
452}
453
454#endif
455
456
457void ExternalStringTable::AddString(String* string) {
458  ASSERT(string->IsExternalString());
459  if (Heap::InNewSpace(string)) {
460    new_space_strings_.Add(string);
461  } else {
462    old_space_strings_.Add(string);
463  }
464}
465
466
467void ExternalStringTable::Iterate(ObjectVisitor* v) {
468  if (!new_space_strings_.is_empty()) {
469    Object** start = &new_space_strings_[0];
470    v->VisitPointers(start, start + new_space_strings_.length());
471  }
472  if (!old_space_strings_.is_empty()) {
473    Object** start = &old_space_strings_[0];
474    v->VisitPointers(start, start + old_space_strings_.length());
475  }
476}
477
478
479// Verify() is inline to avoid ifdef-s around its calls in release
480// mode.
481void ExternalStringTable::Verify() {
482#ifdef DEBUG
483  for (int i = 0; i < new_space_strings_.length(); ++i) {
484    ASSERT(Heap::InNewSpace(new_space_strings_[i]));
485    ASSERT(new_space_strings_[i] != Heap::raw_unchecked_null_value());
486  }
487  for (int i = 0; i < old_space_strings_.length(); ++i) {
488    ASSERT(!Heap::InNewSpace(old_space_strings_[i]));
489    ASSERT(old_space_strings_[i] != Heap::raw_unchecked_null_value());
490  }
491#endif
492}
493
494
495void ExternalStringTable::AddOldString(String* string) {
496  ASSERT(string->IsExternalString());
497  ASSERT(!Heap::InNewSpace(string));
498  old_space_strings_.Add(string);
499}
500
501
502void ExternalStringTable::ShrinkNewStrings(int position) {
503  new_space_strings_.Rewind(position);
504  Verify();
505}
506
507} }  // namespace v8::internal
508
509#endif  // V8_HEAP_INL_H_
510