OLD | NEW |
1 // Copyright (c) 2008, Google Inc. | 1 // Copyright (c) 2008, Google Inc. |
2 // All rights reserved. | 2 // All rights reserved. |
3 // | 3 // |
4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
6 // met: | 6 // met: |
7 // | 7 // |
8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
93 Span* PageHeap::New(Length n) { | 93 Span* PageHeap::New(Length n) { |
94 ASSERT(Check()); | 94 ASSERT(Check()); |
95 ASSERT(n > 0); | 95 ASSERT(n > 0); |
96 | 96 |
97 Span* result = SearchFreeAndLargeLists(n); | 97 Span* result = SearchFreeAndLargeLists(n); |
98 if (result != NULL) | 98 if (result != NULL) |
99 return result; | 99 return result; |
100 | 100 |
101 // Grow the heap and try again. | 101 // Grow the heap and try again. |
102 if (!GrowHeap(n)) { | 102 if (!GrowHeap(n)) { |
| 103 ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes); |
103 ASSERT(Check()); | 104 ASSERT(Check()); |
104 return NULL; | 105 return NULL; |
105 } | 106 } |
106 return SearchFreeAndLargeLists(n); | 107 return SearchFreeAndLargeLists(n); |
107 } | 108 } |
108 | 109 |
109 Span* PageHeap::AllocLarge(Length n) { | 110 Span* PageHeap::AllocLarge(Length n) { |
110 // find the best span (closest to n in size). | 111 // find the best span (closest to n in size). |
111 // The following loops implements address-ordered best-fit. | 112 // The following loops implements address-ordered best-fit. |
112 Span *best = NULL; | 113 Span *best = NULL; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 Span* leftover = NewSpan(span->start + n, extra); | 154 Span* leftover = NewSpan(span->start + n, extra); |
154 ASSERT(leftover->location == Span::IN_USE); | 155 ASSERT(leftover->location == Span::IN_USE); |
155 Event(leftover, 'U', extra); | 156 Event(leftover, 'U', extra); |
156 RecordSpan(leftover); | 157 RecordSpan(leftover); |
157 pagemap_.set(span->start + n - 1, span); // Update map from pageid to span | 158 pagemap_.set(span->start + n - 1, span); // Update map from pageid to span |
158 span->length = n; | 159 span->length = n; |
159 | 160 |
160 return leftover; | 161 return leftover; |
161 } | 162 } |
162 | 163 |
| 164 void PageHeap::CommitSpan(Span* span) { |
| 165 TCMalloc_SystemCommit(reinterpret_cast<void*>(span->start << kPageShift), |
| 166 static_cast<size_t>(span->length << kPageShift)); |
| 167 stats_.committed_bytes += span->length << kPageShift; |
| 168 } |
| 169 |
| 170 void PageHeap::DecommitSpan(Span* span) { |
| 171 TCMalloc_SystemRelease(reinterpret_cast<void*>(span->start << kPageShift), |
| 172 static_cast<size_t>(span->length << kPageShift)); |
| 173 stats_.committed_bytes -= span->length << kPageShift; |
| 174 } |
| 175 |
163 Span* PageHeap::Carve(Span* span, Length n) { | 176 Span* PageHeap::Carve(Span* span, Length n) { |
164 ASSERT(n > 0); | 177 ASSERT(n > 0); |
165 ASSERT(span->location != Span::IN_USE); | 178 ASSERT(span->location != Span::IN_USE); |
166 const int old_location = span->location; | 179 const int old_location = span->location; |
167 RemoveFromFreeList(span); | 180 RemoveFromFreeList(span); |
168 span->location = Span::IN_USE; | 181 span->location = Span::IN_USE; |
169 Event(span, 'A', n); | 182 Event(span, 'A', n); |
170 | 183 |
171 const int extra = span->length - n; | 184 const int extra = span->length - n; |
172 ASSERT(extra >= 0); | 185 ASSERT(extra >= 0); |
173 if (extra > 0) { | 186 if (extra > 0) { |
174 Span* leftover = NewSpan(span->start + n, extra); | 187 Span* leftover = NewSpan(span->start + n, extra); |
175 leftover->location = old_location; | 188 leftover->location = old_location; |
176 Event(leftover, 'S', extra); | 189 Event(leftover, 'S', extra); |
177 RecordSpan(leftover); | 190 RecordSpan(leftover); |
| 191 |
| 192 // The previous span of |leftover| was just splitted -- no need to |
| 193 // coalesce them. The next span of |leftover| was not previously coalesced |
| 194 // with |span|, i.e. is NULL or has got location other than |old_location|. |
| 195 const PageID p = leftover->start; |
| 196 const Length len = leftover->length; |
| 197 Span* next = GetDescriptor(p+len); |
| 198 ASSERT (next == NULL || |
| 199 next->location == Span::IN_USE || |
| 200 next->location != leftover->location); |
| 201 |
178 PrependToFreeList(leftover); // Skip coalescing - no candidates possible | 202 PrependToFreeList(leftover); // Skip coalescing - no candidates possible |
179 span->length = n; | 203 span->length = n; |
180 pagemap_.set(span->start + n - 1, span); | 204 pagemap_.set(span->start + n - 1, span); |
181 } | 205 } |
182 ASSERT(Check()); | 206 ASSERT(Check()); |
| 207 if (old_location == Span::ON_RETURNED_FREELIST) { |
| 208 // We need to recommit this address space. |
| 209 CommitSpan(span); |
| 210 } |
| 211 ASSERT(span->location == Span::IN_USE); |
| 212 ASSERT(span->length == n); |
| 213 ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes); |
183 return span; | 214 return span; |
184 } | 215 } |
185 | 216 |
186 void PageHeap::Delete(Span* span) { | 217 void PageHeap::Delete(Span* span) { |
187 ASSERT(Check()); | 218 ASSERT(Check()); |
188 ASSERT(span->location == Span::IN_USE); | 219 ASSERT(span->location == Span::IN_USE); |
189 ASSERT(span->length > 0); | 220 ASSERT(span->length > 0); |
190 ASSERT(GetDescriptor(span->start) == span); | 221 ASSERT(GetDescriptor(span->start) == span); |
191 ASSERT(GetDescriptor(span->start + span->length - 1) == span); | 222 ASSERT(GetDescriptor(span->start + span->length - 1) == span); |
192 const Length n = span->length; | 223 const Length n = span->length; |
193 span->sizeclass = 0; | 224 span->sizeclass = 0; |
194 span->sample = 0; | 225 span->sample = 0; |
195 span->location = Span::ON_NORMAL_FREELIST; | 226 span->location = Span::ON_NORMAL_FREELIST; |
196 Event(span, 'D', span->length); | 227 Event(span, 'D', span->length); |
197 MergeIntoFreeList(span); // Coalesces if possible | 228 MergeIntoFreeList(span); // Coalesces if possible |
198 IncrementalScavenge(n); | 229 IncrementalScavenge(n); |
| 230 ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes); |
199 ASSERT(Check()); | 231 ASSERT(Check()); |
200 } | 232 } |
201 | 233 |
202 void PageHeap::MergeIntoFreeList(Span* span) { | 234 void PageHeap::MergeIntoFreeList(Span* span) { |
203 ASSERT(span->location != Span::IN_USE); | 235 ASSERT(span->location != Span::IN_USE); |
204 | 236 |
205 // Coalesce -- we guarantee that "p" != 0, so no bounds checking | 237 // Coalesce -- we guarantee that "p" != 0, so no bounds checking |
206 // necessary. We do not bother resetting the stale pagemap | 238 // necessary. We do not bother resetting the stale pagemap |
207 // entries for the pieces we are merging together because we only | 239 // entries for the pieces we are merging together because we only |
208 // care about the pagemap entries for the boundaries. | 240 // care about the pagemap entries for the boundaries. |
209 // | 241 // |
210 // Note that only similar spans are merged together. For example, | 242 // Note that the adjacent spans we merge into "span" may come out of a |
211 // we do not coalesce "returned" spans with "normal" spans. | 243 // "normal" (committed) list, and cleanly merge with our IN_USE span, which |
| 244 // is implicitly committed. If the adjacents spans are on the "returned" |
| 245 // (decommitted) list, then we must get both spans into the same state before |
| 246 // or after we coalesce them. The current code always decomits. This is |
| 247 // achieved by blindly decommitting the entire coalesced region, which may |
| 248 // include any combination of committed and decommitted spans, at the end of |
| 249 // the method. |
| 250 |
| 251 // TODO(jar): "Always decommit" causes some extra calls to commit when we are |
| 252 // called in GrowHeap() during an allocation :-/. We need to eval the cost of |
| 253 // that oscillation, and possibly do something to reduce it. |
| 254 |
| 255 // TODO(jar): We need a better strategy for deciding to commit, or decommit, |
| 256 // based on memory usage and free heap sizes. |
| 257 |
212 const PageID p = span->start; | 258 const PageID p = span->start; |
213 const Length n = span->length; | 259 const Length n = span->length; |
214 Span* prev = GetDescriptor(p-1); | 260 Span* prev = GetDescriptor(p-1); |
215 if (prev != NULL && prev->location == span->location) { | 261 if (prev != NULL && prev->location != Span::IN_USE) { |
216 // Merge preceding span into this span | 262 // Merge preceding span into this span |
217 ASSERT(prev->start + prev->length == p); | 263 ASSERT(prev->start + prev->length == p); |
218 const Length len = prev->length; | 264 const Length len = prev->length; |
| 265 if (prev->location == Span::ON_RETURNED_FREELIST) { |
| 266 // We're about to put the merge span into the returned freelist and call |
| 267 // DecommitSpan() on it, which will mark the entire span including this |
| 268 // one as released and decrease stats_.committed_bytes by the size of the |
| 269 // merged span. To make the math work out we temporarily increase the |
| 270 // stats_.committed_bytes amount. |
| 271 stats_.committed_bytes += prev->length << kPageShift; |
| 272 } |
219 RemoveFromFreeList(prev); | 273 RemoveFromFreeList(prev); |
220 DeleteSpan(prev); | 274 DeleteSpan(prev); |
221 span->start -= len; | 275 span->start -= len; |
222 span->length += len; | 276 span->length += len; |
223 pagemap_.set(span->start, span); | 277 pagemap_.set(span->start, span); |
224 Event(span, 'L', len); | 278 Event(span, 'L', len); |
225 } | 279 } |
226 Span* next = GetDescriptor(p+n); | 280 Span* next = GetDescriptor(p+n); |
227 if (next != NULL && next->location == span->location) { | 281 if (next != NULL && next->location != Span::IN_USE) { |
228 // Merge next span into this span | 282 // Merge next span into this span |
229 ASSERT(next->start == p+n); | 283 ASSERT(next->start == p+n); |
230 const Length len = next->length; | 284 const Length len = next->length; |
| 285 if (next->location == Span::ON_RETURNED_FREELIST) { |
| 286 // See the comment below 'if (prev->location ...' for explanation. |
| 287 stats_.committed_bytes += next->length << kPageShift; |
| 288 } |
231 RemoveFromFreeList(next); | 289 RemoveFromFreeList(next); |
232 DeleteSpan(next); | 290 DeleteSpan(next); |
233 span->length += len; | 291 span->length += len; |
234 pagemap_.set(span->start + span->length - 1, span); | 292 pagemap_.set(span->start + span->length - 1, span); |
235 Event(span, 'R', len); | 293 Event(span, 'R', len); |
236 } | 294 } |
237 | 295 |
| 296 Event(span, 'D', span->length); |
| 297 span->location = Span::ON_RETURNED_FREELIST; |
| 298 DecommitSpan(span); |
238 PrependToFreeList(span); | 299 PrependToFreeList(span); |
239 } | 300 } |
240 | 301 |
241 void PageHeap::PrependToFreeList(Span* span) { | 302 void PageHeap::PrependToFreeList(Span* span) { |
242 ASSERT(span->location != Span::IN_USE); | 303 ASSERT(span->location != Span::IN_USE); |
243 SpanList* list = (span->length < kMaxPages) ? &free_[span->length] : &large_; | 304 SpanList* list = (span->length < kMaxPages) ? &free_[span->length] : &large_; |
244 if (span->location == Span::ON_NORMAL_FREELIST) { | 305 if (span->location == Span::ON_NORMAL_FREELIST) { |
245 stats_.free_bytes += (span->length << kPageShift); | 306 stats_.free_bytes += (span->length << kPageShift); |
246 DLL_Prepend(&list->normal, span); | 307 DLL_Prepend(&list->normal, span); |
247 } else { | 308 } else { |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
414 ask = n; | 475 ask = n; |
415 ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize); | 476 ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize); |
416 } | 477 } |
417 if (ptr == NULL) return false; | 478 if (ptr == NULL) return false; |
418 } | 479 } |
419 ask = actual_size >> kPageShift; | 480 ask = actual_size >> kPageShift; |
420 RecordGrowth(ask << kPageShift); | 481 RecordGrowth(ask << kPageShift); |
421 | 482 |
422 uint64_t old_system_bytes = stats_.system_bytes; | 483 uint64_t old_system_bytes = stats_.system_bytes; |
423 stats_.system_bytes += (ask << kPageShift); | 484 stats_.system_bytes += (ask << kPageShift); |
| 485 stats_.committed_bytes += (ask << kPageShift); |
424 const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; | 486 const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift; |
425 ASSERT(p > 0); | 487 ASSERT(p > 0); |
426 | 488 |
427 // If we have already a lot of pages allocated, just pre allocate a bunch of | 489 // If we have already a lot of pages allocated, just pre allocate a bunch of |
428 // memory for the page map. This prevents fragmentation by pagemap metadata | 490 // memory for the page map. This prevents fragmentation by pagemap metadata |
429 // when a program keeps allocating and freeing large blocks. | 491 // when a program keeps allocating and freeing large blocks. |
430 | 492 |
431 if (old_system_bytes < kPageMapBigAllocationThreshold | 493 if (old_system_bytes < kPageMapBigAllocationThreshold |
432 && stats_.system_bytes >= kPageMapBigAllocationThreshold) { | 494 && stats_.system_bytes >= kPageMapBigAllocationThreshold) { |
433 pagemap_.PreallocateMoreMemory(); | 495 pagemap_.PreallocateMoreMemory(); |
434 } | 496 } |
435 | 497 |
436 // Make sure pagemap_ has entries for all of the new pages. | 498 // Make sure pagemap_ has entries for all of the new pages. |
437 // Plus ensure one before and one after so coalescing code | 499 // Plus ensure one before and one after so coalescing code |
438 // does not need bounds-checking. | 500 // does not need bounds-checking. |
439 if (pagemap_.Ensure(p-1, ask+2)) { | 501 if (pagemap_.Ensure(p-1, ask+2)) { |
440 // Pretend the new area is allocated and then Delete() it to cause | 502 // Pretend the new area is allocated and then Delete() it to cause |
441 // any necessary coalescing to occur. | 503 // any necessary coalescing to occur. |
442 Span* span = NewSpan(p, ask); | 504 Span* span = NewSpan(p, ask); |
443 RecordSpan(span); | 505 RecordSpan(span); |
444 Delete(span); | 506 Delete(span); |
| 507 ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes); |
445 ASSERT(Check()); | 508 ASSERT(Check()); |
446 return true; | 509 return true; |
447 } else { | 510 } else { |
448 // We could not allocate memory within "pagemap_" | 511 // We could not allocate memory within "pagemap_" |
449 // TODO: Once we can return memory to the system, return the new span | 512 // TODO: Once we can return memory to the system, return the new span |
450 return false; | 513 return false; |
451 } | 514 } |
452 } | 515 } |
453 | 516 |
454 bool PageHeap::Check() { | 517 bool PageHeap::Check() { |
(...skipping 19 matching lines...) Expand all Loading... |
474 CHECK_CONDITION(s->location == freelist); // NORMAL or RETURNED | 537 CHECK_CONDITION(s->location == freelist); // NORMAL or RETURNED |
475 CHECK_CONDITION(s->length >= min_pages); | 538 CHECK_CONDITION(s->length >= min_pages); |
476 CHECK_CONDITION(s->length <= max_pages); | 539 CHECK_CONDITION(s->length <= max_pages); |
477 CHECK_CONDITION(GetDescriptor(s->start) == s); | 540 CHECK_CONDITION(GetDescriptor(s->start) == s); |
478 CHECK_CONDITION(GetDescriptor(s->start+s->length-1) == s); | 541 CHECK_CONDITION(GetDescriptor(s->start+s->length-1) == s); |
479 } | 542 } |
480 return true; | 543 return true; |
481 } | 544 } |
482 | 545 |
483 } // namespace tcmalloc | 546 } // namespace tcmalloc |
OLD | NEW |