Home | History | Annotate | Download | only in src
      1 // Copyright 2006-2008 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 #include "v8.h"
     29 
     30 #include "zone-inl.h"
     31 
     32 namespace v8 {
     33 namespace internal {
     34 
     35 
     36 Address Zone::position_ = 0;
     37 Address Zone::limit_ = 0;
     38 int Zone::zone_excess_limit_ = 256 * MB;
     39 int Zone::segment_bytes_allocated_ = 0;
     40 
     41 bool AssertNoZoneAllocation::allow_allocation_ = true;
     42 
     43 int ZoneScope::nesting_ = 0;
     44 
     45 // Segments represent chunks of memory: They have starting address
     46 // (encoded in the this pointer) and a size in bytes. Segments are
     47 // chained together forming a LIFO structure with the newest segment
     48 // available as Segment::head(). Segments are allocated using malloc()
     49 // and de-allocated using free().
     50 
     51 class Segment {
     52  public:
     53   Segment* next() const { return next_; }
     54   void clear_next() { next_ = NULL; }
     55 
     56   int size() const { return size_; }
     57   int capacity() const { return size_ - sizeof(Segment); }
     58 
     59   Address start() const { return address(sizeof(Segment)); }
     60   Address end() const { return address(size_); }
     61 
     62   static Segment* head() { return head_; }
     63   static void set_head(Segment* head) { head_ = head; }
     64 
     65   // Creates a new segment, sets it size, and pushes it to the front
     66   // of the segment chain. Returns the new segment.
     67   static Segment* New(int size) {
     68     Segment* result = reinterpret_cast<Segment*>(Malloced::New(size));
     69     Zone::adjust_segment_bytes_allocated(size);
     70     if (result != NULL) {
     71       result->next_ = head_;
     72       result->size_ = size;
     73       head_ = result;
     74     }
     75     return result;
     76   }
     77 
     78   // Deletes the given segment. Does not touch the segment chain.
     79   static void Delete(Segment* segment, int size) {
     80     Zone::adjust_segment_bytes_allocated(-size);
     81     Malloced::Delete(segment);
     82   }
     83 
     84   static int bytes_allocated() { return bytes_allocated_; }
     85 
     86  private:
     87   // Computes the address of the nth byte in this segment.
     88   Address address(int n) const {
     89     return Address(this) + n;
     90   }
     91 
     92   static Segment* head_;
     93   static int bytes_allocated_;
     94   Segment* next_;
     95   int size_;
     96 };
     97 
     98 
     99 Segment* Segment::head_ = NULL;
    100 int Segment::bytes_allocated_ = 0;
    101 
    102 
    103 void Zone::DeleteAll() {
    104 #ifdef DEBUG
    105   // Constant byte value used for zapping dead memory in debug mode.
    106   static const unsigned char kZapDeadByte = 0xcd;
    107 #endif
    108 
    109   // Find a segment with a suitable size to keep around.
    110   Segment* keep = Segment::head();
    111   while (keep != NULL && keep->size() > kMaximumKeptSegmentSize) {
    112     keep = keep->next();
    113   }
    114 
    115   // Traverse the chained list of segments, zapping (in debug mode)
    116   // and freeing every segment except the one we wish to keep.
    117   Segment* current = Segment::head();
    118   while (current != NULL) {
    119     Segment* next = current->next();
    120     if (current == keep) {
    121       // Unlink the segment we wish to keep from the list.
    122       current->clear_next();
    123     } else {
    124       int size = current->size();
    125 #ifdef DEBUG
    126       // Zap the entire current segment (including the header).
    127       memset(current, kZapDeadByte, size);
    128 #endif
    129       Segment::Delete(current, size);
    130     }
    131     current = next;
    132   }
    133 
    134   // If we have found a segment we want to keep, we must recompute the
    135   // variables 'position' and 'limit' to prepare for future allocate
    136   // attempts. Otherwise, we must clear the position and limit to
    137   // force a new segment to be allocated on demand.
    138   if (keep != NULL) {
    139     Address start = keep->start();
    140     position_ = RoundUp(start, kAlignment);
    141     limit_ = keep->end();
    142 #ifdef DEBUG
    143     // Zap the contents of the kept segment (but not the header).
    144     memset(start, kZapDeadByte, keep->capacity());
    145 #endif
    146   } else {
    147     position_ = limit_ = 0;
    148   }
    149 
    150   // Update the head segment to be the kept segment (if any).
    151   Segment::set_head(keep);
    152 }
    153 
    154 
    155 Address Zone::NewExpand(int size) {
    156   // Make sure the requested size is already properly aligned and that
    157   // there isn't enough room in the Zone to satisfy the request.
    158   ASSERT(size == RoundDown(size, kAlignment));
    159   ASSERT(position_ + size > limit_);
    160 
    161   // Compute the new segment size. We use a 'high water mark'
    162   // strategy, where we increase the segment size every time we expand
    163   // except that we employ a maximum segment size when we delete. This
    164   // is to avoid excessive malloc() and free() overhead.
    165   Segment* head = Segment::head();
    166   int old_size = (head == NULL) ? 0 : head->size();
    167   static const int kSegmentOverhead = sizeof(Segment) + kAlignment;
    168   int new_size = kSegmentOverhead + size + (old_size << 1);
    169   if (new_size < kMinimumSegmentSize) {
    170     new_size = kMinimumSegmentSize;
    171   } else if (new_size > kMaximumSegmentSize) {
    172     // Limit the size of new segments to avoid growing the segment size
    173     // exponentially, thus putting pressure on contiguous virtual address space.
    174     // All the while making sure to allocate a segment large enough to hold the
    175     // requested size.
    176     new_size = Max(kSegmentOverhead + size, kMaximumSegmentSize);
    177   }
    178   Segment* segment = Segment::New(new_size);
    179   if (segment == NULL) {
    180     V8::FatalProcessOutOfMemory("Zone");
    181     return NULL;
    182   }
    183 
    184   // Recompute 'top' and 'limit' based on the new segment.
    185   Address result = RoundUp(segment->start(), kAlignment);
    186   position_ = result + size;
    187   limit_ = segment->end();
    188   ASSERT(position_ <= limit_);
    189   return result;
    190 }
    191 
    192 
    193 } }  // namespace v8::internal
    194