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