blob: f52ae2af029e2174f00c58e04c1af9e954c31c0e [file] [log] [blame]
Austin Schuh745610d2015-09-06 18:19:50 -07001// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
2// Copyright (c) 2008, Google Inc.
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are
7// met:
8//
9// * Redistributions of source code must retain the above copyright
10// notice, this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above
12// copyright notice, this list of conditions and the following disclaimer
13// in the documentation and/or other materials provided with the
14// distribution.
15// * Neither the name of Google Inc. nor the names of its
16// contributors may be used to endorse or promote products derived from
17// this software without specific prior written permission.
18//
19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31// ---
32// Author: Sanjay Ghemawat <opensource@google.com>
33
34#include <config.h>
35#ifdef HAVE_INTTYPES_H
36#include <inttypes.h> // for PRIuPTR
37#endif
38#include <errno.h> // for ENOMEM, errno
39#include <gperftools/malloc_extension.h> // for MallocRange, etc
40#include "base/basictypes.h"
41#include "base/commandlineflags.h"
42#include "internal_logging.h" // for ASSERT, TCMalloc_Printer, etc
43#include "page_heap_allocator.h" // for PageHeapAllocator
44#include "static_vars.h" // for Static
45#include "system-alloc.h" // for TCMalloc_SystemAlloc, etc
46
47DEFINE_double(tcmalloc_release_rate,
48 EnvToDouble("TCMALLOC_RELEASE_RATE", 1.0),
49 "Rate at which we release unused memory to the system. "
50 "Zero means we never release memory back to the system. "
51 "Increase this flag to return memory faster; decrease it "
52 "to return memory slower. Reasonable rates are in the "
53 "range [0,10]");
54
55DEFINE_int64(tcmalloc_heap_limit_mb,
56 EnvToInt("TCMALLOC_HEAP_LIMIT_MB", 0),
57 "Limit total size of the process heap to the "
58 "specified number of MiB. "
59 "When we approach the limit the memory is released "
60 "to the system more aggressively (more minor page faults). "
61 "Zero means to allocate as long as system allows.");
62
63namespace tcmalloc {
64
65PageHeap::PageHeap()
66 : pagemap_(MetaDataAlloc),
67 pagemap_cache_(0),
68 scavenge_counter_(0),
69 // Start scavenging at kMaxPages list
70 release_index_(kMaxPages),
71 aggressive_decommit_(false) {
72 COMPILE_ASSERT(kNumClasses <= (1 << PageMapCache::kValuebits), valuebits);
73 DLL_Init(&large_.normal);
74 DLL_Init(&large_.returned);
75 for (int i = 0; i < kMaxPages; i++) {
76 DLL_Init(&free_[i].normal);
77 DLL_Init(&free_[i].returned);
78 }
79}
80
81Span* PageHeap::SearchFreeAndLargeLists(Length n) {
82 ASSERT(Check());
83 ASSERT(n > 0);
84
85 // Find first size >= n that has a non-empty list
86 for (Length s = n; s < kMaxPages; s++) {
87 Span* ll = &free_[s].normal;
88 // If we're lucky, ll is non-empty, meaning it has a suitable span.
89 if (!DLL_IsEmpty(ll)) {
90 ASSERT(ll->next->location == Span::ON_NORMAL_FREELIST);
91 return Carve(ll->next, n);
92 }
93 // Alternatively, maybe there's a usable returned span.
94 ll = &free_[s].returned;
95 if (!DLL_IsEmpty(ll)) {
96 // We did not call EnsureLimit before, to avoid releasing the span
97 // that will be taken immediately back.
98 // Calling EnsureLimit here is not very expensive, as it fails only if
99 // there is no more normal spans (and it fails efficiently)
100 // or SystemRelease does not work (there is probably no returned spans).
101 if (EnsureLimit(n)) {
102 // ll may have became empty due to coalescing
103 if (!DLL_IsEmpty(ll)) {
104 ASSERT(ll->next->location == Span::ON_RETURNED_FREELIST);
105 return Carve(ll->next, n);
106 }
107 }
108 }
109 }
110 // No luck in free lists, our last chance is in a larger class.
111 return AllocLarge(n); // May be NULL
112}
113
114static const size_t kForcedCoalesceInterval = 128*1024*1024;
115
116Span* PageHeap::New(Length n) {
117 ASSERT(Check());
118 ASSERT(n > 0);
119
120 Span* result = SearchFreeAndLargeLists(n);
121 if (result != NULL)
122 return result;
123
124 if (stats_.free_bytes != 0 && stats_.unmapped_bytes != 0
125 && stats_.free_bytes + stats_.unmapped_bytes >= stats_.system_bytes / 4
126 && (stats_.system_bytes / kForcedCoalesceInterval
127 != (stats_.system_bytes + (n << kPageShift)) / kForcedCoalesceInterval)) {
128 // We're about to grow heap, but there are lots of free pages.
129 // tcmalloc's design decision to keep unmapped and free spans
130 // separately and never coalesce them means that sometimes there
131 // can be free pages span of sufficient size, but it consists of
132 // "segments" of different type so page heap search cannot find
133 // it. In order to prevent growing heap and wasting memory in such
134 // case we're going to unmap all free pages. So that all free
135 // spans are maximally coalesced.
136 //
137 // We're also limiting 'rate' of going into this path to be at
138 // most once per 128 megs of heap growth. Otherwise programs that
139 // grow heap frequently (and that means by small amount) could be
140 // penalized with higher count of minor page faults.
141 //
142 // See also large_heap_fragmentation_unittest.cc and
143 // https://code.google.com/p/gperftools/issues/detail?id=368
144 ReleaseAtLeastNPages(static_cast<Length>(0x7fffffff));
145
146 // then try again. If we are forced to grow heap because of large
147 // spans fragmentation and not because of problem described above,
148 // then at the very least we've just unmapped free but
149 // insufficiently big large spans back to OS. So in case of really
150 // unlucky memory fragmentation we'll be consuming virtual address
151 // space, but not real memory
152 result = SearchFreeAndLargeLists(n);
153 if (result != NULL) return result;
154 }
155
156 // Grow the heap and try again.
157 if (!GrowHeap(n)) {
158 ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes);
159 ASSERT(Check());
160 // underlying SysAllocator likely set ENOMEM but we can get here
161 // due to EnsureLimit so we set it here too.
162 //
163 // Setting errno to ENOMEM here allows us to avoid dealing with it
164 // in fast-path.
165 errno = ENOMEM;
166 return NULL;
167 }
168 return SearchFreeAndLargeLists(n);
169}
170
171Span* PageHeap::AllocLarge(Length n) {
172 // find the best span (closest to n in size).
173 // The following loops implements address-ordered best-fit.
174 Span *best = NULL;
175
176 // Search through normal list
177 for (Span* span = large_.normal.next;
178 span != &large_.normal;
179 span = span->next) {
180 if (span->length >= n) {
181 if ((best == NULL)
182 || (span->length < best->length)
183 || ((span->length == best->length) && (span->start < best->start))) {
184 best = span;
185 ASSERT(best->location == Span::ON_NORMAL_FREELIST);
186 }
187 }
188 }
189
190 Span *bestNormal = best;
191
192 // Search through released list in case it has a better fit
193 for (Span* span = large_.returned.next;
194 span != &large_.returned;
195 span = span->next) {
196 if (span->length >= n) {
197 if ((best == NULL)
198 || (span->length < best->length)
199 || ((span->length == best->length) && (span->start < best->start))) {
200 best = span;
201 ASSERT(best->location == Span::ON_RETURNED_FREELIST);
202 }
203 }
204 }
205
206 if (best == bestNormal) {
207 return best == NULL ? NULL : Carve(best, n);
208 }
209
210 // best comes from returned list.
211
212 if (EnsureLimit(n, false)) {
213 return Carve(best, n);
214 }
215
216 if (EnsureLimit(n, true)) {
217 // best could have been destroyed by coalescing.
218 // bestNormal is not a best-fit, and it could be destroyed as well.
219 // We retry, the limit is already ensured:
220 return AllocLarge(n);
221 }
222
223 // If bestNormal existed, EnsureLimit would succeeded:
224 ASSERT(bestNormal == NULL);
225 // We are not allowed to take best from returned list.
226 return NULL;
227}
228
229Span* PageHeap::Split(Span* span, Length n) {
230 ASSERT(0 < n);
231 ASSERT(n < span->length);
232 ASSERT(span->location == Span::IN_USE);
233 ASSERT(span->sizeclass == 0);
234 Event(span, 'T', n);
235
236 const int extra = span->length - n;
237 Span* leftover = NewSpan(span->start + n, extra);
238 ASSERT(leftover->location == Span::IN_USE);
239 Event(leftover, 'U', extra);
240 RecordSpan(leftover);
241 pagemap_.set(span->start + n - 1, span); // Update map from pageid to span
242 span->length = n;
243
244 return leftover;
245}
246
247void PageHeap::CommitSpan(Span* span) {
248 TCMalloc_SystemCommit(reinterpret_cast<void*>(span->start << kPageShift),
249 static_cast<size_t>(span->length << kPageShift));
250 stats_.committed_bytes += span->length << kPageShift;
251}
252
253bool PageHeap::DecommitSpan(Span* span) {
254 bool rv = TCMalloc_SystemRelease(reinterpret_cast<void*>(span->start << kPageShift),
255 static_cast<size_t>(span->length << kPageShift));
256 if (rv) {
257 stats_.committed_bytes -= span->length << kPageShift;
258 }
259
260 return rv;
261}
262
263Span* PageHeap::Carve(Span* span, Length n) {
264 ASSERT(n > 0);
265 ASSERT(span->location != Span::IN_USE);
266 const int old_location = span->location;
267 RemoveFromFreeList(span);
268 span->location = Span::IN_USE;
269 Event(span, 'A', n);
270
271 const int extra = span->length - n;
272 ASSERT(extra >= 0);
273 if (extra > 0) {
274 Span* leftover = NewSpan(span->start + n, extra);
275 leftover->location = old_location;
276 Event(leftover, 'S', extra);
277 RecordSpan(leftover);
278
279 // The previous span of |leftover| was just splitted -- no need to
280 // coalesce them. The next span of |leftover| was not previously coalesced
281 // with |span|, i.e. is NULL or has got location other than |old_location|.
282#ifndef NDEBUG
283 const PageID p = leftover->start;
284 const Length len = leftover->length;
285 Span* next = GetDescriptor(p+len);
286 ASSERT (next == NULL ||
287 next->location == Span::IN_USE ||
288 next->location != leftover->location);
289#endif
290
291 PrependToFreeList(leftover); // Skip coalescing - no candidates possible
292 span->length = n;
293 pagemap_.set(span->start + n - 1, span);
294 }
295 ASSERT(Check());
296 if (old_location == Span::ON_RETURNED_FREELIST) {
297 // We need to recommit this address space.
298 CommitSpan(span);
299 }
300 ASSERT(span->location == Span::IN_USE);
301 ASSERT(span->length == n);
302 ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes);
303 return span;
304}
305
306void PageHeap::Delete(Span* span) {
307 ASSERT(Check());
308 ASSERT(span->location == Span::IN_USE);
309 ASSERT(span->length > 0);
310 ASSERT(GetDescriptor(span->start) == span);
311 ASSERT(GetDescriptor(span->start + span->length - 1) == span);
312 const Length n = span->length;
313 span->sizeclass = 0;
314 span->sample = 0;
315 span->location = Span::ON_NORMAL_FREELIST;
316 Event(span, 'D', span->length);
317 MergeIntoFreeList(span); // Coalesces if possible
318 IncrementalScavenge(n);
319 ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes);
320 ASSERT(Check());
321}
322
323bool PageHeap::MayMergeSpans(Span *span, Span *other) {
324 if (aggressive_decommit_) {
325 return other->location != Span::IN_USE;
326 }
327 return span->location == other->location;
328}
329
330void PageHeap::MergeIntoFreeList(Span* span) {
331 ASSERT(span->location != Span::IN_USE);
332
333 // Coalesce -- we guarantee that "p" != 0, so no bounds checking
334 // necessary. We do not bother resetting the stale pagemap
335 // entries for the pieces we are merging together because we only
336 // care about the pagemap entries for the boundaries.
337 //
338 // Note: depending on aggressive_decommit_ mode we allow only
339 // similar spans to be coalesced.
340 //
341 // The following applies if aggressive_decommit_ is enabled:
342 //
343 // Note that the adjacent spans we merge into "span" may come out of a
344 // "normal" (committed) list, and cleanly merge with our IN_USE span, which
345 // is implicitly committed. If the adjacents spans are on the "returned"
346 // (decommitted) list, then we must get both spans into the same state before
347 // or after we coalesce them. The current code always decomits. This is
348 // achieved by blindly decommitting the entire coalesced region, which may
349 // include any combination of committed and decommitted spans, at the end of
350 // the method.
351
352 // TODO(jar): "Always decommit" causes some extra calls to commit when we are
353 // called in GrowHeap() during an allocation :-/. We need to eval the cost of
354 // that oscillation, and possibly do something to reduce it.
355
356 // TODO(jar): We need a better strategy for deciding to commit, or decommit,
357 // based on memory usage and free heap sizes.
358
359 uint64_t temp_committed = 0;
360
361 const PageID p = span->start;
362 const Length n = span->length;
363 Span* prev = GetDescriptor(p-1);
364 if (prev != NULL && MayMergeSpans(span, prev)) {
365 // Merge preceding span into this span
366 ASSERT(prev->start + prev->length == p);
367 const Length len = prev->length;
368 if (aggressive_decommit_ && prev->location == Span::ON_RETURNED_FREELIST) {
369 // We're about to put the merge span into the returned freelist and call
370 // DecommitSpan() on it, which will mark the entire span including this
371 // one as released and decrease stats_.committed_bytes by the size of the
372 // merged span. To make the math work out we temporarily increase the
373 // stats_.committed_bytes amount.
374 temp_committed = prev->length << kPageShift;
375 }
376 RemoveFromFreeList(prev);
377 DeleteSpan(prev);
378 span->start -= len;
379 span->length += len;
380 pagemap_.set(span->start, span);
381 Event(span, 'L', len);
382 }
383 Span* next = GetDescriptor(p+n);
384 if (next != NULL && MayMergeSpans(span, next)) {
385 // Merge next span into this span
386 ASSERT(next->start == p+n);
387 const Length len = next->length;
388 if (aggressive_decommit_ && next->location == Span::ON_RETURNED_FREELIST) {
389 // See the comment below 'if (prev->location ...' for explanation.
390 temp_committed += next->length << kPageShift;
391 }
392 RemoveFromFreeList(next);
393 DeleteSpan(next);
394 span->length += len;
395 pagemap_.set(span->start + span->length - 1, span);
396 Event(span, 'R', len);
397 }
398
399 if (aggressive_decommit_) {
400 if (DecommitSpan(span)) {
401 span->location = Span::ON_RETURNED_FREELIST;
402 stats_.committed_bytes += temp_committed;
403 } else {
404 ASSERT(temp_committed == 0);
405 }
406 }
407 PrependToFreeList(span);
408}
409
410void PageHeap::PrependToFreeList(Span* span) {
411 ASSERT(span->location != Span::IN_USE);
412 SpanList* list = (span->length < kMaxPages) ? &free_[span->length] : &large_;
413 if (span->location == Span::ON_NORMAL_FREELIST) {
414 stats_.free_bytes += (span->length << kPageShift);
415 DLL_Prepend(&list->normal, span);
416 } else {
417 stats_.unmapped_bytes += (span->length << kPageShift);
418 DLL_Prepend(&list->returned, span);
419 }
420}
421
422void PageHeap::RemoveFromFreeList(Span* span) {
423 ASSERT(span->location != Span::IN_USE);
424 if (span->location == Span::ON_NORMAL_FREELIST) {
425 stats_.free_bytes -= (span->length << kPageShift);
426 } else {
427 stats_.unmapped_bytes -= (span->length << kPageShift);
428 }
429 DLL_Remove(span);
430}
431
432void PageHeap::IncrementalScavenge(Length n) {
433 // Fast path; not yet time to release memory
434 scavenge_counter_ -= n;
435 if (scavenge_counter_ >= 0) return; // Not yet time to scavenge
436
437 const double rate = FLAGS_tcmalloc_release_rate;
438 if (rate <= 1e-6) {
439 // Tiny release rate means that releasing is disabled.
440 scavenge_counter_ = kDefaultReleaseDelay;
441 return;
442 }
443
444 Length released_pages = ReleaseAtLeastNPages(1);
445
446 if (released_pages == 0) {
447 // Nothing to scavenge, delay for a while.
448 scavenge_counter_ = kDefaultReleaseDelay;
449 } else {
450 // Compute how long to wait until we return memory.
451 // FLAGS_tcmalloc_release_rate==1 means wait for 1000 pages
452 // after releasing one page.
453 const double mult = 1000.0 / rate;
454 double wait = mult * static_cast<double>(released_pages);
455 if (wait > kMaxReleaseDelay) {
456 // Avoid overflow and bound to reasonable range.
457 wait = kMaxReleaseDelay;
458 }
459 scavenge_counter_ = static_cast<int64_t>(wait);
460 }
461}
462
463Length PageHeap::ReleaseLastNormalSpan(SpanList* slist) {
464 Span* s = slist->normal.prev;
465 ASSERT(s->location == Span::ON_NORMAL_FREELIST);
466
467 if (DecommitSpan(s)) {
468 RemoveFromFreeList(s);
469 const Length n = s->length;
470 s->location = Span::ON_RETURNED_FREELIST;
471 MergeIntoFreeList(s); // Coalesces if possible.
472 return n;
473 }
474
475 return 0;
476}
477
478Length PageHeap::ReleaseAtLeastNPages(Length num_pages) {
479 Length released_pages = 0;
480
481 // Round robin through the lists of free spans, releasing the last
482 // span in each list. Stop after releasing at least num_pages
483 // or when there is nothing more to release.
484 while (released_pages < num_pages && stats_.free_bytes > 0) {
485 for (int i = 0; i < kMaxPages+1 && released_pages < num_pages;
486 i++, release_index_++) {
487 if (release_index_ > kMaxPages) release_index_ = 0;
488 SpanList* slist = (release_index_ == kMaxPages) ?
489 &large_ : &free_[release_index_];
490 if (!DLL_IsEmpty(&slist->normal)) {
491 Length released_len = ReleaseLastNormalSpan(slist);
492 // Some systems do not support release
493 if (released_len == 0) return released_pages;
494 released_pages += released_len;
495 }
496 }
497 }
498 return released_pages;
499}
500
501bool PageHeap::EnsureLimit(Length n, bool withRelease)
502{
503 Length limit = (FLAGS_tcmalloc_heap_limit_mb*1024*1024) >> kPageShift;
504 if (limit == 0) return true; //there is no limit
505
506 // We do not use stats_.system_bytes because it does not take
507 // MetaDataAllocs into account.
508 Length takenPages = TCMalloc_SystemTaken >> kPageShift;
509 //XXX takenPages may be slightly bigger than limit for two reasons:
510 //* MetaDataAllocs ignore the limit (it is not easy to handle
511 // out of memory there)
512 //* sys_alloc may round allocation up to huge page size,
513 // although smaller limit was ensured
514
515 ASSERT(takenPages >= stats_.unmapped_bytes >> kPageShift);
516 takenPages -= stats_.unmapped_bytes >> kPageShift;
517
518 if (takenPages + n > limit && withRelease) {
519 takenPages -= ReleaseAtLeastNPages(takenPages + n - limit);
520 }
521
522 return takenPages + n <= limit;
523}
524
525void PageHeap::RegisterSizeClass(Span* span, size_t sc) {
526 // Associate span object with all interior pages as well
527 ASSERT(span->location == Span::IN_USE);
528 ASSERT(GetDescriptor(span->start) == span);
529 ASSERT(GetDescriptor(span->start+span->length-1) == span);
530 Event(span, 'C', sc);
531 span->sizeclass = sc;
532 for (Length i = 1; i < span->length-1; i++) {
533 pagemap_.set(span->start+i, span);
534 }
535}
536
537void PageHeap::GetSmallSpanStats(SmallSpanStats* result) {
538 for (int s = 0; s < kMaxPages; s++) {
539 result->normal_length[s] = DLL_Length(&free_[s].normal);
540 result->returned_length[s] = DLL_Length(&free_[s].returned);
541 }
542}
543
544void PageHeap::GetLargeSpanStats(LargeSpanStats* result) {
545 result->spans = 0;
546 result->normal_pages = 0;
547 result->returned_pages = 0;
548 for (Span* s = large_.normal.next; s != &large_.normal; s = s->next) {
549 result->normal_pages += s->length;;
550 result->spans++;
551 }
552 for (Span* s = large_.returned.next; s != &large_.returned; s = s->next) {
553 result->returned_pages += s->length;
554 result->spans++;
555 }
556}
557
558bool PageHeap::GetNextRange(PageID start, base::MallocRange* r) {
559 Span* span = reinterpret_cast<Span*>(pagemap_.Next(start));
560 if (span == NULL) {
561 return false;
562 }
563 r->address = span->start << kPageShift;
564 r->length = span->length << kPageShift;
565 r->fraction = 0;
566 switch (span->location) {
567 case Span::IN_USE:
568 r->type = base::MallocRange::INUSE;
569 r->fraction = 1;
570 if (span->sizeclass > 0) {
571 // Only some of the objects in this span may be in use.
572 const size_t osize = Static::sizemap()->class_to_size(span->sizeclass);
573 r->fraction = (1.0 * osize * span->refcount) / r->length;
574 }
575 break;
576 case Span::ON_NORMAL_FREELIST:
577 r->type = base::MallocRange::FREE;
578 break;
579 case Span::ON_RETURNED_FREELIST:
580 r->type = base::MallocRange::UNMAPPED;
581 break;
582 default:
583 r->type = base::MallocRange::UNKNOWN;
584 break;
585 }
586 return true;
587}
588
589static void RecordGrowth(size_t growth) {
590 StackTrace* t = Static::stacktrace_allocator()->New();
591 t->depth = GetStackTrace(t->stack, kMaxStackDepth-1, 3);
592 t->size = growth;
593 t->stack[kMaxStackDepth-1] = reinterpret_cast<void*>(Static::growth_stacks());
594 Static::set_growth_stacks(t);
595}
596
597bool PageHeap::GrowHeap(Length n) {
598 ASSERT(kMaxPages >= kMinSystemAlloc);
599 if (n > kMaxValidPages) return false;
600 Length ask = (n>kMinSystemAlloc) ? n : static_cast<Length>(kMinSystemAlloc);
601 size_t actual_size;
602 void* ptr = NULL;
603 if (EnsureLimit(ask)) {
604 ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize);
605 }
606 if (ptr == NULL) {
607 if (n < ask) {
608 // Try growing just "n" pages
609 ask = n;
610 if (EnsureLimit(ask)) {
611 ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize);
612 }
613 }
614 if (ptr == NULL) return false;
615 }
616 ask = actual_size >> kPageShift;
617 RecordGrowth(ask << kPageShift);
618
619 uint64_t old_system_bytes = stats_.system_bytes;
620 stats_.system_bytes += (ask << kPageShift);
621 stats_.committed_bytes += (ask << kPageShift);
622 const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
623 ASSERT(p > 0);
624
625 // If we have already a lot of pages allocated, just pre allocate a bunch of
626 // memory for the page map. This prevents fragmentation by pagemap metadata
627 // when a program keeps allocating and freeing large blocks.
628
629 if (old_system_bytes < kPageMapBigAllocationThreshold
630 && stats_.system_bytes >= kPageMapBigAllocationThreshold) {
631 pagemap_.PreallocateMoreMemory();
632 }
633
634 // Make sure pagemap_ has entries for all of the new pages.
635 // Plus ensure one before and one after so coalescing code
636 // does not need bounds-checking.
637 if (pagemap_.Ensure(p-1, ask+2)) {
638 // Pretend the new area is allocated and then Delete() it to cause
639 // any necessary coalescing to occur.
640 Span* span = NewSpan(p, ask);
641 RecordSpan(span);
642 Delete(span);
643 ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes);
644 ASSERT(Check());
645 return true;
646 } else {
647 // We could not allocate memory within "pagemap_"
648 // TODO: Once we can return memory to the system, return the new span
649 return false;
650 }
651}
652
653bool PageHeap::Check() {
654 ASSERT(free_[0].normal.next == &free_[0].normal);
655 ASSERT(free_[0].returned.next == &free_[0].returned);
656 return true;
657}
658
659bool PageHeap::CheckExpensive() {
660 bool result = Check();
661 CheckList(&large_.normal, kMaxPages, 1000000000, Span::ON_NORMAL_FREELIST);
662 CheckList(&large_.returned, kMaxPages, 1000000000, Span::ON_RETURNED_FREELIST);
663 for (Length s = 1; s < kMaxPages; s++) {
664 CheckList(&free_[s].normal, s, s, Span::ON_NORMAL_FREELIST);
665 CheckList(&free_[s].returned, s, s, Span::ON_RETURNED_FREELIST);
666 }
667 return result;
668}
669
670bool PageHeap::CheckList(Span* list, Length min_pages, Length max_pages,
671 int freelist) {
672 for (Span* s = list->next; s != list; s = s->next) {
673 CHECK_CONDITION(s->location == freelist); // NORMAL or RETURNED
674 CHECK_CONDITION(s->length >= min_pages);
675 CHECK_CONDITION(s->length <= max_pages);
676 CHECK_CONDITION(GetDescriptor(s->start) == s);
677 CHECK_CONDITION(GetDescriptor(s->start+s->length-1) == s);
678 }
679 return true;
680}
681
682} // namespace tcmalloc