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