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