GCHeap.cpp

Go to the documentation of this file.
00001 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is [Open Source Virtual Machine.].
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Adobe System Incorporated.
00019  * Portions created by the Initial Developer are Copyright (C) 2004-2006
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Adobe AS3 Team
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 
00040 #include <string.h>
00041 // TODO: remove this hack:
00042 #ifdef __WINSCW__
00043 #include <e32std.h>
00044 #endif
00045 #include "MMgc.h"
00046 
00047 #if defined(DARWIN) || defined(MMGC_ARM) || defined (MMGC_SPARC)
00048 #include <stdlib.h>
00049 #endif
00050 
00051 namespace MMgc
00052 {
00053 #ifdef _DEBUG
00054     // 64bit - Warning, this debug mechanism will fail on 64-bit systems when we 
00055     // allocate pages across more than 4 GB of memory space.  We no longer have 20-bits
00056     // to track - we have 52-bits.  
00057     uint8 GCHeap::m_megamap[1048576];
00058 #endif
00059 
00060     GCHeap *GCHeap::instance = NULL;
00061 
00062     const bool decommit = true;
00063 
00064     // set this to true to disable code that prevents decommmission from 
00065     // happening too frequently
00066     const bool decommitStress = false;
00067 
00068     void GCHeap::Init(GCMallocFuncPtr m, GCFreeFuncPtr f, int initialSize)
00069     {
00070         GCAssert(instance == NULL);
00071         instance = new GCHeap(m,f, initialSize);
00072     }
00073 
00074     void GCHeap::Destroy()
00075     {
00076         GCAssert(instance != NULL);
00077         delete instance;
00078         instance = NULL;
00079     }
00080 
00081     GCHeap::GCHeap(GCMallocFuncPtr m, GCFreeFuncPtr f, int initialSize)
00082         : heapVerbose(false),
00083           kNativePageSize(0),
00084           heapLimit((size_t)-1)
00085     {
00086 #ifdef _DEBUG
00087         // dump memory profile after sweeps
00088         enableMemoryProfiling = false;
00089 #endif
00090 
00091 #if defined(MMGC_PORTING_API)
00092         // When Porting API is in use, we don't want explicit mention of malloc or free.
00093         m_malloc = m ? m : MMGC_PortAPI_Alloc_func;
00094         m_free = f ? f : MMGC_PortAPI_Free_func;        
00095 #else
00096 #if defined(_MAC) || defined(MMGC_ARM) || defined (MMGC_SPARC)
00097         m_malloc = m ? m : (GCMallocFuncPtr)malloc;
00098         m_free = f ? f : (GCFreeFuncPtr)free;       
00099 #else
00100         (void)m;
00101         (void)f;
00102 #endif
00103 #endif
00104         #ifdef _DEBUG
00105         // Initialize the megamap for debugging.
00106         memset(m_megamap, 0, sizeof(m_megamap));
00107         #endif
00108         
00109         lastRegion  = 0;
00110         blocksLen   = 0;
00111         numAlloc    = 0;
00112         numDecommitted = 0;
00113         blocks      = NULL;
00114 
00115         // Initialize free lists
00116         HeapBlock *block = freelists;
00117         for (int i=0; i<kNumFreeLists; i++) {
00118             block->baseAddr     = NULL;
00119             block->size         = 0;
00120             block->sizePrevious = 0;
00121             block->prev         = block;
00122             block->next         = block;
00123             block->committed    = true;
00124             block->dirty        = true;
00125             block++;
00126         }
00127         
00128 #ifdef MMGC_AVMPLUS
00129         // get native page size from OS
00130         kNativePageSize = vmPageSize();
00131         committedCodeMemory = 0;
00132 
00133         #ifdef WIN32
00134         // Check OS version to see if PAGE_GUARD is supported
00135         // (It is not supported on Win9x)
00136         OSVERSIONINFO osvi;
00137         ::GetVersionEx(&osvi);
00138         useGuardPages = (osvi.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS);
00139         #endif
00140 #endif
00141 
00142         // Create the initial heap
00143         ExpandHeap(initialSize);
00144 
00145         decommitTicks = 0;
00146         decommitThresholdTicks = kDecommitThresholdMillis * GC::GetPerformanceFrequency() / 1000;
00147 
00148 #ifdef MMGC_AVMPLUS
00149         InitMirMemory();
00150 #endif /*MMGC_AVMPLUS*/
00151     }
00152 
00153     GCHeap::~GCHeap()
00154     {
00155 #ifdef MMGC_AVMPLUS
00156         FlushMirMemory();
00157 #endif /*MMGC_AVMPLUS*/
00158 
00159 #ifdef MEMORY_INFO
00160         if(numAlloc != 0)
00161         {
00162             for (unsigned int i=0; i<blocksLen; i++) 
00163             {
00164                 HeapBlock *block = &blocks[i];
00165                 if(block->baseAddr)
00166                 {
00167                     uint32 megamapIndex = ((uint32)(uintptr)block->baseAddr) >> 12;
00168                     GCAssert(m_megamap[megamapIndex] == 0);
00169                 }
00170                 if(block->inUse() && block->baseAddr)
00171                 {
00172                     GCDebugMsg(false, "Block 0x%x not freed\n", block->baseAddr);
00173                     PrintStackTraceByIndex(block->allocTrace);
00174                 }
00175             }   
00176             GCAssert(false);
00177         }
00178 #endif
00179         FreeAll();
00180     }
00181 
00182     void* GCHeap::Alloc(int size, bool expand/*=true*/, bool zero/*true*/)
00183     {
00184         GCAssert(size > 0);
00185 
00186         char *baseAddr = 0;
00187 
00188         // nested block to keep memset/memory commit out of critical section
00189         {
00190 #ifdef GCHEAP_LOCK
00191             GCAcquireSpinlock spinlock(m_spinlock);
00192 #endif /* GCHEAP_LOCK */
00193 
00194             HeapBlock *block = AllocBlock(size, zero);
00195 
00196             if (!block && expand) {
00197                 ExpandHeapPrivate(size);
00198                 block = AllocBlock(size, zero);
00199             }
00200 
00201             if (!block) {
00202                 return NULL;
00203             }
00204 
00205             GCAssert(block->size == size);
00206             
00207             numAlloc += size;
00208 
00209 #ifdef _DEBUG
00210             // Check for debug builds only:
00211             // Use the megamap to double-check that we haven't handed
00212             // any of these pages out already.
00213             uint32 megamapIndex = ((uint32)(uintptr)block->baseAddr)>>12;
00214             for (int i=0; i<size; i++) {
00215                 GCAssert(m_megamap[megamapIndex] == 0);
00216                 
00217                 // Set the megamap entry
00218                 m_megamap[megamapIndex++] = 1;
00219             }
00220 #endif
00221 
00222             // copy baseAddr to a stack variable to fix :
00223             // http://flashqa.macromedia.com/bugapp/detail.asp?ID=125938
00224             baseAddr = block->baseAddr;
00225         }
00226 
00227 #ifdef MEMORY_INFO
00228         // do this outside of the spinlock to prevent deadlock w/ trace table lock
00229         AddrToBlock(baseAddr)->allocTrace = GetStackTraceIndex(2);
00230 #endif
00231         // Zero out the memory, if requested to do so
00232         // FIXME: if we know that this memory was just committed we shouldn't do this
00233         // maybe zero should pass deeper into the system so that on initial expansions
00234         // and re-commit we avoid this, should be a noise optimization over all but
00235         // possibly a big win for startup
00236         if (zero) {
00237             memset(baseAddr, 0, size * kBlockSize);
00238         }
00239         
00240         return baseAddr;
00241     }
00242 
00243     void GCHeap::Free(void *item)
00244     {
00245 #ifdef GCHEAP_LOCK
00246         GCAcquireSpinlock spinlock(m_spinlock);
00247 #endif /* GCHEAP_LOCK */
00248 
00249         HeapBlock *block = AddrToBlock(item);
00250         if (block) {
00251 
00252             #ifdef _DEBUG
00253             // For debug builds only:
00254             // Check the megamap to ensure that all the pages
00255             // being freed are in fact allocated.
00256             uint32 megamapIndex = ((uint32)(uintptr)block->baseAddr) >> 12;
00257             for (int i=0; i<block->size; i++) {
00258                 if(m_megamap[megamapIndex] != 1) {
00259                     GCAssertMsg(false, "Megamap is screwed up, are you freeing freed memory?");
00260                     int *data = (int*)item;
00261                     (void)data; // silence compiler warning
00262                 }
00263 
00264                 // Clear the entry
00265                 m_megamap[megamapIndex++] = 0;
00266             }
00267             #endif
00268 
00269             // Update metrics
00270             GCAssert(numAlloc >= (unsigned int)block->size);
00271             numAlloc -= block->size;
00272             
00273             FreeBlock(block);
00274         }
00275     }
00276 
00277     
00278     size_t GCHeap::Size(const void *item)
00279     {
00280 #ifdef GCHEAP_LOCK
00281         GCAcquireSpinlock spinlock(m_spinlock);
00282 #endif /* GCHEAP_LOCK */
00283         HeapBlock *block = AddrToBlock(item);
00284         return block->size;
00285     }
00286 
00287     void GCHeap::Decommit()
00288     {
00289 #ifdef DECOMMIT_MEMORY
00290 
00291 #ifdef GCHEAP_LOCK
00292         GCAcquireSpinlock spinlock(m_spinlock);
00293 #endif /* GCHEAP_LOCK */
00294 
00295         // commit if > %25 of the heap stays free
00296         size_t decommitSize = GetFreeHeapSize() * 100 - GetTotalHeapSize() * kDecommitThresholdPercentage;
00297 
00298         decommitSize /= 100;
00299 
00300         if(!decommitStress)
00301         {
00302             if(!decommit || decommitSize < 0) {
00303                 
00304                 decommitTicks = 0;
00305                 return;
00306             }
00307         
00308             if(decommitTicks == 0) {
00309                 decommitTicks = GC::GetPerformanceCounter();
00310                 return;
00311             } else if ((GC::GetPerformanceCounter() - decommitTicks) < decommitThresholdTicks) {
00312                 return;
00313             }
00314         }
00315 
00316         decommitTicks = 0;
00317 
00318         // don't trifle
00319         if(decommitSize < (size_t)kMinHeapIncrement)
00320             return;
00321 
00322         // search from the end of the free list so we decommit big blocks, if
00323         // a free block is bigger than 
00324         HeapBlock *freelist = freelists+kNumFreeLists-1;
00325 
00326         HeapBlock *endOfBigFreelists = &freelists[GetFreeListIndex(kMinHeapIncrement)];
00327 
00328         for (; freelist >= endOfBigFreelists && decommitSize > 0; freelist--)
00329         {
00330             HeapBlock *block = freelist;
00331             while ((block = block->prev) != freelist && decommitSize > 0)
00332             {
00333                 // decommitting already decommitted blocks doesn't help
00334                 if(!block->committed || block->size == 0)
00335                     continue;
00336 
00337 #ifdef USE_MMAP             
00338                 RemoveFromList(block);
00339                 if((size_t)block->size > decommitSize)
00340                 {
00341                     HeapBlock *newBlock = block + decommitSize;
00342                     newBlock->baseAddr = block->baseAddr + kBlockSize * decommitSize;
00343 
00344                     newBlock->size = (int)(block->size - decommitSize);
00345                     newBlock->sizePrevious = (int)decommitSize;
00346                     newBlock->committed = block->committed;
00347                     newBlock->dirty = block->dirty;
00348                     block->size = (int)decommitSize;
00349 
00350                     // Update sizePrevious in block after that
00351                     HeapBlock *nextBlock = newBlock + newBlock->size;
00352                     nextBlock->sizePrevious = newBlock->size;
00353 
00354                     // Add the newly created block to the
00355                     // free list
00356                     AddToFreeList(newBlock);
00357                 }
00358 
00359                 if(DecommitMemoryThatMaySpanRegions(block->baseAddr, block->size * kBlockSize))
00360                 {
00361                     block->committed = false;
00362 #ifdef _MAC
00363                     block->dirty = true;
00364 #else                   
00365                     block->dirty = false;
00366 #endif                  
00367                     decommitSize -= block->size;
00368 #ifdef MEMORY_INFO
00369                     if(heapVerbose)
00370                         GCDebugMsg(false, "Decommitted %d page block\n", block->size);
00371 #endif
00372                 }
00373                 else
00374                 {
00375                     GCAssert(false);
00376                 }
00377 
00378                 numDecommitted += block->size;
00379 
00380                 // merge with previous/next if not in use and not committed
00381                 HeapBlock *prev = block - block->sizePrevious;
00382                 if(block->sizePrevious != 0 && !prev->committed && !prev->inUse()) {
00383                     RemoveFromList(prev);
00384 
00385                     prev->size += block->size;
00386 
00387                     block->size = 0;
00388                     block->sizePrevious = 0;
00389                     block->baseAddr = 0;
00390 
00391                     block = prev;
00392 #ifdef _MAC
00393                     block->dirty = true;
00394 #endif
00395                 }
00396 
00397                 HeapBlock *next = block + block->size;
00398                 if(next->size != 0 && !next->committed && !next->inUse()) {
00399                     RemoveFromList(next);
00400 
00401                     block->size += next->size;
00402                 
00403                     next->size = 0;
00404                     next->sizePrevious = 0;
00405                     next->baseAddr = 0;
00406                 }
00407 
00408                 next = block + block->size;
00409                 next->sizePrevious = block->size;
00410 
00411                 // add this block to the back of the bus to make sure we consume committed memory
00412                 // first
00413                 HeapBlock *backOfTheBus = &freelists[kNumFreeLists-1];
00414                 HeapBlock *pointToInsert = backOfTheBus;
00415                 while ((pointToInsert = pointToInsert->next) !=  backOfTheBus) {
00416                     if (pointToInsert->size >= block->size && !pointToInsert->committed) {
00417                         break;
00418                     }
00419                 }
00420                 AddToFreeList(block, pointToInsert);
00421 
00422                 // so we keep going through freelist properly
00423                 block = freelist;
00424 #else
00425                 // if we aren't using mmap we can only do something if the block maps to a region
00426                 // that is completely empty
00427                 Region *region = AddrToRegion(block->baseAddr);
00428                 if(block->baseAddr == region->baseAddr && // beginnings match
00429                     region->commitTop == block->baseAddr + block->size*kBlockSize) {
00430 
00431                     RemoveFromList(block);
00432 
00433                     // we're removing a region so re-allocate the blocks w/o the blocks for this region
00434                     HeapBlock *newBlocks = new HeapBlock[blocksLen - block->size];
00435 
00436                     // copy blocks before this block
00437                     memcpy(newBlocks, blocks, (block - blocks) * sizeof(HeapBlock));
00438 
00439                     // copy blocks after
00440                     size_t lastChunkSize = (char*)(blocks + blocksLen) - (char*)(block + block->size);
00441                     memcpy(newBlocks + (block - blocks), block + block->size, lastChunkSize);
00442 
00443                     // Fix up the prev/next pointers of each freelist.  This is a little more complicated
00444                     // than the similiar code in ExpandHeap because blocks after the one we are free'ing
00445                     // are sliding down by block->size
00446                     HeapBlock *fl = freelists;
00447                     for (int i=0; i<kNumFreeLists; i++) {
00448                         HeapBlock *temp = fl;
00449                         do {
00450                             if (temp->prev != fl) {
00451                                 if(temp->prev > block) {
00452                                     temp->prev = newBlocks + (temp->prev-blocks-block->size);
00453                                 } else {
00454                                     temp->prev = newBlocks + (temp->prev-blocks);
00455                                 }
00456                             }
00457                             if (temp->next != fl) {
00458                                 if(temp->next > block) {
00459                                     temp->next = newBlocks + (temp->next-blocks-block->size);
00460                                 } else {
00461                                     temp->next = newBlocks + (temp->next-blocks);
00462                                 }
00463                             }
00464                         } while ((temp = temp->next) != fl);
00465                         fl++;
00466                     }
00467 
00468                     // need to decrement blockId for regions in blocks after block
00469                     Region *r = lastRegion;
00470                     while(r) {
00471                         if(r->blockId > region->blockId) {
00472                             r->blockId -= block->size;
00473                         }
00474                         r = r->prev;
00475                     }
00476 
00477                     blocksLen -= block->size;
00478                     decommitSize -= block->size;
00479                     
00480                     delete [] blocks;
00481                     blocks = newBlocks;
00482                     RemoveRegion(region);
00483 
00484                     // start over at beginning of freelist
00485                     block = freelist;
00486                 }
00487 #endif
00488             }
00489         }       
00490 #endif
00491     }
00492 
00493     GCHeap::Region *GCHeap::AddrToRegion(const void *item) const
00494     {
00495         // Linear search of regions list to find this address.
00496         // The regions list should usually be pretty short.
00497         for (Region *region = lastRegion;
00498              region != NULL;
00499              region = region->prev)
00500         {
00501             if (item >= region->baseAddr && item < region->reserveTop) {
00502                 return region;
00503             }
00504         }
00505         return NULL;
00506     }
00507 
00508     GCHeap::HeapBlock* GCHeap::AddrToBlock(const void *item) const
00509     {
00510         Region *region = AddrToRegion(item);
00511         if(region) {
00512             size_t index = ((char*)item - region->baseAddr) / kBlockSize;
00513             HeapBlock *b = blocks + region->blockId + index;
00514             GCAssert(item >= b->baseAddr && item < b->baseAddr + b->size * GCHeap::kBlockSize);
00515             return b;
00516         }
00517         return NULL;
00518     }
00519     
00520     GCHeap::HeapBlock* GCHeap::AllocBlock(int size, bool& zero)
00521     {
00522         int startList = GetFreeListIndex(size);
00523         HeapBlock *freelist = &freelists[startList];
00524 
00525         HeapBlock *decommittedSuitableBlock = NULL;
00526 
00527         for (int i = startList; i < kNumFreeLists; i++)
00528         {
00529             // Search for a big enough block in free list
00530             HeapBlock *block = freelist;
00531             while ((block = block->next) != freelist)
00532             {
00533                 if (block->size >= size && block->committed) 
00534                 {
00535                     // why do we do this?  putting in assert but I think it should be removed
00536                     GCAssert(!block->inUse());  
00537                     if( !block->prev || !block->next ) return NULL;
00538 
00539 
00540                     RemoveFromList(block);
00541 
00542                     if(block->size > size)
00543                     {
00544                         HeapBlock *newBlock = Split(block, size);
00545 
00546                         // Add the newly created block to the free list
00547                         AddToFreeList(newBlock);
00548                     }
00549                 
00550                     CheckFreelist();
00551 
00552                     zero = block->dirty && zero;
00553                     
00554                     GCAssert(!block->dirty ? *(uint32*)block->baseAddr == 0 : true);
00555                     
00556                     return block;
00557                 }
00558 
00559 #if defined(USE_MMAP) && defined(DECOMMIT_MEMORY)
00560                 // if the block isn't committed see if this request can be met with by committing
00561                 // it and combining it with its neighbors
00562                 if(!block->committed && !decommittedSuitableBlock)
00563                 {
00564                     int totalSize = block->size;
00565 
00566                     // first try predecessors
00567                     HeapBlock *firstFree = block;
00568 
00569                     // loop because we could have interleaved committed/non-committed blocks
00570                     while(totalSize < size && firstFree->sizePrevious != 0)
00571                     {   
00572                         HeapBlock *prevBlock = firstFree - firstFree->sizePrevious;
00573                         if(!prevBlock->inUse() && prevBlock->size > 0) {
00574                             totalSize += prevBlock->size;
00575                             firstFree = prevBlock;
00576                         } else {
00577                             break;
00578                         }
00579                     }
00580 
00581                     if(totalSize > size) {
00582                         decommittedSuitableBlock = firstFree;
00583                     } else {
00584                         // now try successors
00585                         HeapBlock *nextBlock = block + block->size;
00586                         while(nextBlock->size > 0 && !nextBlock->inUse() && totalSize < size) {
00587                             totalSize += nextBlock->size;
00588                             nextBlock = nextBlock + nextBlock->size;
00589                         }
00590 
00591                         if(totalSize > size) {
00592                             decommittedSuitableBlock = firstFree;
00593                         }
00594                     }
00595                 }
00596 #endif
00597             }
00598             freelist++;
00599         }   
00600 
00601 #if defined(USE_MMAP) && defined(DECOMMIT_MEMORY)
00602         if(decommittedSuitableBlock)
00603         {
00604             // first handle case where its too big
00605             if(decommittedSuitableBlock->size > size)
00606             {               
00607                 int toCommit = size > kMinHeapIncrement ? size : kMinHeapIncrement;
00608 
00609                 if(toCommit > decommittedSuitableBlock->size)
00610                     toCommit = decommittedSuitableBlock->size;
00611 
00612                 RemoveFromList(decommittedSuitableBlock);
00613                 
00614                 // first split off part we're gonna commit
00615                 if(decommittedSuitableBlock->size > toCommit) {
00616                     HeapBlock *newBlock = Split(decommittedSuitableBlock, toCommit);
00617 
00618                     // put the still uncommitted part back on freelist
00619                     AddToFreeList(newBlock);
00620                 }
00621                 
00622                 Commit(decommittedSuitableBlock);
00623 
00624                 if(toCommit > size) {
00625                     HeapBlock *newBlock = Split(decommittedSuitableBlock, size);
00626                     AddToFreeList(newBlock);
00627                 }
00628             }
00629             else // too small
00630             {
00631                 // need to stitch blocks together committing uncommitted blocks
00632                 HeapBlock *block = decommittedSuitableBlock;
00633                 RemoveFromList(block);
00634 
00635                 int amountRecommitted = block->committed ? 0 : block->size;
00636                     
00637                 while(block->size < size)
00638                 {
00639                     HeapBlock *nextBlock = block + block->size;
00640 
00641                     RemoveFromList(nextBlock);
00642                         
00643                     // Increase size of current block
00644                     block->size += nextBlock->size;
00645                     amountRecommitted += nextBlock->committed ? 0 : nextBlock->size;
00646 
00647                     nextBlock->size = 0;
00648                     nextBlock->baseAddr = 0;
00649                     nextBlock->sizePrevious = 0;
00650 
00651                     block->dirty |= nextBlock->dirty;
00652                 }
00653 
00654                 GCAssert(amountRecommitted > 0);
00655 
00656                 if(!CommitMemoryThatMaySpanRegions(block->baseAddr, block->size * kBlockSize)) 
00657                 {
00658                     GCAssert(false);
00659                 }
00660 #ifdef MEMORY_INFO
00661                 if(heapVerbose)
00662                     GCDebugMsg(false, "Recommitted %d pages\n", amountRecommitted);
00663 #endif
00664                 numDecommitted -= amountRecommitted;
00665                 block->committed = true;
00666 
00667                 GCAssert(decommittedSuitableBlock->size >= size);
00668 
00669                 // split last block
00670                 if(block->size > size)
00671                 {
00672                     HeapBlock *newBlock = Split(block, size);
00673                     AddToFreeList(newBlock);
00674                 }
00675             }
00676 
00677             GCAssert(decommittedSuitableBlock->size == size);
00678 
00679             // update sizePrevious in next block
00680             HeapBlock *nextBlock = decommittedSuitableBlock + size;
00681             nextBlock->sizePrevious = size;
00682 
00683             CheckFreelist();
00684 
00685             return decommittedSuitableBlock;
00686         }
00687 #endif
00688     
00689         CheckFreelist();
00690         return 0;
00691     }
00692 
00693     GCHeap::HeapBlock *GCHeap::Split(HeapBlock *block, int size)
00694     {
00695         GCAssert(block->size > size);
00696         HeapBlock *newBlock = block + size;
00697         newBlock->baseAddr = block->baseAddr + kBlockSize * size;
00698 
00699         newBlock->size = block->size - size;
00700         newBlock->sizePrevious = size;
00701         newBlock->committed = block->committed;
00702         newBlock->dirty = block->dirty;
00703         block->size = size;
00704 
00705         // Update sizePrevious in block after that
00706         HeapBlock *nextBlock = newBlock + newBlock->size;
00707         nextBlock->sizePrevious = newBlock->size;
00708     
00709         return newBlock;
00710     }
00711 
00712     
00713 #if defined(DECOMMIT_MEMORY) && defined(USE_MMAP)
00714     void GCHeap::Commit(HeapBlock *block)
00715     {
00716         if(!block->committed)
00717         {
00718             if(!CommitMemoryThatMaySpanRegions(block->baseAddr, block->size * kBlockSize)) 
00719             {
00720                 GCAssert(false);
00721             }
00722 #ifdef MEMORY_INFO
00723             if(heapVerbose)
00724                 GCDebugMsg(false, "Recommitted %d pages\n", block->size);
00725 #endif
00726             numDecommitted -= block->size;
00727             block->committed = true;
00728 #ifdef _MAC
00729             // can't set to false, but don't need to set to true
00730             GCAssert(block->dirty == true);
00731 #else
00732             block->dirty = false;
00733 #endif
00734         }
00735     }
00736 #endif
00737         
00738 
00739     void GCHeap::CheckFreelist()
00740     {
00741 #ifdef _DEBUG
00742         HeapBlock *freelist = freelists;
00743         for (int i = 0; i < kNumFreeLists; i++)
00744         {
00745             HeapBlock *block = freelist;
00746             while((block = block->next) != freelist)
00747             {
00748                 GCAssert(block != block->next);
00749                 GCAssert(block != block->next->next || block->next == freelist);
00750                 if(block->sizePrevious)
00751                 {
00752                     HeapBlock *prev = block - block->sizePrevious;
00753                     GCAssert(block->sizePrevious == prev->size);
00754                 }
00755             }
00756             freelist++;
00757         }
00758 #endif
00759     }
00760 
00761     bool GCHeap::BlocksAreContiguous(void *item1, void *item2)
00762     {
00763         Region *r1 = AddrToRegion(item1);
00764         Region *r2 = AddrToRegion(item2);
00765         return r1 == r2 || r1->reserveTop == r2->baseAddr;
00766     }
00767 
00768     void GCHeap::AddToFreeList(HeapBlock *block)
00769     {
00770         GCAssert(m_megamap[((uint32)(uintptr)block->baseAddr)>>12] == 0);
00771 
00772         int index = GetFreeListIndex(block->size);
00773         HeapBlock *freelist = &freelists[index];
00774 
00775         HeapBlock *pointToInsert = freelist;
00776         
00777         // Note: We don't need to bother searching for the right
00778         // insertion point if we know all blocks on this free list
00779         // are the same size.
00780         if (block->size >= kUniqueThreshold) {
00781             while ((pointToInsert = pointToInsert->next) != freelist) {
00782                 if (pointToInsert->size >= block->size) {
00783                     break;
00784                 }
00785             }
00786         }
00787 
00788         AddToFreeList(block, pointToInsert);
00789     }
00790         
00791     void GCHeap::AddToFreeList(HeapBlock *block, HeapBlock* pointToInsert)
00792     {
00793         CheckFreelist();
00794 
00795         block->next = pointToInsert;
00796         block->prev = pointToInsert->prev;
00797         block->prev->next = block;
00798         pointToInsert->prev = block;
00799 
00800         CheckFreelist();
00801     }                          
00802 
00803     void GCHeap::FreeBlock(HeapBlock *block)
00804     {
00805         GCAssert(block->inUse());
00806 
00807 #ifdef _DEBUG
00808         // trash it. fb == free block
00809         memset(block->baseAddr, 0xfb, block->size * kBlockSize);
00810 #ifdef MEMORY_INFO
00811         block->freeTrace = GetStackTraceIndex(2);
00812 #endif
00813 #endif
00814 
00815         // Try to coalesce this block with its predecessor
00816         HeapBlock *prevBlock = block - block->sizePrevious;
00817         if (!prevBlock->inUse() && prevBlock->committed) {
00818             GCAssert(m_megamap[((uint32)(uintptr)prevBlock->baseAddr)>>12] == 0);
00819             // Remove predecessor block from free list
00820             RemoveFromList(prevBlock);
00821 
00822             // Increase size of predecessor block
00823             prevBlock->size += block->size;
00824 
00825             block->size = 0;
00826             block->sizePrevious = 0;
00827             block->baseAddr = 0;                
00828 
00829             block = prevBlock;
00830         }
00831 
00832         // Try to coalesce this block with its successor
00833         HeapBlock *nextBlock = block + block->size;
00834 
00835         if (!nextBlock->inUse() && nextBlock->committed) {
00836             // Remove successor block from free list
00837             RemoveFromList(nextBlock);
00838 
00839             // Increase size of current block
00840             block->size += nextBlock->size;
00841             nextBlock->size = 0;
00842             nextBlock->baseAddr = 0;
00843             nextBlock->sizePrevious = 0;
00844         }
00845 
00846         // Update sizePrevious in the next block
00847         nextBlock = block + block->size;
00848         nextBlock->sizePrevious = block->size;
00849 
00850         // Add this block to the right free list
00851         block->dirty = true;
00852 
00853         AddToFreeList(block);
00854 
00855         CheckFreelist();
00856     }
00857 
00858     bool GCHeap::ExpandHeap(int askSize)
00859     {
00860 #ifdef GCHEAP_LOCK
00861         // Acquire the spinlock, as this is a publicly
00862         // accessible API.
00863         GCAcquireSpinlock spinlock(m_spinlock);
00864 #endif /* GCHEAP_LOCK */
00865         return ExpandHeapPrivate(askSize);
00866     }
00867      
00868     bool GCHeap::ExpandHeapPrivate(int askSize)
00869     {
00870         int size = askSize;
00871 #ifdef _DEBUG
00872         // Turn this switch on to force non-contiguous heaps.
00873         bool debug_noncontiguous = false;
00874 
00875         // Turn this switch on to test bridging of contiguous
00876         // regions.
00877         bool test_bridging = false;
00878         int defaultReserve = test_bridging ? (size+kMinHeapIncrement) : kDefaultReserve;
00879 #else
00880         const int defaultReserve = kDefaultReserve;
00881 #endif
00882         
00883         if(GetTotalHeapSize() > heapLimit)
00884             return false;
00885         
00886         char *baseAddr = NULL;
00887         char *newRegionAddr = NULL;
00888         int newRegionSize = 0;
00889         bool contiguous = false;
00890         int commitAvail = 0;
00891         
00892         // Allocate at least kMinHeapIncrement blocks
00893         if (size < kMinHeapIncrement) {
00894             size = kMinHeapIncrement;
00895         }
00896 
00897         // Round to the nearest kMinHeapIncrement
00898         size = ((size + kMinHeapIncrement - 1) / kMinHeapIncrement) * kMinHeapIncrement;
00899 
00900         #ifdef USE_MMAP
00901 #ifdef _DEBUG
00902         if (lastRegion != NULL && !debug_noncontiguous)
00903 #else
00904         if (lastRegion != NULL)
00905 #endif
00906         {
00907             commitAvail = (int)((lastRegion->reserveTop - lastRegion->commitTop) / kBlockSize);
00908             
00909             // Can this request be satisfied purely by committing more memory that
00910             // is already reserved?
00911             if (size <= commitAvail) {
00912                 if (CommitMemory(lastRegion->commitTop, size * kBlockSize))
00913                 {
00914                     // Succeeded!
00915                     baseAddr = lastRegion->commitTop;
00916                     contiguous = true;
00917 
00918                     // Update the commit top.
00919                     lastRegion->commitTop += size*kBlockSize;
00920 
00921                     // Go set up the block list.
00922                     goto gotMemory;
00923                 }
00924                 else
00925                 {
00926                     // If we can't commit memory we've already reserved,
00927                     // no other trick is going to work.  Fail.
00928                     return false;
00929                 }
00930             }
00931 
00932             // Try to reserve a region contiguous to the last region.
00933 
00934             // - Try for the "default reservation size" if it's larger than
00935             //   the requested block.
00936             if (defaultReserve > size) {
00937                 newRegionAddr = ReserveMemory(lastRegion->reserveTop,
00938                                               defaultReserve * kBlockSize);
00939                 newRegionSize = defaultReserve;
00940             }
00941 
00942             // - If the default reservation size didn't work or isn't big
00943             //   enough, go for the exact amount requested, minus the
00944             //   committable space in the current region.
00945             if (newRegionAddr == NULL) {
00946                 newRegionAddr = ReserveMemory(lastRegion->reserveTop,
00947                                               (size - commitAvail)*kBlockSize);
00948                 newRegionSize = size - commitAvail;
00949             }
00950 
00951             if (newRegionAddr != NULL) {
00952                 // We were able to reserve some space.
00953 
00954                 // Commit available space from the existing region.
00955                 if (commitAvail != 0) {
00956                     if (!CommitMemory(lastRegion->commitTop, commitAvail * kBlockSize))
00957                     {
00958                         // We couldn't commit even this space.  We're doomed.
00959                         // Un-reserve the space we just reserved and fail.
00960                         ReleaseMemory(newRegionAddr, newRegionSize);
00961                         return false;
00962                     }
00963                 }
00964 
00965                 // Commit needed space from the new region.
00966                 if (!CommitMemory(newRegionAddr, (size - commitAvail) * kBlockSize))
00967                 {
00968                     // We couldn't commit this space.  We can't meet the
00969                     // request.  Un-commit any memory we just committed,
00970                     // un-reserve any memory we just reserved, and fail.
00971                     if (commitAvail != 0) {
00972                         DecommitMemory(lastRegion->commitTop,
00973                                        commitAvail * kBlockSize);
00974                     }
00975                     ReleaseMemory(newRegionAddr,
00976                                   (size-commitAvail)*kBlockSize);
00977                     return false;
00978                 }
00979 
00980                 // We successfully reserved a new contiguous region
00981                 // and committed the memory we need.  Finish up.
00982                 baseAddr = lastRegion->commitTop;
00983                 lastRegion->commitTop = lastRegion->reserveTop;
00984                 contiguous = true;
00985                 
00986                 goto gotMemory;
00987             }
00988         }
00989 
00990         // We were unable to allocate a contiguous region, or there
00991         // was no existing region to be contiguous to because this
00992         // is the first-ever expansion.  Allocate a non-contiguous region.
00993 
00994         // Don't use any of the available space in the current region.
00995         commitAvail = 0;
00996 
00997         // - Go for the default reservation size unless the requested
00998         //   size is bigger.
00999         if (size < defaultReserve) {
01000             newRegionAddr = ReserveMemory(NULL,
01001                                           defaultReserve*kBlockSize);
01002             newRegionSize = defaultReserve;
01003         }
01004 
01005         // - If that failed or the requested size is bigger than default,
01006         //   go for the requested size exactly.
01007         if (newRegionAddr == NULL) {
01008             newRegionAddr = ReserveMemory(NULL,
01009                                           size*kBlockSize);
01010             newRegionSize = size;
01011         }
01012 
01013         // - If that didn't work, give up.
01014         if (newRegionAddr == NULL) {
01015             return false;
01016         }
01017 
01018         // - Try to commit the memory.
01019         if (CommitMemory(newRegionAddr,
01020                          size*kBlockSize) == 0)
01021         {
01022             // Failed.  Un-reserve the memory and fail.
01023             ReleaseMemory(newRegionAddr, newRegionSize*kBlockSize);
01024             return false;
01025         }
01026 
01027         // If we got here, we've successfully allocated a
01028         // non-contiguous region.
01029         baseAddr = newRegionAddr;
01030         contiguous = false;
01031 
01032       gotMemory:
01033 
01034 #else       
01035         // Allocate the requested amount of space as a new region.
01036         newRegionAddr = AllocateMemory(size * kBlockSize);
01037         baseAddr = newRegionAddr;
01038         newRegionSize = size;
01039 
01040         // If that didn't work, give up.
01041         if (newRegionAddr == NULL) {
01042             return false;
01043         }
01044 #endif
01045 
01046         // If we were able to allocate a contiguous block, remove
01047         // the old top sentinel.
01048         if (contiguous) {
01049             blocksLen--;
01050         }
01051 
01052         // Expand the block list.
01053         int newBlocksLen = blocksLen + size;
01054 
01055         // Add space for the "top" sentinel
01056         newBlocksLen++;
01057 
01058         HeapBlock *newBlocks = new HeapBlock[newBlocksLen];
01059         if (!newBlocks) {
01060             // Could not get the memory.
01061             #ifdef USE_MMAP
01062             ReleaseMemory(newRegionAddr, newRegionSize);
01063             #else
01064             ReleaseMemory(newRegionAddr);
01065             #endif
01066             return false;
01067         }
01068         
01069         // Copy all the existing blocks.
01070         if (blocksLen) {
01071             memcpy(newBlocks, blocks, blocksLen * sizeof(HeapBlock));
01072 
01073             // Fix up the prev/next pointers of each freelist.
01074             HeapBlock *freelist = freelists;
01075             for (int i=0; i<kNumFreeLists; i++) {
01076                 HeapBlock *temp = freelist;
01077                 do {
01078                     if (temp->prev != freelist) {
01079                         temp->prev = newBlocks + (temp->prev-blocks);
01080                     }
01081                     if (temp->next != freelist) {
01082                         temp->next = newBlocks + (temp->next-blocks);
01083                     }
01084                 } while ((temp = temp->next) != freelist);
01085                 freelist++;
01086             }
01087             CheckFreelist();
01088         }
01089 
01090         // Create a single free block for the new space,
01091         // and add it to the free list.
01092         HeapBlock *block = newBlocks+blocksLen;
01093         block->baseAddr = baseAddr;
01094 
01095         block->size = size;
01096         block->sizePrevious = 0;
01097         // link up contiguous blocks
01098         if(blocksLen && contiguous)
01099         {
01100             // search backwards for first real block
01101             HeapBlock *b = &blocks[blocksLen-1];
01102             while(b->size == 0) 
01103             {
01104                 b--;
01105                 GCAssert(b >= blocks);
01106             }
01107             block->sizePrevious = b->size;
01108         }
01109         block->prev = NULL;
01110         block->next = NULL;
01111         block->committed = true;
01112 #ifdef USE_MMAP
01113 #ifdef _MAC
01114         block->dirty = true;
01115 #else
01116         block->dirty = false; // correct?
01117 #endif      
01118 #else
01119         block->dirty = true;
01120 #endif
01121 
01122 #ifdef MEMORY_INFO
01123         block->allocTrace = 0;
01124         block->freeTrace = 0;
01125 #endif
01126 
01127         AddToFreeList(block);
01128 
01129         // Initialize the rest of the new blocks to empty.
01130         for (int i=1; i<size; i++) {
01131             block++;
01132             block->baseAddr = NULL;
01133             block->size = 0;
01134             block->sizePrevious = 0;
01135             block->prev = NULL;
01136             block->next = NULL;
01137             block->committed = false;
01138 #ifdef _MAC
01139             block->dirty = true;
01140 #else
01141             block->dirty = false;
01142 #endif          
01143 #ifdef MEMORY_INFO
01144             block->allocTrace = 0;
01145             block->freeTrace = 0;
01146 #endif
01147         }
01148 
01149         // Fill in the sentinel for the top of the heap.
01150         block++;
01151         block->baseAddr     = NULL;
01152         block->size         = 0;
01153         block->sizePrevious = size;
01154         block->prev         = NULL;
01155         block->next         = NULL;
01156 #ifdef MEMORY_INFO
01157         block->allocTrace = 0;
01158 #endif
01159 
01160         // Replace the blocks list
01161         if (blocks) {
01162             delete [] blocks;
01163         }
01164         blocks = newBlocks;
01165         blocksLen = newBlocksLen;
01166 
01167         // If we created a new region, save the base address so we can free later.
01168         if (newRegionAddr) {
01169             Region *newRegion = new Region;
01170             if (newRegion == NULL) {
01171                 // Ugh, FUBAR.
01172                 return false;
01173             }
01174             newRegion->baseAddr   = newRegionAddr;
01175             newRegion->reserveTop = newRegionAddr+newRegionSize*kBlockSize;
01176             newRegion->commitTop  = newRegionAddr+(size-commitAvail)*kBlockSize;
01177             newRegion->blockId    = newBlocksLen-(size-commitAvail)-1;
01178             newRegion->prev = lastRegion;
01179             lastRegion = newRegion;
01180 
01181             #ifdef TRACE
01182             printf("ExpandHeap: new region, %d reserve, %d commit, %s\n",
01183                    newRegion->reserveTop-newRegion->baseAddr,
01184                    newRegion->commitTop-newRegion->baseAddr,
01185                    contiguous ? "contiguous" : "non-contiguous");
01186             #endif
01187         }
01188 
01189         CheckFreelist();
01190         
01191 #ifdef MEMORY_INFO
01192         if(heapVerbose)
01193             GCDebugMsg(false, "Heap expanded by %d pages:\n", size);
01194 #endif
01195             
01196         // Success!
01197         return true;
01198     }
01199 
01200     void GCHeap::RemoveRegion(Region *region)
01201     {
01202         Region **next = &lastRegion;
01203         while(*next != region) 
01204             next = &((*next)->prev);
01205         *next = region->prev;
01206 #ifdef USE_MMAP
01207         ReleaseMemory(region->baseAddr,
01208                 region->reserveTop-region->baseAddr);
01209 #else
01210         ReleaseMemory(region->baseAddr);
01211 #endif
01212         delete region;
01213     }
01214 
01215     void GCHeap::FreeAll()
01216     {
01217         // Release all of the heap regions
01218         while (lastRegion != NULL) {
01219             Region *region = lastRegion;
01220             lastRegion = lastRegion->prev;
01221             #ifdef USE_MMAP
01222             ReleaseMemory(region->baseAddr,
01223                           region->reserveTop-region->baseAddr);
01224             #else
01225             ReleaseMemory(region->baseAddr);
01226             #endif
01227             delete region;
01228         }
01229         delete [] blocks;
01230 
01231     }
01232 
01233     void GCHeap::FlushMirMemory()
01234     {
01235         for(int i=0;i<MirBufferCount;i++)
01236         {
01237             MirMemInfo* b = &m_mirBuffers[i];
01238             GCAssertMsg(b->free, "WARNING: ReleaseMirMemory was not called on this block\n");
01239             if (b->size)
01240                 ReleaseCodeMemory(b->addr, b->size);
01241         }
01242     }
01243 
01244     void GCHeap::InitMirMemory()
01245     {
01246         for(int i=0;i<MirBufferCount;i++)
01247         {
01248             MirMemInfo* b = &m_mirBuffers[i];
01249             b->addr = 0;
01250             b->size = 0;
01251             b->free = true;
01252         }
01253     }
01254         
01255     void* GCHeap::ReserveMirMemory(size_t size)
01256     {
01257         MirMemInfo* buf = 0;
01258         {
01259 #ifdef GCHEAP_LOCK
01260             GCAcquireSpinlock lock(m_mirBufferLock);
01261 #endif
01262             for(int i=0;i<MirBufferCount;i++)
01263             {
01264                 MirMemInfo* b = &m_mirBuffers[i];
01265                 if (b->free)
01266                 {
01267                     if (b->size >= size)
01268                     {
01269                         b->free = false;
01270                         buf = b;
01271                         break;
01272                     }
01273 
01274                     // pick best between empty or least largest
01275                     if (!buf || b->size < buf->size)
01276                         buf = b;
01277                 }
01278             }
01279 
01280             // lock in our selection
01281             if (buf) 
01282                 buf->free = false; 
01283         }
01284         // WATCH OUT; lock on buffers relased
01285 
01286         // no match, no room 
01287         if (!buf) return 0;
01288         
01289         // match 
01290         if (buf->size >= size) return buf->addr;
01291          
01292         // eviction 
01293         if (buf->size)
01294             ReleaseCodeMemory(buf->addr, buf->size);
01295             
01296         buf->size = size;
01297         buf->addr = ReserveCodeMemory(0,size);
01298         if (buf->addr)
01299             return buf->addr;
01300             
01301         // failure
01302         buf->size = 0;
01303 #ifdef GCHEAP_LOCK
01304         GCAcquireSpinlock lock(m_mirBufferLock);  // technically needed
01305 #endif
01306         buf->free = true;
01307         return 0;   
01308     }   
01309     
01310     void GCHeap::ReleaseMirMemory(void* addr, size_t /*size*/)
01311     {
01312         MirMemInfo* b = 0;
01313         for(int i=0;i<MirBufferCount;i++)
01314         {
01315             b = &m_mirBuffers[i];
01316             if (b->addr == addr)
01317                 break;
01318         }
01319         GCAssertMsg(b && !b->free, "Ohh very bad we have a memory leak");
01320 #ifdef GCHEAP_LOCK
01321         GCAcquireSpinlock lock(m_mirBufferLock);  // technically needed 
01322 #endif
01323         if (b) b->free = true;
01324     }
01325     
01326     size_t GCHeap::GetTotalHeapSize() const
01327     {
01328         size_t size = 0;
01329         // Release all of the heap regions
01330         Region *region = lastRegion;
01331         while (region != NULL) {
01332             size += region->commitTop - region->baseAddr;
01333             region = region->prev;
01334         }
01335         return SizeToBlocks(size);
01336     }
01337 }

Generated on Sun Oct 12 18:50:13 2008 for Tamarin by  doxygen 1.4.6