Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2008, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 // ---
     31 // Author: Sanjay Ghemawat <opensource (at) google.com>
     32 
     33 #include <config.h>
     34 #ifdef HAVE_INTTYPES_H
     35 #include <inttypes.h>                   // for PRIuPTR
     36 #endif
     37 #include <gperftools/malloc_extension.h>      // for MallocRange, etc
     38 #include "base/basictypes.h"
     39 #include "base/commandlineflags.h"
     40 #include "internal_logging.h"  // for ASSERT, TCMalloc_Printer, etc
     41 #include "page_heap_allocator.h"  // for PageHeapAllocator
     42 #include "static_vars.h"       // for Static
     43 #include "system-alloc.h"      // for TCMalloc_SystemAlloc, etc
     44 
     45 DEFINE_double(tcmalloc_release_rate,
     46               EnvToDouble("TCMALLOC_RELEASE_RATE", 1.0),
     47               "Rate at which we release unused memory to the system.  "
     48               "Zero means we never release memory back to the system.  "
     49               "Increase this flag to return memory faster; decrease it "
     50               "to return memory slower.  Reasonable rates are in the "
     51               "range [0,10]");
     52 
     53 namespace tcmalloc {
     54 
     55 PageHeap::PageHeap()
     56     : pagemap_(MetaDataAlloc),
     57       pagemap_cache_(0),
     58       scavenge_counter_(0),
     59       // Start scavenging at kMaxPages list
     60       release_index_(kMaxPages) {
     61   COMPILE_ASSERT(kNumClasses <= (1 << PageMapCache::kValuebits), valuebits);
     62   DLL_Init(&large_.normal);
     63   DLL_Init(&large_.returned);
     64   for (int i = 0; i < kMaxPages; i++) {
     65     DLL_Init(&free_[i].normal);
     66     DLL_Init(&free_[i].returned);
     67   }
     68 }
     69 
     70 Span* PageHeap::SearchFreeAndLargeLists(Length n) {
     71   ASSERT(Check());
     72   ASSERT(n > 0);
     73 
     74   // Find first size >= n that has a non-empty list
     75   for (Length s = n; s < kMaxPages; s++) {
     76     Span* ll = &free_[s].normal;
     77     // If we're lucky, ll is non-empty, meaning it has a suitable span.
     78     if (!DLL_IsEmpty(ll)) {
     79       ASSERT(ll->next->location == Span::ON_NORMAL_FREELIST);
     80       return Carve(ll->next, n);
     81     }
     82     // Alternatively, maybe there's a usable returned span.
     83     ll = &free_[s].returned;
     84     if (!DLL_IsEmpty(ll)) {
     85       ASSERT(ll->next->location == Span::ON_RETURNED_FREELIST);
     86       return Carve(ll->next, n);
     87     }
     88   }
     89   // No luck in free lists, our last chance is in a larger class.
     90   return AllocLarge(n);  // May be NULL
     91 }
     92 
     93 Span* PageHeap::New(Length n) {
     94   ASSERT(Check());
     95   ASSERT(n > 0);
     96 
     97   Span* result = SearchFreeAndLargeLists(n);
     98   if (result != NULL)
     99     return result;
    100 
    101   // Grow the heap and try again.
    102   if (!GrowHeap(n)) {
    103     ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes);
    104     ASSERT(Check());
    105     return NULL;
    106   }
    107   return SearchFreeAndLargeLists(n);
    108 }
    109 
    110 Span* PageHeap::AllocLarge(Length n) {
    111   // find the best span (closest to n in size).
    112   // The following loops implements address-ordered best-fit.
    113   Span *best = NULL;
    114 
    115   // Search through normal list
    116   for (Span* span = large_.normal.next;
    117        span != &large_.normal;
    118        span = span->next) {
    119     if (span->length >= n) {
    120       if ((best == NULL)
    121           || (span->length < best->length)
    122           || ((span->length == best->length) && (span->start < best->start))) {
    123         best = span;
    124         ASSERT(best->location == Span::ON_NORMAL_FREELIST);
    125       }
    126     }
    127   }
    128 
    129   // Search through released list in case it has a better fit
    130   for (Span* span = large_.returned.next;
    131        span != &large_.returned;
    132        span = span->next) {
    133     if (span->length >= n) {
    134       if ((best == NULL)
    135           || (span->length < best->length)
    136           || ((span->length == best->length) && (span->start < best->start))) {
    137         best = span;
    138         ASSERT(best->location == Span::ON_RETURNED_FREELIST);
    139       }
    140     }
    141   }
    142 
    143   return best == NULL ? NULL : Carve(best, n);
    144 }
    145 
    146 Span* PageHeap::Split(Span* span, Length n) {
    147   ASSERT(0 < n);
    148   ASSERT(n < span->length);
    149   ASSERT(span->location == Span::IN_USE);
    150   ASSERT(span->sizeclass == 0);
    151   Event(span, 'T', n);
    152 
    153   const int extra = span->length - n;
    154   Span* leftover = NewSpan(span->start + n, extra);
    155   ASSERT(leftover->location == Span::IN_USE);
    156   Event(leftover, 'U', extra);
    157   RecordSpan(leftover);
    158   pagemap_.set(span->start + n - 1, span); // Update map from pageid to span
    159   span->length = n;
    160 
    161   return leftover;
    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 
    176 Span* PageHeap::Carve(Span* span, Length n) {
    177   ASSERT(n > 0);
    178   ASSERT(span->location != Span::IN_USE);
    179   const int old_location = span->location;
    180   RemoveFromFreeList(span);
    181   span->location = Span::IN_USE;
    182   Event(span, 'A', n);
    183 
    184   const int extra = span->length - n;
    185   ASSERT(extra >= 0);
    186   if (extra > 0) {
    187     Span* leftover = NewSpan(span->start + n, extra);
    188     leftover->location = old_location;
    189     Event(leftover, 'S', extra);
    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 
    202     PrependToFreeList(leftover);  // Skip coalescing - no candidates possible
    203     span->length = n;
    204     pagemap_.set(span->start + n - 1, span);
    205   }
    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);
    214   return span;
    215 }
    216 
    217 void PageHeap::Delete(Span* span) {
    218   ASSERT(Check());
    219   ASSERT(span->location == Span::IN_USE);
    220   ASSERT(span->length > 0);
    221   ASSERT(GetDescriptor(span->start) == span);
    222   ASSERT(GetDescriptor(span->start + span->length - 1) == span);
    223   const Length n = span->length;
    224   span->sizeclass = 0;
    225   span->sample = 0;
    226   span->location = Span::ON_NORMAL_FREELIST;
    227   Event(span, 'D', span->length);
    228   MergeIntoFreeList(span);  // Coalesces if possible
    229   IncrementalScavenge(n);
    230   ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes);
    231   ASSERT(Check());
    232 }
    233 
    234 void PageHeap::MergeIntoFreeList(Span* span) {
    235   ASSERT(span->location != Span::IN_USE);
    236 
    237   // Coalesce -- we guarantee that "p" != 0, so no bounds checking
    238   // necessary.  We do not bother resetting the stale pagemap
    239   // entries for the pieces we are merging together because we only
    240   // care about the pagemap entries for the boundaries.
    241   //
    242   // Note that the adjacent spans we merge into "span" may come out of a
    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 
    258   const PageID p = span->start;
    259   const Length n = span->length;
    260   Span* prev = GetDescriptor(p-1);
    261   if (prev != NULL && prev->location != Span::IN_USE) {
    262     // Merge preceding span into this span
    263     ASSERT(prev->start + prev->length == p);
    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     }
    273     RemoveFromFreeList(prev);
    274     DeleteSpan(prev);
    275     span->start -= len;
    276     span->length += len;
    277     pagemap_.set(span->start, span);
    278     Event(span, 'L', len);
    279   }
    280   Span* next = GetDescriptor(p+n);
    281   if (next != NULL && next->location != Span::IN_USE) {
    282     // Merge next span into this span
    283     ASSERT(next->start == p+n);
    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     }
    289     RemoveFromFreeList(next);
    290     DeleteSpan(next);
    291     span->length += len;
    292     pagemap_.set(span->start + span->length - 1, span);
    293     Event(span, 'R', len);
    294   }
    295 
    296   Event(span, 'D', span->length);
    297   span->location = Span::ON_RETURNED_FREELIST;
    298   DecommitSpan(span);
    299   PrependToFreeList(span);
    300 }
    301 
    302 void PageHeap::PrependToFreeList(Span* span) {
    303   ASSERT(span->location != Span::IN_USE);
    304   SpanList* list = (span->length < kMaxPages) ? &free_[span->length] : &large_;
    305   if (span->location == Span::ON_NORMAL_FREELIST) {
    306     stats_.free_bytes += (span->length << kPageShift);
    307     DLL_Prepend(&list->normal, span);
    308   } else {
    309     stats_.unmapped_bytes += (span->length << kPageShift);
    310     DLL_Prepend(&list->returned, span);
    311   }
    312 }
    313 
    314 void PageHeap::RemoveFromFreeList(Span* span) {
    315   ASSERT(span->location != Span::IN_USE);
    316   if (span->location == Span::ON_NORMAL_FREELIST) {
    317     stats_.free_bytes -= (span->length << kPageShift);
    318   } else {
    319     stats_.unmapped_bytes -= (span->length << kPageShift);
    320   }
    321   DLL_Remove(span);
    322 }
    323 
    324 void PageHeap::IncrementalScavenge(Length n) {
    325   // Fast path; not yet time to release memory
    326   scavenge_counter_ -= n;
    327   if (scavenge_counter_ >= 0) return;  // Not yet time to scavenge
    328 
    329   const double rate = FLAGS_tcmalloc_release_rate;
    330   if (rate <= 1e-6) {
    331     // Tiny release rate means that releasing is disabled.
    332     scavenge_counter_ = kDefaultReleaseDelay;
    333     return;
    334   }
    335 
    336   Length released_pages = ReleaseAtLeastNPages(1);
    337 
    338   if (released_pages == 0) {
    339     // Nothing to scavenge, delay for a while.
    340     scavenge_counter_ = kDefaultReleaseDelay;
    341   } else {
    342     // Compute how long to wait until we return memory.
    343     // FLAGS_tcmalloc_release_rate==1 means wait for 1000 pages
    344     // after releasing one page.
    345     const double mult = 1000.0 / rate;
    346     double wait = mult * static_cast<double>(released_pages);
    347     if (wait > kMaxReleaseDelay) {
    348       // Avoid overflow and bound to reasonable range.
    349       wait = kMaxReleaseDelay;
    350     }
    351     scavenge_counter_ = static_cast<int64_t>(wait);
    352   }
    353 }
    354 
    355 Length PageHeap::ReleaseLastNormalSpan(SpanList* slist) {
    356   Span* s = slist->normal.prev;
    357   ASSERT(s->location == Span::ON_NORMAL_FREELIST);
    358   RemoveFromFreeList(s);
    359   const Length n = s->length;
    360   TCMalloc_SystemRelease(reinterpret_cast<void*>(s->start << kPageShift),
    361                          static_cast<size_t>(s->length << kPageShift));
    362   s->location = Span::ON_RETURNED_FREELIST;
    363   MergeIntoFreeList(s);  // Coalesces if possible.
    364   return n;
    365 }
    366 
    367 Length PageHeap::ReleaseAtLeastNPages(Length num_pages) {
    368   Length released_pages = 0;
    369   Length prev_released_pages = -1;
    370 
    371   // Round robin through the lists of free spans, releasing the last
    372   // span in each list.  Stop after releasing at least num_pages.
    373   while (released_pages < num_pages) {
    374     if (released_pages == prev_released_pages) {
    375       // Last iteration of while loop made no progress.
    376       break;
    377     }
    378     prev_released_pages = released_pages;
    379 
    380     for (int i = 0; i < kMaxPages+1 && released_pages < num_pages;
    381          i++, release_index_++) {
    382       if (release_index_ > kMaxPages) release_index_ = 0;
    383       SpanList* slist = (release_index_ == kMaxPages) ?
    384           &large_ : &free_[release_index_];
    385       if (!DLL_IsEmpty(&slist->normal)) {
    386         Length released_len = ReleaseLastNormalSpan(slist);
    387         released_pages += released_len;
    388       }
    389     }
    390   }
    391   return released_pages;
    392 }
    393 
    394 void PageHeap::RegisterSizeClass(Span* span, size_t sc) {
    395   // Associate span object with all interior pages as well
    396   ASSERT(span->location == Span::IN_USE);
    397   ASSERT(GetDescriptor(span->start) == span);
    398   ASSERT(GetDescriptor(span->start+span->length-1) == span);
    399   Event(span, 'C', sc);
    400   span->sizeclass = sc;
    401   for (Length i = 1; i < span->length-1; i++) {
    402     pagemap_.set(span->start+i, span);
    403   }
    404 }
    405 
    406 void PageHeap::GetSmallSpanStats(SmallSpanStats* result) {
    407   for (int s = 0; s < kMaxPages; s++) {
    408     result->normal_length[s] = DLL_Length(&free_[s].normal);
    409     result->returned_length[s] = DLL_Length(&free_[s].returned);
    410   }
    411 }
    412 
    413 void PageHeap::GetLargeSpanStats(LargeSpanStats* result) {
    414   result->spans = 0;
    415   result->normal_pages = 0;
    416   result->returned_pages = 0;
    417   for (Span* s = large_.normal.next; s != &large_.normal; s = s->next) {
    418     result->normal_pages += s->length;;
    419     result->spans++;
    420   }
    421   for (Span* s = large_.returned.next; s != &large_.returned; s = s->next) {
    422     result->returned_pages += s->length;
    423     result->spans++;
    424   }
    425 }
    426 
    427 bool PageHeap::GetNextRange(PageID start, base::MallocRange* r) {
    428   Span* span = reinterpret_cast<Span*>(pagemap_.Next(start));
    429   if (span == NULL) {
    430     return false;
    431   }
    432   r->address = span->start << kPageShift;
    433   r->length = span->length << kPageShift;
    434   r->fraction = 0;
    435   switch (span->location) {
    436     case Span::IN_USE:
    437       r->type = base::MallocRange::INUSE;
    438       r->fraction = 1;
    439       if (span->sizeclass > 0) {
    440         // Only some of the objects in this span may be in use.
    441         const size_t osize = Static::sizemap()->class_to_size(span->sizeclass);
    442         r->fraction = (1.0 * osize * span->refcount) / r->length;
    443       }
    444       break;
    445     case Span::ON_NORMAL_FREELIST:
    446       r->type = base::MallocRange::FREE;
    447       break;
    448     case Span::ON_RETURNED_FREELIST:
    449       r->type = base::MallocRange::UNMAPPED;
    450       break;
    451     default:
    452       r->type = base::MallocRange::UNKNOWN;
    453       break;
    454   }
    455   return true;
    456 }
    457 
    458 static void RecordGrowth(size_t growth) {
    459   StackTrace* t = Static::stacktrace_allocator()->New();
    460   t->depth = GetStackTrace(t->stack, kMaxStackDepth-1, 3);
    461   t->size = growth;
    462   t->stack[kMaxStackDepth-1] = reinterpret_cast<void*>(Static::growth_stacks());
    463   Static::set_growth_stacks(t);
    464 }
    465 
    466 bool PageHeap::GrowHeap(Length n) {
    467   ASSERT(kMaxPages >= kMinSystemAlloc);
    468   if (n > kMaxValidPages) return false;
    469   Length ask = (n>kMinSystemAlloc) ? n : static_cast<Length>(kMinSystemAlloc);
    470   size_t actual_size;
    471   void* ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize);
    472   if (ptr == NULL) {
    473     if (n < ask) {
    474       // Try growing just "n" pages
    475       ask = n;
    476       ptr = TCMalloc_SystemAlloc(ask << kPageShift, &actual_size, kPageSize);
    477     }
    478     if (ptr == NULL) return false;
    479   }
    480   ask = actual_size >> kPageShift;
    481   RecordGrowth(ask << kPageShift);
    482 
    483   uint64_t old_system_bytes = stats_.system_bytes;
    484   stats_.system_bytes += (ask << kPageShift);
    485   stats_.committed_bytes += (ask << kPageShift);
    486   const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
    487   ASSERT(p > 0);
    488 
    489   // If we have already a lot of pages allocated, just pre allocate a bunch of
    490   // memory for the page map. This prevents fragmentation by pagemap metadata
    491   // when a program keeps allocating and freeing large blocks.
    492 
    493   if (old_system_bytes < kPageMapBigAllocationThreshold
    494       && stats_.system_bytes >= kPageMapBigAllocationThreshold) {
    495     pagemap_.PreallocateMoreMemory();
    496   }
    497 
    498   // Make sure pagemap_ has entries for all of the new pages.
    499   // Plus ensure one before and one after so coalescing code
    500   // does not need bounds-checking.
    501   if (pagemap_.Ensure(p-1, ask+2)) {
    502     // Pretend the new area is allocated and then Delete() it to cause
    503     // any necessary coalescing to occur.
    504     Span* span = NewSpan(p, ask);
    505     RecordSpan(span);
    506     Delete(span);
    507     ASSERT(stats_.unmapped_bytes+ stats_.committed_bytes==stats_.system_bytes);
    508     ASSERT(Check());
    509     return true;
    510   } else {
    511     // We could not allocate memory within "pagemap_"
    512     // TODO: Once we can return memory to the system, return the new span
    513     return false;
    514   }
    515 }
    516 
    517 bool PageHeap::Check() {
    518   ASSERT(free_[0].normal.next == &free_[0].normal);
    519   ASSERT(free_[0].returned.next == &free_[0].returned);
    520   return true;
    521 }
    522 
    523 bool PageHeap::CheckExpensive() {
    524   bool result = Check();
    525   CheckList(&large_.normal, kMaxPages, 1000000000, Span::ON_NORMAL_FREELIST);
    526   CheckList(&large_.returned, kMaxPages, 1000000000, Span::ON_RETURNED_FREELIST);
    527   for (Length s = 1; s < kMaxPages; s++) {
    528     CheckList(&free_[s].normal, s, s, Span::ON_NORMAL_FREELIST);
    529     CheckList(&free_[s].returned, s, s, Span::ON_RETURNED_FREELIST);
    530   }
    531   return result;
    532 }
    533 
    534 bool PageHeap::CheckList(Span* list, Length min_pages, Length max_pages,
    535                          int freelist) {
    536   for (Span* s = list->next; s != list; s = s->next) {
    537     CHECK_CONDITION(s->location == freelist);  // NORMAL or RETURNED
    538     CHECK_CONDITION(s->length >= min_pages);
    539     CHECK_CONDITION(s->length <= max_pages);
    540     CHECK_CONDITION(GetDescriptor(s->start) == s);
    541     CHECK_CONDITION(GetDescriptor(s->start+s->length-1) == s);
    542   }
    543   return true;
    544 }
    545 
    546 }  // namespace tcmalloc
    547