00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040 #include <string.h>
00041
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
00055
00056
00057 uint8 GCHeap::m_megamap[1048576];
00058 #endif
00059
00060 GCHeap *GCHeap::instance = NULL;
00061
00062 const bool decommit = true;
00063
00064
00065
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
00088 enableMemoryProfiling = false;
00089 #endif
00090
00091 #if defined(MMGC_PORTING_API)
00092
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
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
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
00130 kNativePageSize = vmPageSize();
00131 committedCodeMemory = 0;
00132
00133 #ifdef WIN32
00134
00135
00136 OSVERSIONINFO osvi;
00137 ::GetVersionEx(&osvi);
00138 useGuardPages = (osvi.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS);
00139 #endif
00140 #endif
00141
00142
00143 ExpandHeap(initialSize);
00144
00145 decommitTicks = 0;
00146 decommitThresholdTicks = kDecommitThresholdMillis * GC::GetPerformanceFrequency() / 1000;
00147
00148 #ifdef MMGC_AVMPLUS
00149 InitMirMemory();
00150 #endif
00151 }
00152
00153 GCHeap::~GCHeap()
00154 {
00155 #ifdef MMGC_AVMPLUS
00156 FlushMirMemory();
00157 #endif
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, bool zero)
00183 {
00184 GCAssert(size > 0);
00185
00186 char *baseAddr = 0;
00187
00188
00189 {
00190 #ifdef GCHEAP_LOCK
00191 GCAcquireSpinlock spinlock(m_spinlock);
00192 #endif
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
00211
00212
00213 uint32 megamapIndex = ((uint32)(uintptr)block->baseAddr)>>12;
00214 for (int i=0; i<size; i++) {
00215 GCAssert(m_megamap[megamapIndex] == 0);
00216
00217
00218 m_megamap[megamapIndex++] = 1;
00219 }
00220 #endif
00221
00222
00223
00224 baseAddr = block->baseAddr;
00225 }
00226
00227 #ifdef MEMORY_INFO
00228
00229 AddrToBlock(baseAddr)->allocTrace = GetStackTraceIndex(2);
00230 #endif
00231
00232
00233
00234
00235
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
00248
00249 HeapBlock *block = AddrToBlock(item);
00250 if (block) {
00251
00252 #ifdef _DEBUG
00253
00254
00255
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;
00262 }
00263
00264
00265 m_megamap[megamapIndex++] = 0;
00266 }
00267 #endif
00268
00269
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
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
00294
00295
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
00319 if(decommitSize < (size_t)kMinHeapIncrement)
00320 return;
00321
00322
00323
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
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
00351 HeapBlock *nextBlock = newBlock + newBlock->size;
00352 nextBlock->sizePrevious = newBlock->size;
00353
00354
00355
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
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
00412
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
00423 block = freelist;
00424 #else
00425
00426
00427 Region *region = AddrToRegion(block->baseAddr);
00428 if(block->baseAddr == region->baseAddr &&
00429 region->commitTop == block->baseAddr + block->size*kBlockSize) {
00430
00431 RemoveFromList(block);
00432
00433
00434 HeapBlock *newBlocks = new HeapBlock[blocksLen - block->size];
00435
00436
00437 memcpy(newBlocks, blocks, (block - blocks) * sizeof(HeapBlock));
00438
00439
00440 size_t lastChunkSize = (char*)(blocks + blocksLen) - (char*)(block + block->size);
00441 memcpy(newBlocks + (block - blocks), block + block->size, lastChunkSize);
00442
00443
00444
00445
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
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
00485 block = freelist;
00486 }
00487 #endif
00488 }
00489 }
00490 #endif
00491 }
00492
00493 GCHeap::Region *GCHeap::AddrToRegion(const void *item) const
00494 {
00495
00496
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
00530 HeapBlock *block = freelist;
00531 while ((block = block->next) != freelist)
00532 {
00533 if (block->size >= size && block->committed)
00534 {
00535
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
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
00561
00562 if(!block->committed && !decommittedSuitableBlock)
00563 {
00564 int totalSize = block->size;
00565
00566
00567 HeapBlock *firstFree = block;
00568
00569
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
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
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
00615 if(decommittedSuitableBlock->size > toCommit) {
00616 HeapBlock *newBlock = Split(decommittedSuitableBlock, toCommit);
00617
00618
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
00630 {
00631
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
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
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
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
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
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
00778
00779
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
00809 memset(block->baseAddr, 0xfb, block->size * kBlockSize);
00810 #ifdef MEMORY_INFO
00811 block->freeTrace = GetStackTraceIndex(2);
00812 #endif
00813 #endif
00814
00815
00816 HeapBlock *prevBlock = block - block->sizePrevious;
00817 if (!prevBlock->inUse() && prevBlock->committed) {
00818 GCAssert(m_megamap[((uint32)(uintptr)prevBlock->baseAddr)>>12] == 0);
00819
00820 RemoveFromList(prevBlock);
00821
00822
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
00833 HeapBlock *nextBlock = block + block->size;
00834
00835 if (!nextBlock->inUse() && nextBlock->committed) {
00836
00837 RemoveFromList(nextBlock);
00838
00839
00840 block->size += nextBlock->size;
00841 nextBlock->size = 0;
00842 nextBlock->baseAddr = 0;
00843 nextBlock->sizePrevious = 0;
00844 }
00845
00846
00847 nextBlock = block + block->size;
00848 nextBlock->sizePrevious = block->size;
00849
00850
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
00862
00863 GCAcquireSpinlock spinlock(m_spinlock);
00864 #endif
00865 return ExpandHeapPrivate(askSize);
00866 }
00867
00868 bool GCHeap::ExpandHeapPrivate(int askSize)
00869 {
00870 int size = askSize;
00871 #ifdef _DEBUG
00872
00873 bool debug_noncontiguous = false;
00874
00875
00876
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
00893 if (size < kMinHeapIncrement) {
00894 size = kMinHeapIncrement;
00895 }
00896
00897
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
00910
00911 if (size <= commitAvail) {
00912 if (CommitMemory(lastRegion->commitTop, size * kBlockSize))
00913 {
00914
00915 baseAddr = lastRegion->commitTop;
00916 contiguous = true;
00917
00918
00919 lastRegion->commitTop += size*kBlockSize;
00920
00921
00922 goto gotMemory;
00923 }
00924 else
00925 {
00926
00927
00928 return false;
00929 }
00930 }
00931
00932
00933
00934
00935
00936 if (defaultReserve > size) {
00937 newRegionAddr = ReserveMemory(lastRegion->reserveTop,
00938 defaultReserve * kBlockSize);
00939 newRegionSize = defaultReserve;
00940 }
00941
00942
00943
00944
00945 if (newRegionAddr == NULL) {
00946 newRegionAddr = ReserveMemory(lastRegion->reserveTop,
00947 (size - commitAvail)*kBlockSize);
00948 newRegionSize = size - commitAvail;
00949 }
00950
00951 if (newRegionAddr != NULL) {
00952
00953
00954
00955 if (commitAvail != 0) {
00956 if (!CommitMemory(lastRegion->commitTop, commitAvail * kBlockSize))
00957 {
00958
00959
00960 ReleaseMemory(newRegionAddr, newRegionSize);
00961 return false;
00962 }
00963 }
00964
00965
00966 if (!CommitMemory(newRegionAddr, (size - commitAvail) * kBlockSize))
00967 {
00968
00969
00970
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
00981
00982 baseAddr = lastRegion->commitTop;
00983 lastRegion->commitTop = lastRegion->reserveTop;
00984 contiguous = true;
00985
00986 goto gotMemory;
00987 }
00988 }
00989
00990
00991
00992
00993
00994
00995 commitAvail = 0;
00996
00997
00998
00999 if (size < defaultReserve) {
01000 newRegionAddr = ReserveMemory(NULL,
01001 defaultReserve*kBlockSize);
01002 newRegionSize = defaultReserve;
01003 }
01004
01005
01006
01007 if (newRegionAddr == NULL) {
01008 newRegionAddr = ReserveMemory(NULL,
01009 size*kBlockSize);
01010 newRegionSize = size;
01011 }
01012
01013
01014 if (newRegionAddr == NULL) {
01015 return false;
01016 }
01017
01018
01019 if (CommitMemory(newRegionAddr,
01020 size*kBlockSize) == 0)
01021 {
01022
01023 ReleaseMemory(newRegionAddr, newRegionSize*kBlockSize);
01024 return false;
01025 }
01026
01027
01028
01029 baseAddr = newRegionAddr;
01030 contiguous = false;
01031
01032 gotMemory:
01033
01034 #else
01035
01036 newRegionAddr = AllocateMemory(size * kBlockSize);
01037 baseAddr = newRegionAddr;
01038 newRegionSize = size;
01039
01040
01041 if (newRegionAddr == NULL) {
01042 return false;
01043 }
01044 #endif
01045
01046
01047
01048 if (contiguous) {
01049 blocksLen--;
01050 }
01051
01052
01053 int newBlocksLen = blocksLen + size;
01054
01055
01056 newBlocksLen++;
01057
01058 HeapBlock *newBlocks = new HeapBlock[newBlocksLen];
01059 if (!newBlocks) {
01060
01061 #ifdef USE_MMAP
01062 ReleaseMemory(newRegionAddr, newRegionSize);
01063 #else
01064 ReleaseMemory(newRegionAddr);
01065 #endif
01066 return false;
01067 }
01068
01069
01070 if (blocksLen) {
01071 memcpy(newBlocks, blocks, blocksLen * sizeof(HeapBlock));
01072
01073
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
01091
01092 HeapBlock *block = newBlocks+blocksLen;
01093 block->baseAddr = baseAddr;
01094
01095 block->size = size;
01096 block->sizePrevious = 0;
01097
01098 if(blocksLen && contiguous)
01099 {
01100
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;
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
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
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
01161 if (blocks) {
01162 delete [] blocks;
01163 }
01164 blocks = newBlocks;
01165 blocksLen = newBlocksLen;
01166
01167
01168 if (newRegionAddr) {
01169 Region *newRegion = new Region;
01170 if (newRegion == NULL) {
01171
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
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
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
01275 if (!buf || b->size < buf->size)
01276 buf = b;
01277 }
01278 }
01279
01280
01281 if (buf)
01282 buf->free = false;
01283 }
01284
01285
01286
01287 if (!buf) return 0;
01288
01289
01290 if (buf->size >= size) return buf->addr;
01291
01292
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
01302 buf->size = 0;
01303 #ifdef GCHEAP_LOCK
01304 GCAcquireSpinlock lock(m_mirBufferLock);
01305 #endif
01306 buf->free = true;
01307 return 0;
01308 }
01309
01310 void GCHeap::ReleaseMirMemory(void* addr, size_t )
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);
01322 #endif
01323 if (b) b->free = true;
01324 }
01325
01326 size_t GCHeap::GetTotalHeapSize() const
01327 {
01328 size_t size = 0;
01329
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 }