GC.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  *   leon.sha@sun.com
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 // for vsprintf
00041 #include <stdio.h>
00042 // For memset
00043 #include <string.h>
00044 
00045 #include "MMgc.h"
00046 #include "StaticAssert.h"
00047 
00048 #ifdef HAVE_STDARG
00049 #include <stdarg.h>
00050 #endif
00051 
00052 #ifdef MEMORY_INFO
00053 #if !defined(__MWERKS__)
00054 #if !defined(__ICC)
00055 #if !defined(UNDER_CE)
00056 #include <typeinfo>
00057 #endif
00058 #endif
00059 #endif
00060 #endif
00061 
00062 #ifdef _DEBUG
00063 #include "GCTests.h"
00064 #endif
00065 
00066 #ifdef DARWIN
00067 #include <mach/mach_time.h>
00068 #endif
00069 
00070 // get alloca for CleanStack
00071 #ifdef WIN32
00072 #include <malloc.h>
00073 #endif
00074 
00075 #if defined(_MAC)
00076 #include <alloca.h>
00077 #endif
00078 
00079 #ifdef UNIX
00080     #ifdef HAVE_ALLOCA_H
00081         #include <alloca.h>
00082     #else // HAVE_ALLOCA_H
00083         #include <stdlib.h>
00084     #endif // HAVE_ALLOCA_H
00085     #include <sys/time.h>
00086 #endif // UNIX
00087 
00088 #if defined(_MAC) && (defined(MMGC_IA32) || defined(MMGC_AMD64))
00089 #include <pthread.h>
00090 #endif
00091 
00092 #ifdef HAVE_PTHREAD_H
00093 #include <pthread.h>
00094 #endif // HAVE_PTHREAD_H
00095 
00096 #ifdef HAVE_PTHREAD_NP_H
00097 #include <pthread_np.h>
00098 #endif // HAVE_PTHREAD_NP_H
00099 
00100 #if defined(_MSC_VER) && defined(_DEBUG)
00101 // we turn on exceptions in DEBUG builds
00102 #pragma warning(disable:4291) // no matching operator delete found; memory will not be freed if initialization throws an exception
00103 #endif
00104 
00105 #ifdef FEATURE_SAMPLER
00106  //sampling support
00107 #include "avmplus.h"
00108 #else
00109 #define SAMPLE_FRAME(_x, _s) 
00110 #define SAMPLE_CHECK() 
00111 #endif
00112 
00113 #ifdef SOLARIS
00114 #include <ucontext.h>
00115 #include <sys/frame.h>
00116 #include <errno.h>
00117 #include <unistd.h>
00118 #include <sys/stack.h>
00119 extern "C" greg_t _getsp(void);
00120 #endif
00121 
00122 // Werner mode is a back pointer chain facility for Relase mode
00123 //#define WERNER_MODE
00124 
00125 #ifdef WERNER_MODE
00126     #include <typeinfo>
00127     #include <stdio.h>
00128     void *shouldGo;
00129     char statusBuffer[1024];
00130 #endif
00131 
00132 namespace MMgc
00133 {
00134 
00135 #ifdef MMGC_DRC
00136 
00137     // how many objects trigger a reap, should be high
00138     // enough that the stack scan time is noise but low enough
00139     // so that objects to away in a timely manner
00140     const int ZCT::ZCT_REAP_THRESHOLD = 512;
00141 
00142     // size of table in pages
00143     const int ZCT::ZCT_START_SIZE = 1;
00144 #endif
00145 
00146 #ifdef MMGC_64BIT
00147     const uintptr MAX_UINTPTR = 0xFFFFFFFFFFFFFFFF;
00148 #else
00149     const uintptr MAX_UINTPTR = 0xFFFFFFFF;
00150 #endif  
00151 
00152     // get detailed info on each size class allocators
00153     const bool dumpSizeClassState = false;
00154     
00159     const static int kFreeSpaceDivisor = 4;
00160 
00161     const static int kMaxIncrement = 4096;
00162 
00166     const static uint64 kIncrementalMarkDelayTicks = int(10 * GC::GetPerformanceFrequency() / 1000);
00167 
00168     const static uint64 kMarkSweepBurstTicks = 1515909; // 200 ms on a 2ghz machine
00169 
00170     // Size classes for our GC.  From 8 to 128, size classes are spaced
00171     // evenly 8 bytes apart.  The finest granularity we can achieve is
00172     // 8 bytes, as pointers must be 8-byte aligned thanks to our use
00173     // of the bottom 3 bits of 32-bit atoms for Special Purposes.
00174     // Above that, the size classes have been chosen to maximize the
00175     // number of items that fit in a 4096-byte block, given our block
00176     // header information.
00177     const int16 GC::kSizeClasses[kNumSizeClasses] = {
00178         8, 16, 24, 32, 40, 48, 56, 64, 72, 80, //0-9
00179         88, 96, 104, 112, 120, 128, 144, 160, 168, 176,  //10-19
00180         184, 192, 200, 216, 224, 240, 256, 280, 296, 328, //20-29
00181         352, 392, 432, 488, 560, 656, 784, 984, 1312, 1968 //30-39
00182     };
00183 
00184     /* kSizeClassIndex[] generated with this code:
00185         kSizeClassIndex[0] = 0;
00186         for (var i:int = 1; i < kNumSizeClasses; i++)
00187             for (var j:int = (kSizeClasses[i-1]>>3), n=(kSizeClasses[i]>>3); j < n; j++)
00188                 kSizeClassIndex[j] = i;
00189     */
00190 
00191     // index'd by size>>3 - 1
00192     const uint8 GC::kSizeClassIndex[246] = {
00193         0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
00194         10, 11, 12, 13, 14, 15, 16, 16, 17, 17,
00195         18, 19, 20, 21, 22, 23, 23, 24, 25, 25,
00196         26, 26, 27, 27, 27, 28, 28, 29, 29, 29,
00197         29, 30, 30, 30, 31, 31, 31, 31, 31, 32,
00198         32, 32, 32, 32, 33, 33, 33, 33, 33, 33,
00199         33, 34, 34, 34, 34, 34, 34, 34, 34, 34,
00200         35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
00201         35, 35, 36, 36, 36, 36, 36, 36, 36, 36,
00202         36, 36, 36, 36, 36, 36, 36, 36, 37, 37,
00203         37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
00204         37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
00205         37, 37, 37, 38, 38, 38, 38, 38, 38, 38,
00206         38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
00207         38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
00208         38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
00209         38, 38, 38, 38, 39, 39, 39, 39, 39, 39,
00210         39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
00211         39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
00212         39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
00213         39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
00214         39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
00215         39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
00216         39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
00217         39, 39, 39, 39, 39, 39  
00218     };
00219     const size_t kLargestAlloc = 1968;
00220 
00221 #ifdef MMGC_THREADSAFE
00222 #define USING_CALLBACK_LIST(gc)  GCAcquireSpinlock _cblock((gc)->m_callbackListLock)
00223 #define USING_PAGE_MAP()         GCAcquireSpinlock _pmlock(pageMapLock)
00224 #else
00225 #define USING_CALLBACK_LIST(gc)  ((gc)->CheckThread())
00226 #define USING_PAGE_MAP()         ((void) 0)
00227 #endif
00228 
00229     GC::GC(GCHeap *gcheap)
00230         :
00231 #ifdef MMGC_DRC
00232           zct(),
00233 #endif
00234           nogc(false),
00235           greedy(false),
00236           findUnmarkedPointers(false),
00237           validateDefRef(false),
00238           keepDRCHistory(false),
00239           gcstats(false),
00240 #ifdef WRITE_BARRIERS
00241           incremental(true),
00242 #else
00243           incremental(false),
00244 #endif
00245           incrementalValidation(false),
00246 #ifdef _DEBUG
00247           // check for missing write barriers at every Alloc
00248           incrementalValidationPedantic(false),
00249 #endif
00250 
00251           marking(false),
00252           memStart(MAX_UINTPTR),
00253           memEnd(0),
00254           heap(gcheap),
00255           allocsSinceCollect(0),
00256           collecting(false),
00257           m_roots(0),
00258           m_callbacks(0),
00259           markTicks(0),
00260           bytesMarked(0),
00261           markIncrements(0),
00262           marks(0),
00263           sweeps(0),
00264           totalGCPages(0),
00265           stackCleaned(true),
00266           rememberedStackTop(0),
00267           destroying(false),
00268           lastMarkTicks(0),
00269           lastSweepTicks(0),
00270           lastStartMarkIncrementCount(0),
00271           t0(GetPerformanceCounter()),
00272           dontAddToZCTDuringCollection(false),
00273           numObjects(0),
00274           hitZeroObjects(false),
00275           smallEmptyPageList(NULL),
00276           largeEmptyPageList(NULL),
00277           sweepStart(0),
00278           heapSizeAtLastAlloc(gcheap->GetTotalHeapSize()),
00279           finalizedValue(true),
00280           // Expand, don't collect, until we hit this threshold
00281           collectThreshold(256),
00282 #if MMGC_THREADSAFE
00283           m_exclusiveGCThread(NULL),
00284           m_gcRunning(false),
00285           m_condDone(m_lock),
00286           m_requestCount(0),
00287           m_condNoRequests(m_lock)
00288 #else
00289           disableThreadCheck(false)
00290 #endif
00291     {
00292         // sanity check for all our types
00293         MMGC_STATIC_ASSERT(sizeof(int8) == 1);
00294         MMGC_STATIC_ASSERT(sizeof(uint8) == 1);     
00295         MMGC_STATIC_ASSERT(sizeof(int16) == 2);
00296         MMGC_STATIC_ASSERT(sizeof(uint16) == 2);
00297         MMGC_STATIC_ASSERT(sizeof(int32) == 4);
00298         MMGC_STATIC_ASSERT(sizeof(uint32) == 4);
00299         MMGC_STATIC_ASSERT(sizeof(int64) == 8);
00300         MMGC_STATIC_ASSERT(sizeof(uint64) == 8);
00301         MMGC_STATIC_ASSERT(sizeof(sintptr) == sizeof(void *));
00302         MMGC_STATIC_ASSERT(sizeof(uintptr) == sizeof(void *));
00303         #ifdef MMGC_64BIT
00304         MMGC_STATIC_ASSERT(sizeof(sintptr) == 8);
00305         MMGC_STATIC_ASSERT(sizeof(uintptr) == 8);   
00306         #else   
00307         MMGC_STATIC_ASSERT(sizeof(sintptr) == 4);
00308         MMGC_STATIC_ASSERT(sizeof(uintptr) == 4);   
00309         #endif      
00310     
00311         {
00312             GCWorkItem item;
00313             MMGC_GET_STACK_EXTENTS(this, item.ptr, item._size);
00314             rememberedStackBottom =  (const void *)((const char *)item.ptr + item._size);
00315         }
00316 
00317 #ifdef MMGC_DRC
00318         zct.SetGC(this);
00319 #endif
00320         // Create all the allocators up front (not lazy)
00321         // so that we don't have to check the pointers for
00322         // NULL on every allocation.
00323         for (int i=0; i<kNumSizeClasses; i++) {         
00324             containsPointersAllocs[i] = new GCAlloc(this, kSizeClasses[i], true, false, i);
00325 #ifdef MMGC_DRC
00326             containsPointersRCAllocs[i] = new GCAlloc(this, kSizeClasses[i], true, true, i);
00327 #endif
00328             noPointersAllocs[i] = new GCAlloc(this, kSizeClasses[i], false, false, i);
00329         }
00330         
00331         largeAlloc = new GCLargeAlloc(this);
00332 
00333         pageMap = (unsigned char*) heapAlloc(1);
00334 
00335         memset(m_bitsFreelists, 0, sizeof(uint32*) * kNumSizeClasses);
00336         m_bitsNext = (uint32*)heapAlloc(1);
00337 
00338         // precondition for emptyPageList 
00339         GCAssert(offsetof(GCLargeAlloc::LargeBlock, next) == offsetof(GCAlloc::GCBlock, next));
00340 
00341         
00342         for(int i=0; i<GCV_COUNT; i++)
00343         {
00344             SetGCContextVariable(i, NULL);
00345         }
00346 
00347 #ifdef _DEBUG
00348         // this doens't hurt performance too much so alway leave it on in DEBUG builds
00349         // before sweeping we check for missing write barriers
00350         incrementalValidation = true;
00351 #ifdef WIN32
00352         m_gcThread = GetCurrentThreadId();
00353 #endif
00354 #endif
00355 
00356         // keep GC::Size honest
00357         GCAssert(offsetof(GCLargeAlloc::LargeBlock, usableSize) == offsetof(GCAlloc::GCBlock, size));
00358 
00359 #ifndef MMGC_PORTING_API
00360 #ifndef MMGC_ARM // TODO MMGC_ARM
00361 #ifdef _DEBUG
00362         if (!nogc)
00363             RunGCTests(this);
00364 #endif
00365 #endif
00366 #endif
00367 
00368         // keep GC::Size honest
00369         GCAssert(offsetof(GCLargeAlloc::LargeBlock, usableSize) == offsetof(GCAlloc::GCBlock, size));
00370 
00371     }
00372 
00373     GC::~GC()
00374     {
00375         // Force all objects to be destroyed
00376         destroying = true;
00377         ClearMarks();
00378         ForceSweep();
00379 
00380         // Go through m_bitsFreelist and collect list of all pointers
00381         // that are on page boundaries into new list, pageList
00382         void **pageList = NULL;
00383         for(int i=0, n=kNumSizeClasses; i<n; i++) {
00384             uint32* bitsFreelist = m_bitsFreelists[i];
00385             while(bitsFreelist) {
00386                 uint32 *next = *(uint32**)bitsFreelist;
00387                 if(((uintptr)bitsFreelist & 0xfff) == 0) {
00388                     *((void**)bitsFreelist) = pageList;
00389                     pageList = (void**)bitsFreelist;
00390                 }
00391                 bitsFreelist = next;
00392             } 
00393         }
00394         
00395         // Go through page list and free all pages on it
00396         while (pageList) {
00397             void **next = (void**) *pageList;
00398             heap->Free((void*)pageList);
00399             pageList = next;
00400         }
00401 
00402         for (int i=0; i < kNumSizeClasses; i++) {
00403             delete containsPointersAllocs[i];
00404 #ifdef MMGC_DRC
00405             delete containsPointersRCAllocs[i];
00406 #endif
00407             delete noPointersAllocs[i];
00408         }
00409 
00410         if (largeAlloc) {
00411             delete largeAlloc;
00412         }
00413 
00414         // dtors for each GCAlloc will use this
00415         pageList = NULL;
00416 
00417         heap->Free(pageMap);
00418 
00419 #ifndef MMGC_THREADSAFE
00420         CheckThread();
00421 #endif
00422 
00423         GCAssert(!m_roots);
00424         GCAssert(!m_callbacks);
00425 
00426         {
00427             GCSpinLock lock(m_rootListLock);
00428             // apparently the player can't be made to clean up so keep it from crashing at least
00429             while(m_roots) {
00430                 m_roots->Destroy();
00431             }
00432         }
00433 
00434         while(m_callbacks) {
00435             m_callbacks->Destroy();         
00436         }
00437     }
00438 
00439     void GC::Collect()
00440     {
00441         CollectWithBookkeeping(false, false);
00442     }
00443 
00444     void GC::CollectWithBookkeeping(bool callerHoldsLock,
00445                                     bool callerHasActiveRequest)
00446     {
00447 #ifdef MMGC_THREADSAFE
00448         GCAssert(callerHoldsLock == m_lock.IsHeld());
00449 #ifdef _DEBUG
00450         GCAssert(callerHasActiveRequest == GCThread::GetCurrentThread()->IsInActiveRequest());
00451 #endif
00452 #else
00453         (void) callerHoldsLock;
00454         (void) callerHasActiveRequest;
00455         CheckThread();
00456 #endif
00457 
00458 #ifdef MMGC_THREADSAFE
00459         if (!callerHoldsLock)
00460             m_lock.Acquire();
00461 
00462         if (nogc || m_gcRunning || zct.reaping) {
00463             if (!callerHoldsLock)
00464                 m_lock.Release();
00465             return;
00466         }
00467 #else
00468         if (nogc || collecting || zct.reaping) {
00469             return;
00470         }
00471 #endif
00472 
00473 #ifdef MMGC_THREADSAFE
00474         GCThread *thisThread = GCThread::GetCurrentThread();
00475         if (m_exclusiveGCThread != NULL) {
00476             // Someone is already collecting or waiting to collect.
00477             //
00478             // If it's the current thread, then we're being called
00479             // recursively.  This maybe shouldn't happen, but Collect can be
00480             // called indirectly from a finalizer.  Don't recurse, just ignore
00481             // it.
00482             //
00483             // If it's some other thread, wait for that thread to finish.
00484             //
00485             if (m_exclusiveGCThread != thisThread) {
00486                 GCRoot *stackRoot;
00487                 void *stack;
00488                 size_t stackSize;
00489 
00490                 // Call this macro here, not under "if
00491                 // (callerHasActiveRequest)", because it introduces local
00492                 // variables that must survive for the whole lifetime of
00493                 // stackRoot.
00494                 MMGC_GET_STACK_EXTENTS(this, stack, stackSize);
00495 
00496                 // Before waiting, this thread must suspend any active
00497                 // requests.  This avoids a deadlock where m_exclusiveGCThread
00498                 // is waiting for m_requestCount to go to zero while we're
00499                 // waiting for m_exclusiveGCThread to be done.
00500                 //
00501                 // When we do this, we root the calling thread's stack; this
00502                 // way each collection scans the stack of every thread that is
00503                 // in a suspended request.
00504                 //
00505                 if (callerHasActiveRequest) {
00506                     stackRoot = new GCRoot(this, stack, stackSize);
00507 
00508                     OnLeaveRequestAlreadyLocked();
00509                 }
00510 
00511                 while (m_exclusiveGCThread != NULL)
00512                     m_condDone.Wait();
00513 
00514                 if (callerHasActiveRequest) {
00515                     // Resume previously suspended request.
00516                     delete stackRoot;
00517                     OnEnterRequestAlreadyLocked();
00518                 }
00519             }
00520 
00521             if (!callerHoldsLock)
00522                 m_lock.Release();
00523             return;
00524         }
00525 
00526         // If we get here, this thread will collect.
00527         m_exclusiveGCThread = thisThread;
00528         if (callerHasActiveRequest) {
00529             // We must suspend any active requests on this thread.
00530             OnLeaveRequestAlreadyLocked();
00531         }
00532         // Wait for other threads to suspend or end their active requests,
00533         // if any.
00534         while (m_requestCount > 0)
00535             m_condNoRequests.Wait();
00536 
00537         // These should not have changed while we were waiting, even though we
00538         // released the lock.
00539         GCAssert(m_requestCount == 0);
00540         GCAssert(m_exclusiveGCThread == thisThread);
00541 
00542         // This thread has acquired exclusiveGC.
00543         m_gcRunning = true;
00544 #endif
00545 
00546         {
00547             USING_CALLBACK_LIST(this);
00548             for (GCCallback *cb = m_callbacks; cb; cb = cb->nextCB)
00549                 cb->enterExclusiveGC();
00550         }
00551 
00552 #ifdef MMGC_THREADSAFE
00553         m_lock.Release();
00554 #endif
00555 
00556         {
00557             USING_CALLBACK_LIST(this);
00558             for (GCCallback *cb = m_callbacks; cb; cb = cb->nextCB)
00559                 cb->enterExclusiveGCNoLock();
00560         }
00561 
00562         CollectImpl();
00563 
00564 #ifdef MMGC_THREADSAFE
00565         m_lock.Acquire();
00566 
00567         // These should not have changed during collection,
00568         // even though we released the lock.
00569         GCAssert(m_requestCount == 0);
00570         GCAssert(m_exclusiveGCThread == thisThread);
00571 #endif
00572 
00573         {
00574             USING_CALLBACK_LIST(this);
00575             for (GCCallback *cb = m_callbacks; cb; cb = cb->nextCB)
00576                 cb->leaveExclusiveGC();
00577         }
00578 
00579 #ifdef MMGC_THREADSAFE
00580         // This thread is relinquishing exclusiveGC.
00581         m_gcRunning = false;
00582 
00583         m_exclusiveGCThread = NULL;
00584         if (callerHasActiveRequest)
00585             OnEnterRequestAlreadyLocked();
00586 
00587         m_condDone.NotifyAll();
00588 
00589         if (!callerHoldsLock)
00590             m_lock.Release();
00591 #endif
00592     }
00593 
00594     void GC::CollectImpl()
00595     {
00596 #ifndef MMGC_THREADSAFE
00597         ReapZCT();
00598 #endif
00599 
00600         if(!marking)
00601             StartIncrementalMark();
00602         if(marking)
00603             FinishIncrementalMark();
00604 
00605 #ifdef _DEBUG
00606         // Dumping the stack trace at GC time can be very helpful with stack walk bugs
00607         // where something was created, stored away in untraced, unmanaged memory and not 
00608         // reachable by the conservative stack walk
00609         //DumpStackTrace();
00610         FindUnmarkedPointers();
00611 #endif
00612     }
00613 
00614 #ifdef _DEBUG
00615     void GC::Trace(const void *stackStart/*=NULL*/, uint32 stackSize/*=0*/)
00616     {
00617         MMGC_ASSERT_EXCLUSIVE_GC(this);
00618 
00619         SAMPLE_FRAME("[mark]", core());
00620 
00621         // Clear all mark bits.
00622         ClearMarks();
00623 
00624         SAMPLE_CHECK();
00625 
00626         GCStack<GCWorkItem> work;
00627         {
00628 #ifdef GCHEAP_LOCK
00629             GCAcquireSpinlock lock(m_rootListLock);
00630 #endif
00631             GCRoot *r = m_roots;
00632             while(r) {
00633                 GCWorkItem item = r->GetWorkItem();
00634                 MarkItem(item, work);
00635                 r = r->next;
00636             }
00637         }
00638 
00639         SAMPLE_CHECK();
00640 
00641         if(stackStart == NULL) {
00642             MarkQueueAndStack(work);
00643         } else {
00644             GCWorkItem item(stackStart, stackSize, false);
00645             PushWorkItem(work, item);
00646             Mark(work);
00647         }
00648         
00649         SAMPLE_CHECK();
00650 
00651         ClearMarks();
00652     }
00653 #endif
00654 
00655     void *GC::Alloc(size_t size, int flags/*0*/)
00656     {
00657 #ifdef MMGC_THREADSAFE
00658         GCAutoLock _lock(m_lock);
00659         GCAssert(!m_gcRunning);
00660 #else
00661         CheckThread();
00662 #endif
00663         return AllocAlreadyLocked(size, flags);
00664     }
00665 
00666     void *GC::AllocAlreadyLocked(size_t size, int flags/*0*/)
00667     {
00668 #ifdef FEATURE_SAMPLER
00669         avmplus::AvmCore *core = (avmplus::AvmCore*)GetGCContextVariable(GCV_AVMCORE);
00670         if(core)
00671             core->sampleCheck();
00672 #endif
00673         GCAssertMsg(size > 0, "cannot allocate a 0 sized block\n");
00674 
00675 #ifdef _DEBUG
00676         GCAssert(size + 7 > size);
00677         if (GC::greedy) {
00678             CollectWithBookkeeping(true, true);
00679         }
00680         // always be marking in pedantic mode
00681         if(incrementalValidationPedantic) {
00682             if(!marking)
00683                 StartIncrementalMark();
00684         }
00685 #endif
00686 
00687         // overflow detection
00688         if(size+7 < size)
00689             return NULL;
00690 
00691         size = (size+7)&~7; // round up to multiple of 8
00692 
00693         size += DebugSize();
00694 
00695         GCAssertMsg(size > 0, "debug overflow, adding space for Debug stuff overflowed size_t\n");
00696     
00697         GCAlloc **allocs = noPointersAllocs;
00698 #ifdef MMGC_DRC
00699         if(flags & kRCObject) {
00700             allocs = containsPointersRCAllocs;
00701         } else 
00702 #endif
00703         if(flags & kContainsPointers) {
00704             allocs = containsPointersAllocs;
00705         }
00706 
00707         void *item;
00708         // Buckets up to 128 are spaced evenly at 8 bytes.
00709         if (size <= 128) {
00710             GCAlloc *b = (GCAlloc*)allocs[(size >> 3) - 1];
00711             GCAssert(size <= b->GetItemSize());
00712             item = b->Alloc(size, flags);
00713         } else if (size <= kLargestAlloc) {             
00714             // This is the fast lookup table implementation to
00715             // find the right allocator.
00716             unsigned index = kSizeClassIndex[(size>>3)-1];
00717 
00718             // assert that I fit 
00719             GCAssert(size <= allocs[index]->GetItemSize());
00720 
00721             // assert that I don't fit (makes sure we don't waste space)
00722             GCAssert(size > allocs[index-1]->GetItemSize());
00723 
00724             item = allocs[index]->Alloc(size, flags);
00725         } else {
00726             item = largeAlloc->Alloc(size, flags);
00727         }
00728 
00729 #ifdef MEMORY_INFO
00730         if(item)
00731         item = DebugDecorate(item,  GC::Size(GetUserPointer(item)) + DebugSize(), 3);
00732 #endif
00733 
00734 #ifdef _DEBUG
00735         bool shouldZero = (flags & kZero) || incrementalValidationPedantic;
00736         
00737         // in debug mode memory is poisoned so we have to clear it here
00738         // in release builds memory is zero'd to start and on free/sweep
00739         // in pedantic mode uninitialized data can trip the write barrier 
00740         // detector, only do it for pedantic because otherwise we want the
00741         // mutator to get the poisoned data so it crashes if it relies on 
00742         // uninitialized values
00743         if((item) && (shouldZero)) {
00744             memset(item, 0, Size(item));
00745         }
00746 #endif
00747 
00748         SAMPLE_ALLOC(item, GC::Size(item));
00749 
00750         return item;
00751     }
00752 
00753     void GC::MaybeGC(bool callerHasActiveRequest)
00754     {
00755         if (greedy) {
00756             CollectWithBookkeeping(false, callerHasActiveRequest);
00757         } else if (marking) {
00758             IncrementalMark();
00759         } else {
00760 #ifdef MMGC_THREADSAFE
00761             GCAutoLock _lock(m_lock);
00762 #endif
00763 
00764             // Burst logic to prevent collections from happening back to back.
00765             uint64 now = GetPerformanceCounter();
00766             if (now - lastSweepTicks <= kMarkSweepBurstTicks)
00767                 return;
00768 
00769             // Definitely start GC if the heap expanded due to FixedMalloc
00770             // allocations.  The same heuristic applies to incremental and
00771             // non-incremental.
00772             bool force = (heapSizeAtLastAlloc > collectThreshold &&
00773                           heapSizeAtLastAlloc < heap->GetTotalHeapSize());
00774 
00775             if (incremental) {
00776                 if (force || (totalGCPages > collectThreshold &&
00777                               allocsSinceCollect * kFreeSpaceDivisor >= totalGCPages)) {
00778                     StartIncrementalMark();
00779                 }
00780             } else {
00781                 // Collect only if the heap is completely full (a conservative
00782                 // heuristic).
00783                 if (force || heap->GetFreeHeapSize() == 0)
00784                     CollectWithBookkeeping(true, callerHasActiveRequest);
00785             }
00786         }
00787     }
00788 
00789     void *GC::Calloc(size_t num, size_t elsize, int flags)
00790     {
00791         uint64 size = (uint64)num * (uint64)elsize;
00792         if(size > 0xfffffff0) 
00793         {
00794             GCAssertMsg(false, "Attempted allocation overflows size_t\n");
00795             return NULL;
00796         }
00797         return Alloc(num * elsize, flags);
00798     }
00799 
00800     void GC::Free(const void *item)
00801     {
00802 #ifdef MMGC_THREADSAFE
00803         GCAutoLock _lock(m_lock);
00804         GCAssert(!m_gcRunning);
00805 #else
00806         CheckThread();
00807 #endif
00808 
00809         if(item == NULL) {
00810             return;
00811         }
00812 
00813         bool isLarge;
00814 
00815         SAMPLE_DEALLOC(GetRealPointer(item), GC::Size(item));
00816 
00817         // we can't allow free'ing something during Sweeping, otherwise alloc counters
00818         // get decremented twice and destructors will be called twice.
00819         if(collecting) {
00820             goto bail;
00821         }
00822 
00823         isLarge = GCLargeAlloc::IsLargeBlock(GetRealPointer(item));
00824 
00825         if (marking) {
00826             // if its on the work queue don't delete it, if this item is
00827             // really garbage we're just leaving it for the next sweep
00828             if(IsQueued(item)) 
00829                 goto bail;
00830         }
00831 
00832 #ifdef _DEBUG
00833 #ifdef MMGC_DRC
00834         // RCObject have constract that they must clean themselves, since they 
00835         // have to scan themselves to decrement other RCObjects they might as well
00836         // clean themselves too, better than suffering a memset later
00837         if(isLarge ? GCLargeAlloc::IsRCObject(item) : GCAlloc::IsRCObject(item))
00838         {
00839              RCObjectZeroCheck((RCObject*)item);
00840         }
00841 #endif // MMGC_DRC
00842 #endif
00843 
00844 #ifdef MEMORY_INFO
00845         DebugFree(item, 0xca, 4);
00846 #endif
00847     
00848         if (isLarge) {
00849             largeAlloc->Free(GetRealPointer(item));
00850         } else {
00851             GCAlloc::Free(GetRealPointer(item));
00852         }
00853         return;
00854 
00855 bail:
00856 
00857         // this presumes we got here via delete, maybe we should have
00858         // delete call something other than the public Free to distinguish
00859         if(IsFinalized(item))
00860             ClearFinalized(item);
00861         if(HasWeakRef(item))
00862             ClearWeakRef(item);
00863     }
00864 
00865     /*static*/ int GC::GetMark(const void *item)
00866     {
00867         item = GetRealPointer(item);
00868         if (GCLargeAlloc::IsLargeBlock(item)) {
00869             return GCLargeAlloc::GetMark(item);
00870         } else {
00871             return GCAlloc::GetMark(item);
00872         }
00873     }
00874 
00875     void GC::ClearMarks()
00876     {
00877         MMGC_ASSERT_EXCLUSIVE_GC(this);
00878 
00879         for (int i=0; i < kNumSizeClasses; i++) {
00880 #ifdef MMGC_DRC
00881             containsPointersRCAllocs[i]->ClearMarks();
00882 #endif
00883             containsPointersAllocs[i]->ClearMarks();
00884             noPointersAllocs[i]->ClearMarks();
00885         }
00886         largeAlloc->ClearMarks();
00887     }
00888 
00889     void GC::Finalize()
00890     {
00891         MMGC_ASSERT_EXCLUSIVE_GC(this);
00892 
00893         for(int i=0; i < kNumSizeClasses; i++) {
00894 #ifdef MMGC_DRC
00895             containsPointersRCAllocs[i]->Finalize();
00896 #endif
00897             containsPointersAllocs[i]->Finalize();
00898             noPointersAllocs[i]->Finalize();
00899         }
00900         largeAlloc->Finalize();
00901         finalizedValue = !finalizedValue;
00902 
00903         
00904         for(int i=0; i < kNumSizeClasses; i++) {
00905 #ifdef MMGC_DRC
00906             containsPointersRCAllocs[i]->m_finalized = false;
00907 #endif
00908             containsPointersAllocs[i]->m_finalized = false;
00909             noPointersAllocs[i]->m_finalized = false;
00910         }
00911     }
00912 
00913     void GC::Sweep(bool force)
00914     {   
00915         MMGC_ASSERT_EXCLUSIVE_GC(this);
00916 
00917         // collecting must be true because it indicates allocations should
00918         // start out marked, we can't rely on write barriers below since 
00919         // presweep could write a new GC object to a root
00920         collecting = true;
00921 
00922         SAMPLE_FRAME("[sweep]", core());
00923         sweeps++;
00924 
00925         size_t heapSize = heap->GetUsedHeapSize();
00926 
00927 #ifdef MEMORY_INFO
00928         if(heap->enableMemoryProfiling) {
00929             GCDebugMsg(false, "Pre sweep memory info:\n");
00930             DumpMemoryInfo();
00931         }
00932 #endif
00933         
00934 
00935         // invoke presweep on all callbacks
00936         {
00937             USING_CALLBACK_LIST(this);
00938             GCCallback *cb = m_callbacks;
00939             while(cb) {
00940                 cb->presweep();
00941                 cb = cb->nextCB;
00942             }
00943         }
00944 
00945         SAMPLE_CHECK();
00946 
00947         // if force is true we're being called from ~GC and this isn't necessary
00948         if(!force) {
00949             // we just executed mutator code which could have fired some WB's
00950             Mark(m_incrementalWork);
00951         }
00952 
00953         Finalize();
00954 
00955         // if force is true we're being called from ~GC and this isn't necessary
00956         if(!force) {
00957             // we just executed mutator code which could have fired some WB's
00958             Mark(m_incrementalWork);
00959         }
00960     
00961         SAMPLE_CHECK();
00962         // ISSUE: this could be done lazily at the expense other GC's potentially expanding
00963         // unnecessarily, not sure its worth it as this should be pretty fast
00964         GCAlloc::GCBlock *b = smallEmptyPageList;
00965         while(b) {
00966             GCAlloc::GCBlock *next = b->next;
00967 #ifdef _DEBUG
00968             b->alloc->SweepGuts(b);
00969 #endif
00970             b->alloc->FreeChunk(b);
00971             b = next;
00972         }
00973         smallEmptyPageList = NULL;
00974 
00975         SAMPLE_CHECK();
00976 
00977         GCLargeAlloc::LargeBlock *lb = largeEmptyPageList;      
00978         while(lb) {
00979             GCLargeAlloc::LargeBlock *next = lb->next;
00980 #ifdef MEMORY_INFO
00981             DebugFreeReverse(lb+1, 0xba, 3);
00982 #endif
00983             // FIXME: this makes for some chatty locking, maybe not a problem?
00984             FreeBlock(lb, lb->GetNumBlocks());
00985             lb = next;
00986         }
00987         largeEmptyPageList = NULL;
00988 
00989         SAMPLE_CHECK();
00990 
00991 #ifdef MEMORY_INFO
00992         if(heap->enableMemoryProfiling) {           
00993             GCDebugMsg(false, "Post sweep memory info:\n");
00994             DumpMemoryInfo();
00995         }
00996 #endif
00997 
00998         // don't want postsweep to fire WB's
00999         marking = false;
01000         collecting = false;
01001 
01002         // invoke postsweep callback
01003         {
01004             USING_CALLBACK_LIST(this);
01005             GCCallback *cb = m_callbacks;
01006             while(cb) {
01007                 cb->postsweep();
01008                 cb = cb->nextCB;
01009             }
01010         }
01011 
01012         SAMPLE_CHECK();
01013 
01014         allocsSinceCollect = 0;
01015         lastSweepTicks = GetPerformanceCounter();
01016 
01017         if(GC::gcstats) {
01018             int sweepResults = 0;
01019             GCAlloc::GCBlock *bb = smallEmptyPageList;
01020             while(bb) {
01021                 sweepResults++;
01022                 bb = bb->next;
01023             }
01024             
01025             GCLargeAlloc::LargeBlock *lbb = largeEmptyPageList;
01026             while(lbb) {
01027                 sweepResults += lbb->GetNumBlocks();
01028                 lbb = lbb->next;
01029             }
01030             // include large pages given back
01031             sweepResults += int(heapSize - heap->GetUsedHeapSize());
01032             double millis = duration(sweepStart);
01033             gclog("[mem] sweep(%d) reclaimed %d whole pages (%d kb) in %.2f millis (%.4f s)\n", 
01034                 sweeps, sweepResults, sweepResults*GCHeap::kBlockSize>>10, millis,
01035                 duration(t0)/1000);
01036         }
01037 #ifdef DEBUGGER
01038         StopGCActivity();
01039 #endif
01040 
01041 #ifdef MEMORY_INFO
01042         m_gcLastStackTrace = GetStackTraceIndex(5);
01043 #endif
01044     }
01045 
01046     void* GC::AllocBlock(int size, int pageType, bool zero)
01047     {
01048         MMGC_ASSERT_GC_LOCK(this);
01049 #ifdef DEBUGGER
01050         AllocActivity(size);
01051 #endif
01052         GCAssert(size > 0);
01053     
01054         // perform gc if heap expanded due to fixed memory allocations
01055         // utilize burst logic to prevent this from happening back to back
01056         // this logic is here to apply to incremental and non-incremental
01057         uint64 now = GetPerformanceCounter();
01058         if(!marking && !collecting &&
01059             heapSizeAtLastAlloc > collectThreshold &&
01060             now - lastSweepTicks > kMarkSweepBurstTicks && 
01061             heapSizeAtLastAlloc < heap->GetTotalHeapSize()) 
01062         {
01063             if(incremental && !nogc)
01064                 StartIncrementalMark();
01065             else
01066                 CollectWithBookkeeping(true, true);
01067         }
01068 
01069         void *item;
01070 
01071         if(incremental && !nogc)
01072             item = AllocBlockIncremental(size, zero);
01073         else
01074             item = AllocBlockNonIncremental(size, zero);
01075 
01076         if(!item) {             
01077             int incr = (int)(size + totalGCPages / kFreeSpaceDivisor);
01078             if(incr > kMaxIncrement) {
01079                 incr = size < kMaxIncrement ? kMaxIncrement : size;
01080             }
01081             heap->ExpandHeap(incr);
01082             item = heap->Alloc(size, false);
01083         }
01084 
01085         GCAssert(item != NULL);
01086         if (item != NULL)
01087         {
01088             allocsSinceCollect += size;
01089 
01090             // mark GC pages in page map, small pages get marked one,
01091             // the first page of large pages is 3 and the rest are 2
01092             MarkGCPages(item, 1, pageType);
01093             if(pageType == kGCLargeAllocPageFirst) {
01094                 MarkGCPages((char*)item+GCHeap::kBlockSize, size - 1, kGCLargeAllocPageRest);
01095             }
01096             totalGCPages += size;
01097         }
01098 
01099         // do this after any heap expansions from GC
01100         heapSizeAtLastAlloc = heap->GetTotalHeapSize();
01101 
01102         return item;
01103     }
01104 
01105     void* GC::AllocBlockIncremental(int size, bool zero)
01106     {
01107         MMGC_ASSERT_GC_LOCK(this);
01108 
01109         if(!collecting || incrementalValidation) {
01110             uint64 now = GetPerformanceCounter();
01111             if (marking) {      
01112                 if(now - lastMarkTicks > kIncrementalMarkDelayTicks) {
01113                     IncrementalMark();
01114                 }
01115             } else if (incrementalValidation ||
01116                 (totalGCPages > collectThreshold &&
01117                 allocsSinceCollect * kFreeSpaceDivisor >= totalGCPages &&
01118                 // burst detection
01119                 (now - lastSweepTicks > kMarkSweepBurstTicks))) {
01120                     StartIncrementalMark();
01121             }
01122         }
01123 
01124         void *item = heap->Alloc(size, false, zero);
01125         if(!item && !collecting) {
01126             if(marking) {
01127                 GCAssert(!nogc);
01128                 FinishIncrementalMark();
01129                 item = heap->Alloc(size, false, zero);
01130             }
01131         }
01132         return item;
01133     }
01134 
01135     void* GC::AllocBlockNonIncremental(int size, bool zero)
01136     {
01137         MMGC_ASSERT_GC_LOCK(this);
01138 
01139         void *item = heap->Alloc(size, false, zero);
01140         if (!item) {
01141             if (heap->GetTotalHeapSize() >= collectThreshold &&
01142                 allocsSinceCollect >= totalGCPages / kFreeSpaceDivisor) 
01143             {
01144                 CollectWithBookkeeping(true, true);
01145                 item = heap->Alloc(size, false, zero);
01146             }
01147         }
01148         return item;
01149     }
01150 
01151     void GC::FreeBlock(void *ptr, uint32 size)
01152     {
01153 #ifdef MMGC_THREADSAFE
01154         GCAssert(m_lock.IsHeld() || destroying
01155                  || (m_gcRunning
01156                      && m_exclusiveGCThread == GCThread::GetCurrentThread()));
01157 #endif
01158 #ifdef DEBUGGER
01159         AllocActivity(- (int)size);
01160 #endif
01161         if(!collecting) {
01162             allocsSinceCollect -= size;
01163         }
01164         totalGCPages -= size;
01165         heap->Free(ptr);
01166         UnmarkGCPages(ptr, size);
01167     }
01168 
01169     int GC::GetPageMapValueAlreadyLocked(uintptr addr) const
01170     {
01171         uintptr index = (addr-memStart) >> 12;
01172 
01173 #ifdef MMGC_AMD64
01174         GCAssert((index >> 2) < uintptr(64*65536) * uintptr(GCHeap::kBlockSize));
01175 #else
01176         GCAssert(index >> 2 < 64 * GCHeap::kBlockSize);
01177 #endif
01178         // shift amount to determine position in the byte (times 2 b/c 2 bits per page)
01179         uint32 shiftAmount = (index&0x3) * 2;
01180         // 3 ... is mask for 2 bits, shifted to the left by shiftAmount
01181         // finally shift back by shift amount to get the value 0, 1 or 3
01182         //return (pageMap[addr >> 2] & (3<<shiftAmount)) >> shiftAmount;
01183         return (pageMap[index >> 2] >> shiftAmount) & 3;
01184     }
01185 
01186     void GC::SetPageMapValue(uintptr addr, int val)
01187     {
01188         uintptr index = (addr-memStart) >> 12;
01189 #ifdef MMGC_AMD64
01190         GCAssert((index >> 2) < uintptr(64*65536) * uintptr(GCHeap::kBlockSize));
01191 #else
01192         GCAssert(index >> 2 < 64 * GCHeap::kBlockSize);
01193 #endif
01194         pageMap[index >> 2] |= (val<<((index&0x3)*2));
01195     }   
01196 
01197     void GC::ClearPageMapValue(uintptr addr)
01198     {
01199         uintptr index = (addr-memStart) >> 12;
01200 #ifdef MMGC_AMD64
01201         GCAssert((index >> 2) < uintptr(64*65536) * uintptr(GCHeap::kBlockSize));
01202 #else
01203         GCAssert((index >> 2) < 64 * GCHeap::kBlockSize);
01204 #endif
01205         pageMap[index >> 2] &= ~(3<<((index&0x3)*2));
01206     }   
01207 
01208     void GC::Mark(GCStack<GCWorkItem> &work)
01209     {
01210         MMGC_ASSERT_EXCLUSIVE_GC(this);
01211         while(work.Count()) {
01212             MarkItem(work);
01213         }
01214     }
01215 
01216     void GC::MarkGCPages(void *item, uint32 numPages, int to)
01217     {
01218         USING_PAGE_MAP();
01219         uintptr addr = (uintptr)item;
01220         size_t shiftAmount=0;
01221         unsigned char *dst = pageMap;
01222 
01223         // save the current live range in case we need to move/copy
01224         size_t numBytesToCopy = (memEnd-memStart)>>14;
01225 
01226         if(addr < memStart) {
01227             // round down to nearest 16k boundary, makes shift logic work cause it works
01228             // in bytes, ie 16k chunks
01229             addr &= ~0x3fff;
01230             // marking earlier pages
01231             if(memStart != MAX_UINTPTR) {
01232                 shiftAmount = (memStart - addr) >> 14;
01233             }
01234             memStart = addr;
01235         } 
01236         
01237         if(addr + (numPages+1)*GCHeap::kBlockSize > memEnd) {
01238             // marking later pages
01239             memEnd = addr + (numPages+1)*GCHeap::kBlockSize;
01240             // round up to 16k 
01241             memEnd = (memEnd+0x3fff)&~0x3fff;
01242         }
01243 
01244         uint32 numPagesNeeded = (uint32)(((memEnd-memStart)>>14)/GCHeap::kBlockSize + 1);
01245         if(numPagesNeeded > heap->Size(pageMap)) {
01246             dst = (unsigned char*)heap->Alloc(numPagesNeeded);
01247         }
01248 
01249         if(shiftAmount || dst != pageMap) {
01250             memmove(dst + shiftAmount, pageMap, numBytesToCopy);
01251             memset(dst, 0, shiftAmount);
01252             if(dst != pageMap) {
01253                 heap->Free(pageMap);
01254                 pageMap = dst;
01255             }
01256         }
01257 
01258         addr = (uintptr)item;
01259         while(numPages--)
01260         {
01261             GCAssert(GetPageMapValueAlreadyLocked(addr) == 0);
01262             SetPageMapValue(addr, to);
01263             addr += GCHeap::kBlockSize;
01264         }
01265     }
01266 
01267     void GC::UnmarkGCPages(void *item, uint32 numpages)
01268     {
01269         uintptr addr = (uintptr) item;
01270 
01271         USING_PAGE_MAP();
01272         while(numpages--)
01273         {
01274             ClearPageMapValue(addr);
01275             addr += GCHeap::kBlockSize;
01276         }
01277     }
01278 
01279     void GC::CleanStack(bool force)
01280     {
01281 #if defined(_MSC_VER) && (defined(_DEBUG) || defined(_ARM_))
01282         // debug builds poison the stack already
01283         (void)force;
01284         return;
01285 #else
01286         if(!force && (stackCleaned || rememberedStackTop == 0))
01287             return;
01288 
01289         stackCleaned = true;
01290         
01291         register void *stackP;
01292 
01293 #if defined MMGC_IA32
01294         #ifdef WIN32
01295         __asm {
01296             mov stackP,esp
01297         }
01298         #elif defined SOLARIS
01299         stackP = (void *) _getsp();
01300         #else
01301         asm("movl %%esp,%0" : "=r" (stackP));
01302         #endif
01303 #endif
01304 
01305 #if defined MMGC_AMD64
01306     #ifdef WIN32
01307         int foo;
01308         stackP = &foo;
01309     #else
01310         asm("mov %%rsp,%0" : "=r" (stackP));
01311     #endif
01312 #endif
01313 
01314 #if defined MMGC_SPARC
01315         stackP = (void *) _getsp();
01316 #endif
01317 
01318 #if defined MMGC_PPC
01319         // save off sp
01320     #ifdef _MAC
01321             asm("mr %0,r1" : "=r" (stackP));
01322     #else // _MAC
01323             asm("mr %0,%%r1" : "=r" (stackP));
01324     #endif // _MAC
01325 #endif // MMGC_PPC
01326 
01327         if( ((char*) stackP > (char*)rememberedStackTop) && ((char *)rememberedStackBottom > (char*)stackP)) {
01328             size_t amount = (char*) stackP - (char*)rememberedStackTop;
01329             void *stack = alloca(amount);
01330             if(stack) {
01331                 memset(stack, 0, amount);
01332             }
01333         }
01334 #endif // __MSC_VER && _DEBUG
01335     }
01336     
01337     #if defined(MMGC_PPC) && defined(__GNUC__)
01338     __attribute__((noinline)) 
01339     #endif
01340     void GC::MarkQueueAndStack(GCStack<GCWorkItem>& work)
01341     {
01342         GCWorkItem item;
01343 
01344         MMGC_GET_STACK_EXTENTS(this, item.ptr, item._size);
01345 
01346         // this is where we will clear to when CleanStack is called
01347         if(rememberedStackTop == 0 || rememberedStackTop > item.ptr) {
01348             rememberedStackTop = item.ptr;
01349         }
01350 
01351         PushWorkItem(work, item);
01352         Mark(work);
01353     }
01354 
01355     void GCRoot::init(GC* _gc, const void *_object, size_t _size)
01356     {
01357 #ifndef _DEBUG
01358         // only do the memset on GCRoot subclasses
01359         if(_object == this) {
01360             size_t s = FixedMalloc::GetInstance()->Size(this);
01361             // being a GCRoot its important we are clean
01362             memset(this, 0, s);
01363         }
01364 #endif
01365         gc = _gc;
01366         object = _object;
01367         size = _size;
01368         gc->AddRoot(this);
01369     }
01370 
01371     GCRoot::GCRoot(GC * _gc)
01372     {
01373         init(_gc, this, FixedMalloc::GetInstance()->Size(this));
01374     }
01375 
01376     GCRoot::GCRoot(GC * _gc, const void * _object, size_t _size)
01377     {
01378         init(_gc, _object, _size);
01379     }
01380 
01381     GCRoot::~GCRoot()
01382     {
01383         if(gc) {
01384             gc->RemoveRoot(this);
01385         }
01386     }
01387 
01388     void GCRoot::Set(const void * _object, size_t _size)
01389     {
01390         this->object = _object;
01391         this->size = _size;
01392     }
01393 
01394     void GCRoot::Destroy()
01395     {
01396         Set(NULL, 0);
01397         if(gc) {
01398             gc->RemoveRoot(this);
01399         }
01400         gc = NULL;
01401     }
01402 
01403     GCCallback::GCCallback(GC * _gc) : gc(_gc)
01404     {
01405         gc->AddCallback(this);
01406     }
01407 
01408     GCCallback::~GCCallback()
01409     {
01410         if(gc) {
01411             gc->RemoveCallback(this);
01412         }
01413     }
01414 
01415     void GCCallback::Destroy()
01416     {
01417         if(gc) {
01418             gc->RemoveCallback(this);
01419         }
01420         gc = NULL;
01421     }
01422 
01423 #ifndef MMGC_THREADSAFE
01424     void GC::CheckThread()
01425     {
01426 #ifdef _DEBUG
01427 #ifdef WIN32
01428         GCAssertMsg(disableThreadCheck || m_gcThread == GetCurrentThreadId(), "Unsafe access to GC from wrong thread!");
01429 #endif
01430 #endif
01431     }
01432 #endif
01433 
01434     bool GC::IsPointerToGCPage(const void *item)
01435     {
01436         USING_PAGE_MAP();
01437         if((uintptr)item >= memStart && (uintptr)item < memEnd)
01438             return GetPageMapValueAlreadyLocked((uintptr) item) != 0;
01439         return false;
01440     }
01441 
01442 #ifdef MMGC_DRC
01443 
01444   ZCT::ZCT()
01445     {
01446         zctSize = ZCT_START_SIZE;
01447         zctFreelist = NULL;
01448         reaping = false;
01449         gc = NULL;
01450         count = 0;
01451         zctReapThreshold = ZCT_REAP_THRESHOLD;
01452     }
01453 
01454     ZCT::~ZCT()
01455     {
01456         gc->heapFree(zct, zctSize);
01457     }
01458 
01459     void ZCT::SetGC(GC *gc)
01460     {
01461         this->gc = gc;
01462         zct = (RCObject**) gc->heapAlloc(zctSize);
01463         zctNext = zct;
01464     }
01465 
01466     void ZCT::Add(RCObject *obj)
01467     {
01468         if(gc->collecting)
01469         {
01470             // this is a vestige from FP8 to fix bug 165100, it has the affect of delaying 
01471             // the deletion of some objects which causes the site to work
01472             if(gc->dontAddToZCTDuringCollection)
01473                 return;
01474 
01475             // unmarked objects are gonna get swept anyways
01476             if(!GC::GetMark(obj))
01477                 return;
01478         }
01479 
01480 #if 0
01481         // note if we add things while reaping we could delete the object
01482         // here if we had a way to monitor our stack usage
01483         if(reaping && PLENTY_OF_STACK()) {
01484             {
01485                 USING_CALLBACK_LIST(gc);
01486                 GCCallback *cb = gc->m_callbacks;
01487                 while(cb) {
01488                     cb->prereap(obj);
01489                     cb = cb->nextCB;
01490                 }
01491             }
01492             if(gc->IsFinalized(obj))
01493                 ((GCFinalizable*)obj)->~GCFinalizable();
01494             gc->Free(obj);
01495             return;
01496         }
01497 #endif
01498 
01499         if(zctFreelist) {
01500             RCObject **nextFree = (RCObject**)*zctFreelist;
01501             *zctFreelist = obj;
01502             obj->setZCTIndex((int)(zctFreelist - zct));
01503             zctFreelist = nextFree;         
01504         } else if(reaping && zctIndex > nextPinnedIndex) {
01505             // we're going over the list, insert this guy at the front if possible
01506             zctIndex--;
01507             GCAssert(zct[zctIndex] == NULL);
01508             obj->setZCTIndex(zctIndex);
01509             zct[zctIndex] = obj;
01510         } else if(zctNext - zct <= (RCObject::ZCT_INDEX>>8)) {
01511             *zctNext = obj;
01512             obj->setZCTIndex((int)(zctNext - zct));
01513             zctNext++;
01514         } else {
01515             // zct is full, do nothing, mark/sweep will have to handle it
01516             return;
01517         }
01518 
01519         count++;
01520 
01521         if(!reaping) {
01522             // object that were pinned last reap should be unpinned
01523             if(obj->IsPinned())
01524                 obj->Unpin();
01525             if(!gc->collecting && zctNext >= zct+zctReapThreshold)
01526                 Reap();
01527         }
01528 
01529         if(zctNext >= zct + zctSize*4096/sizeof(void *)) {
01530             // grow 
01531             RCObject **newZCT = (RCObject**) gc->heap->Alloc(zctSize*2);
01532             memcpy(newZCT, zct, zctSize*GCHeap::kBlockSize);
01533             gc->heap->Free(zct);
01534             zctNext = newZCT + (zctNext-zct);
01535             zct = newZCT;   
01536             zctSize *= 2;
01537             GCAssert(!zctFreelist);
01538         }
01539     }
01540 
01541     void ZCT::Remove(RCObject *obj)
01542     {
01543         int index = obj->getZCTIndex();
01544         GCAssert(zct[index] == obj);
01545 
01546         if(reaping)
01547         {
01548             // freelist doesn't exist during reaping, simplifies things, holes will
01549             // be compacted next go around if index < nextPinnedIndex.  the freelist
01550             // allows incoming object to be added behind zctIndex which would mean
01551             // the reap process wouldn't cascade like its supposed to. 
01552             zct[index] = NULL;
01553         }
01554         else
01555         {
01556             zct[index] = (RCObject*) zctFreelist;
01557             zctFreelist = &zct[index];
01558         }
01559         obj->ClearZCTFlag();
01560         count--;
01561     }
01562 
01563     void ZCT::PinStackObjects(const void *start, size_t len)
01564     {
01565         RCObject **p = (RCObject**)start;
01566         RCObject **end = p + len/sizeof(RCObject*);
01567 
01568         const void *_memStart = (const void*)gc->memStart;
01569         const void *_memEnd = (const void*)gc->memEnd;
01570 
01571         while(p < end)
01572         {
01573             const void *val = GC::Pointer(*p++);    
01574             
01575             if(val < _memStart || val >= _memEnd)
01576                 continue;
01577 
01578             int bits = gc->GetPageMapValue((uintptr)val); 
01579             bool doit = false;
01580             if (bits == GC::kGCAllocPage) {
01581                 doit = GCAlloc::IsRCObject(val) && GCAlloc::FindBeginning(val) == GetRealPointer(val);
01582             } else if(bits == GC::kGCLargeAllocPageFirst) {
01583                 doit = GCLargeAlloc::IsRCObject(val) && GCLargeAlloc::FindBeginning(val) == GetRealPointer(val);
01584             }
01585 
01586             if(doit) {
01587                 RCObject *obj = (RCObject*)val;
01588                 obj->Pin();
01589             }
01590         }
01591     }
01592             
01593 #if 0
01594     // this is a release ready tool for hunting down freelist corruption
01595     void GC::CheckFreelists()
01596     {
01597         GCAlloc **allocs = containsPointersAllocs;
01598         for (int l=0; l < GC::kNumSizeClasses; l++) {
01599             GCAlloc *a = allocs[l];
01600             GCAlloc::GCBlock *_b = a->m_firstBlock; 
01601             while(_b) {
01602                 void *fitem = _b->firstFree;
01603                 void *prev = 0;
01604                 while(fitem) {
01605                     if((uintptr(fitem) & 7) != 0) {
01606                         _asm int 3;
01607                         break;
01608                     }
01609                     if((uintptr(fitem) & ~0xfff) != uintptr(_b))
01610                         _asm int 3;
01611                     prev = fitem;
01612                     fitem = *(void**)fitem;
01613                 }       
01614                 _b = _b->next;
01615             }
01616         }
01617     }
01618 #endif
01619 
01620 #ifdef _DEBUG
01621 
01622     void GC::RCObjectZeroCheck(RCObject *item)
01623     {
01624         size_t size = Size(item)/sizeof(int);
01625         int *p = (int*)item;
01626         // skip vtable, first 4 bytes are cleared in Alloc
01627         p++;
01628 #ifdef MMGC_64BIT
01629         p++; // vtable is 8-bytes
01630         size--; 
01631 #endif      
01632         // in incrementalValidation mode manually deleted items
01633         // aren't put on the freelist so skip them
01634         if(incrementalValidation) {
01635             if(*p == (int32)0xcacacaca)
01636                 return;
01637         }
01638         for(int i=1; i<(int)size; i++,p++)
01639         {
01640             if(*p)
01641             {
01642 #ifdef MEMORY_INFO
01643                 PrintStackTrace(item);
01644 #endif
01645                 GCAssertMsg(false, "RCObject didn't clean up itself.");
01646             }
01647         }   
01648     }
01649 
01650     // used in DRC validation
01651     void CleanEntireStack()
01652     {
01653 #if defined(MMGC_IA32) && defined(WIN32)
01654         // wipe clean the entire stack below esp.
01655         register void *stackP;
01656         __asm {
01657             mov stackP,esp
01658         } 
01659         MEMORY_BASIC_INFORMATION mib;
01660         // find the top of the stack
01661         VirtualQuery(stackP, &mib, sizeof(MEMORY_BASIC_INFORMATION));
01662         // go down whilst pages are committed
01663         char *stackPeak = (char*) mib.BaseAddress;
01664         while(true)
01665         {
01666             VirtualQuery(stackPeak - 4096, &mib, sizeof(MEMORY_BASIC_INFORMATION));
01667             // break when we hit the stack growth guard page or an uncommitted page
01668             // guard pages are committed so we need both checks (really the commit check
01669             // is just an extra precaution)
01670             if((mib.Protect & PAGE_GUARD) == PAGE_GUARD || mib.State != MEM_COMMIT)
01671                 break;
01672             stackPeak -= 4096;
01673         }
01674         size_t amount = (char*) stackP - stackPeak - 128;
01675         {
01676             void *stack = alloca(amount);
01677             if(stack) {
01678                 memset(stack, 0, amount);
01679             }
01680         }
01681 #endif // _MMGC_IA32
01682     }
01683 #endif // _DEBUG
01684 
01685     void ZCT::Reap()
01686     {
01687         if(gc->collecting || reaping || count == 0)
01688             return;
01689 
01690         uint64 start = 0;
01691         if(gc->gcstats) {
01692             start = GC::GetPerformanceCounter();
01693         }
01694         uint32 pagesStart = (uint32)gc->totalGCPages;
01695         uint32 numObjects=0;
01696         uint32 objSize=0;
01697 
01698         reaping = true;
01699         
01700         SAMPLE_FRAME("[reap]", gc->core());
01701 
01702         // start by pinning live stack objects
01703         GCWorkItem item;
01704         MMGC_GET_STACK_EXTENTS(gc, item.ptr, item._size);
01705         PinStackObjects(item.ptr, item._size);
01706 
01707         // important to do this before calling prereap
01708         // use index to iterate in case it grows, as we go through the list we
01709         // unpin pinned objects and pack them at the front of the list, that
01710         // way the ZCT is in a good state at the end
01711         zctIndex = 0;
01712         nextPinnedIndex = 0;
01713 
01714         // invoke prereap on all callbacks
01715         {
01716             USING_CALLBACK_LIST(gc);
01717             GCCallback *cb = gc->m_callbacks;
01718             while(cb) {
01719                 cb->prereap();
01720                 cb = cb->nextCB;
01721             }
01722         }
01723 
01724 #ifdef _DEBUG
01725         if(gc->validateDefRef) {
01726             // kill incremental mark since we're gonna wipe the marks
01727             gc->marking = false;
01728             gc->m_incrementalWork.Keep(0);
01729 
01730             CleanEntireStack();
01731             gc->Trace(item.ptr, item._size);
01732         }
01733 #endif
01734 
01735         // first nuke the freelist, we won't have one when we're done reaping
01736         // and we don't want secondary objects put on the freelist (otherwise
01737         // reaping couldn't be a linear scan)
01738         while(zctFreelist) {
01739             RCObject **next = (RCObject**)*zctFreelist;
01740             *zctFreelist = 0;
01741             zctFreelist = next;
01742         }
01743         
01744         while(zct+zctIndex < zctNext) {
01745             SAMPLE_CHECK();
01746             RCObject *rcobj = zct[zctIndex++];
01747             if(rcobj && !rcobj->IsPinned()) {
01748                 rcobj->ClearZCTFlag();
01749                 zct[zctIndex-1] = NULL;
01750                 count--;
01751 #ifdef _DEBUG
01752                 if(gc->validateDefRef) {
01753                     if(gc->GetMark(rcobj)) {    
01754 #ifdef MEMORY_INFO
01755                         rcobj->DumpHistory();
01756                         GCDebugMsg(false, "Back pointer chain:");
01757                         gc->DumpBackPointerChain(rcobj);
01758 #endif
01759                         GCAssertMsg(false, "Zero count object reachable, ref counts not correct!");
01760                     }
01761                 }
01762 #endif
01763                 // invoke prereap on all callbacks
01764                 {
01765                     USING_CALLBACK_LIST(gc);
01766                     GCCallback *cbb = gc->m_callbacks;
01767                     while(cbb) {
01768                         cbb->prereap(rcobj);
01769                         cbb = cbb->nextCB;
01770                     }
01771                 }
01772 
01773                 GCAssert(*(int*)rcobj != 0);
01774                 GCAssert(gc->IsFinalized(rcobj));
01775 //              ((GCFinalizable*)rcobj)->~GCFinalizable();
01776                 ((GCFinalizable*)rcobj)->Finalize();
01777                 numObjects++;
01778                 objSize += (uint32)GC::Size(rcobj);
01779                 gc->Free(rcobj);
01780 
01781                 GCAssert(gc->weakRefs.get(rcobj) == NULL);
01782             } else if(rcobj) {
01783                 // move it to front
01784                 rcobj->Unpin();
01785                 if(nextPinnedIndex != zctIndex-1) {
01786                     rcobj->setZCTIndex(nextPinnedIndex);
01787                     GCAssert(zct[nextPinnedIndex] == NULL);
01788                     zct[nextPinnedIndex] = rcobj;
01789                     zct[zctIndex-1] = NULL;
01790                 }
01791                 nextPinnedIndex++;              
01792             }
01793         }
01794 
01795         zctNext = zct + nextPinnedIndex;
01796         zctReapThreshold = count + ZCT_REAP_THRESHOLD;
01797         if(zctReapThreshold > int(zctSize*GCHeap::kBlockSize/sizeof(RCObject*)))
01798             zctReapThreshold = zctSize*GCHeap::kBlockSize/sizeof(RCObject*);
01799         GCAssert(nextPinnedIndex == count);
01800         zctIndex = nextPinnedIndex = 0;
01801 
01802         // invoke postreap on all callbacks
01803         {
01804             USING_CALLBACK_LIST(gc);
01805             GCCallback *cb = gc->m_callbacks;
01806             while(cb) {
01807                 cb->postreap();
01808                 cb = cb->nextCB;
01809             }
01810         }
01811         if(gc->gcstats && numObjects) {
01812             gc->gclog("[mem] DRC reaped %d objects (%d kb) freeing %d pages (%d kb) in %.2f millis (%.4f s)\n", 
01813                 numObjects, objSize>>10, pagesStart - gc->totalGCPages, gc->totalGCPages*GCHeap::kBlockSize >> 10, 
01814                 GC::duration(start), gc->duration(gc->t0)/1000);
01815         }
01816         reaping = false;
01817 
01818 #ifdef _DEBUG
01819         if(gc->validateDefRef) {
01820             gc->Sweep();
01821         }
01822 #endif
01823     }
01824 
01825 #ifdef SOLARIS
01826 
01827 #ifdef MMGC_SPARC
01828 #define FLUSHWIN() asm("ta 3"); 
01829 #else
01830 #define FLUSHWIN() 
01831 #endif
01832 #define MAX_FRAMES 500
01833 
01834     static int validaddr(void * addr) 
01835     {
01836         static long pagemask = -1;
01837         char c;
01838         if (pagemask == -1) {
01839             pagemask = ~(sysconf(_SC_PAGESIZE) - 1);
01840         }
01841         addr = (void *)((long)addr & pagemask);
01842         if (mincore((char *)addr, 1, &c) == -1 && errno == ENOMEM) {
01843             return 0;  /* invalid */
01844         } else {
01845             return 1;  /* valid */
01846         }
01847     }  /* end of validaddr */
01848 
01849     pthread_key_t stackTopKey = NULL;
01850 
01851     uintptr GC::GetStackTop() const
01852     {
01853         if(stackTopKey == NULL)
01854             {
01855                 int res = pthread_key_create(&stackTopKey, NULL);
01856                 GCAssert(res == 0);
01857             }
01858 
01859         void *stackTop = pthread_getspecific(stackTopKey);
01860         if(stackTop)
01861             return (uintptr)stackTop;
01862 
01863         struct frame *sp;
01864         int i;
01865         int *iptr;
01866 
01867         stack_t st;
01868         stack_getbounds(&st);
01869         uintptr_t stack_base = (uintptr_t)st.ss_sp + st.ss_size;
01870 
01871         FLUSHWIN();
01872 
01873         sp = (struct frame *)_getfp();
01874         stackTop = (void *)sp;
01875         for (i = 0; i < MAX_FRAMES && sp && (uintptr_t)sp < stack_base; i++) { 
01876             if (!validaddr( sp ) || !validaddr(&sp->fr_savpc) || !sp->fr_savpc ) {
01877                 break;
01878             }
01879             stackTop = (void *)sp;
01880             sp = ( struct frame * )sp->fr_savfp;
01881         }
01882         pthread_setspecific(stackTopKey, stackTop);
01883         return (uintptr)stackTop;
01884     }
01885 #elif defined(AVMPLUS_UNIX) // SOLARIS
01886     pthread_key_t stackTopKey = 0;
01887 
01888     uintptr GC::GetStackTop() const
01889     {
01890         if(stackTopKey == 0)
01891         {
01892 #ifdef DEBUG          
01893             int res = 
01894 #endif
01895               pthread_key_create(&stackTopKey, NULL);
01896             GCAssert(res == 0);
01897         }
01898 
01899         void *stackTop = pthread_getspecific(stackTopKey);
01900         if(stackTop)
01901             return (uintptr)stackTop;
01902 
01903         size_t sz;
01904         void *s_base;
01905         pthread_attr_t attr;
01906         
01907         pthread_attr_init(&attr);
01908         // WARNING: stupid expensive function, hence the TLS
01909 #ifdef HAVE_PTHREAD_NP_H
01910         int res = pthread_attr_get_np(pthread_self(),&attr);
01911 #else // HAVE_PTHREAD_NP_H
01912         int res = pthread_getattr_np(pthread_self(),&attr);
01913 #endif // HAVE_PTHREAD_NP_H
01914         GCAssert(res == 0);
01915         
01916         if(res)
01917         {
01918             // not good
01919             return 0;
01920         }
01921 
01922         res = pthread_attr_getstack(&attr,&s_base,&sz);
01923         GCAssert(res == 0);
01924         pthread_attr_destroy(&attr);
01925 
01926         stackTop = (void*) ((size_t)s_base + sz);
01927         // stackTop has to be greater than current stack pointer
01928         GCAssert(stackTop > &sz);
01929         pthread_setspecific(stackTopKey, stackTop);
01930         return (uintptr)stackTop;
01931         
01932     }
01933 #endif // AVMPLUS_UNIX
01934 
01935     void GC::gclog(const char *format, ...)
01936     {
01937         static bool ingclog=false;
01938         
01939         (void)format;
01940         char buf[4096];
01941         va_list argptr;
01942 
01943         va_start(argptr, format);
01944         vsprintf(buf, format, argptr);
01945         va_end(argptr);
01946 
01947         GCAssert(strlen(buf) < 4096);
01948 
01949         {
01950             USING_CALLBACK_LIST(this);
01951             GCCallback *cb = m_callbacks;
01952                 if(cb) {
01953                 cb->log(buf);
01954                 cb = cb->nextCB;
01955             }
01956         }
01957         // log gross stats any time anything interesting happens
01958         if(!ingclog) {
01959             ingclog = true;
01960             updateGrossMemoryStats();
01961             ingclog = false;
01962         }
01963     }
01964 
01965 #ifdef MEMORY_INFO
01966     void GC::DumpBackPointerChain(void *o, pDumpBackCallbackProc proc, void *context)
01967     {
01968         int *p = (int*)GetRealPointer ( o ) ;
01969         int size = *p++;
01970         int traceIndex = *p++;
01971         //if(*(p+1) == 0xcacacaca || *(p+1) == 0xbabababa) {
01972             // bail, object was deleted
01973         //  return;
01974         //}
01975         const char *typeName = GetTypeName(traceIndex, o);
01976 // Disabled for 64-bit Windows.  Debugger doesn't allow exception to go uncaught so always breaks
01977 #if (defined(WIN32) && !defined(UNDER_CE) && !defined(MMGC_64BIT)) || ( defined(AVMPLIS_UNIX) && !defined(__ICC) )
01978         if (strncmp(typeName, "unknown ", 7))
01979         {
01980             try {
01981                 const std::type_info *ti = &typeid(*(MMgc::GCObject*)p);
01982                 if (ti->name() && (int(ti->name()) > 0x10000))
01983                     typeName = ti->name();
01984                 // sometimes name will get set to bogus memory with no exceptions catch that
01985                 char c = *typeName;
01986                 (void)c;    // silence compiler warning
01987             } catch(...) {
01988                 typeName = "unknown";
01989             }
01990         }
01991 #endif
01992 
01993         // strip "class "
01994         if (!strncmp(typeName, "class ", 6))
01995             typeName += 6;
01996         GCDebugMsg(false, "Object: (%s *)0x%x\n", typeName, p);
01997         if (proc)
01998             proc(context, o, typeName);
01999         PrintStackTraceByIndex(traceIndex);
02000         GCDebugMsg(false, "---\n");
02001         // skip data + endMarker
02002         p += 1 + (size>>2);
02003         void *container = (void*)(*(void**)p);
02004         if(container && IsPointerToGCPage(container))
02005             DumpBackPointerChain(container, proc, context);
02006         else 
02007         {
02008             GCDebugMsg(false, "GCRoot object: 0x%x\n", container);
02009             if((uintptr)container >= memStart && (uintptr)container < memEnd)
02010                 PrintStackTrace(container);
02011         }
02012     }
02013 
02014     void GC::WriteBackPointer(const void *item, const void *container, size_t itemSize)
02015     {
02016         GCAssert(container != NULL);
02017         uint32 *p = (uint32*) item;
02018         uint32 size = *p++;
02019         if(size && size <= itemSize) {
02020             // skip traceIndex + data + endMarker
02021             p += (2 + (size>>2));
02022             GCAssert(sizeof(uintptr) == sizeof(void*));
02023             *(uintptr*)p = (uintptr) container;
02024         }
02025     }
02026 
02027 #endif
02028 
02029     bool GC::IsRCObject(const void *item)
02030     {
02031         int bits;
02032         {
02033             USING_PAGE_MAP();
02034 
02035             if ((uintptr)item < memStart || (uintptr)item >= memEnd || ((uintptr)item&0xfff) == 0)
02036                 return false;
02037             bits = GetPageMapValueAlreadyLocked((uintptr)item);
02038         }
02039 
02040         item = GetRealPointer(item);
02041         switch(bits)
02042         {
02043         case kGCAllocPage:
02044             if((char*)item < ((GCAlloc::GCBlock*)((uintptr)item&~0xfff))->items)
02045                 return false;
02046             return GCAlloc::IsRCObject(item);
02047         case kGCLargeAllocPageFirst:
02048             return GCLargeAlloc::IsRCObject(item);
02049         default:
02050             return false;
02051         }
02052     }
02053 
02054 #endif // MMGC_DRC
02055     
02056 #ifdef MEMORY_INFO
02057     
02058     int DumpAlloc(GCAlloc *a)
02059     {
02060         int inUse =  a->GetNumAlloc() * a->GetItemSize();
02061         int maxAlloc =  a->GetMaxAlloc()* a->GetItemSize();
02062         int efficiency = maxAlloc > 0 ? inUse * 100 / maxAlloc : 100;
02063         if(inUse) {
02064             GCDebugMsg(false, "Allocator(%d): %d%% efficiency %d kb out of %d kb\n", a->GetItemSize(), efficiency, inUse>>10, maxAlloc>>10);
02065         }
02066         return maxAlloc-inUse;
02067     }
02068 
02069     void GC::DumpMemoryInfo()
02070     {
02071         if(heap->enableMemoryProfiling)
02072         {
02073             DumpFatties();
02074             if (dumpSizeClassState)
02075             {
02076                 int waste=0;
02077                 for(int i=0; i < kNumSizeClasses; i++)
02078                 {
02079                     waste += DumpAlloc(containsPointersAllocs[i]);
02080 #ifdef MMGC_DRC
02081                     waste += DumpAlloc(containsPointersRCAllocs[i]);
02082 #endif
02083                     waste += DumpAlloc(noPointersAllocs[i]);
02084                 }
02085                 GCDebugMsg(false, "Wasted %d kb\n", waste>>10);
02086 
02087             }
02088         }
02089     }
02090 
02091 #endif
02092 
02093 #ifdef _DEBUG
02094 
02095     void GC::CheckFreelist(GCAlloc *gca)
02096     {   
02097         GCAlloc::GCBlock *b = gca->m_firstFree;
02098         while(b)
02099         {
02100             void *freelist = b->firstFree;
02101             while(freelist)
02102             {           
02103                 // b->firstFree should be either 0 end of free list or a pointer into b, otherwise, someone
02104                 // wrote to freed memory and hosed our freelist
02105                 GCAssert(freelist == 0 || ((uintptr) freelist >= (uintptr) b->items && (uintptr) freelist < (uintptr) b + GCHeap::kBlockSize));
02106                 freelist = *((void**)freelist);
02107             }
02108             b = b->nextFree;
02109         }
02110     }
02111 
02112     void GC::CheckFreelists()
02113     {
02114         for(int i=0; i < kNumSizeClasses; i++)
02115         {
02116             CheckFreelist(containsPointersAllocs[i]);
02117             CheckFreelist(noPointersAllocs[i]);
02118         }
02119     }
02120 
02121     void GC::UnmarkedScan(const void *mem, size_t size)
02122     {
02123         uintptr lowerBound = memStart;
02124         uintptr upperBound = memEnd;
02125         
02126         uintptr *p = (uintptr *) mem;
02127         uintptr *end = p + (size / sizeof(void*));
02128 
02129         while(p < end)
02130         {
02131             uintptr val = *p++;
02132             
02133             if(val < lowerBound || val >= upperBound)
02134                 continue;
02135             
02136             // normalize and divide by 4K to get index
02137             int bits = GetPageMapValueAlreadyLocked(val);
02138             switch(bits)
02139             {
02140             case 0:
02141                 continue;
02142                 break;
02143             case kGCAllocPage:
02144                 GCAssert(GCAlloc::ConservativeGetMark((const void*) (val&~7), true));
02145                 break;
02146             case kGCLargeAllocPageFirst:
02147                 GCAssert(GCLargeAlloc::ConservativeGetMark((const void*) (val&~7), true));
02148                 break;
02149             default:
02150                 GCAssertMsg(false, "Invalid pageMap value");
02151                 break;
02152             }
02153         }
02154     }
02155 
02156     void GC::FindUnmarkedPointers()
02157     {
02158         MMGC_ASSERT_EXCLUSIVE_GC(this);
02159 
02160         if(findUnmarkedPointers)
02161         {
02162             uintptr m = memStart;
02163 
02164             while(m < memEnd)
02165             {
02166                 // divide by 4K to get index
02167                 int bits = GetPageMapValue(m);
02168                 if(bits == kNonGC) {
02169                     UnmarkedScan((const void*)m, GCHeap::kBlockSize);
02170                     m += GCHeap::kBlockSize;
02171                 } else if(bits == kGCLargeAllocPageFirst) {
02172                     GCLargeAlloc::LargeBlock *lb = (GCLargeAlloc::LargeBlock*)m;
02173                     const void *item = GetUserPointer((const void*)(lb+1));
02174                     if(GCLargeAlloc::GetMark(item) && GCLargeAlloc::ContainsPointers(GetRealPointer(item))) {
02175                         UnmarkedScan(item, GC::Size(item));
02176                     }
02177                     m += lb->GetNumBlocks() * GCHeap::kBlockSize;
02178                 } else if(bits == kGCAllocPage) {
02179                     // go through all marked objects
02180                     GCAlloc::GCBlock *b = (GCAlloc::GCBlock *) m;
02181                     for (int i=0; i < b->alloc->m_itemsPerBlock; i++) {
02182                         // If the mark is 0, delete it.
02183                         int marked = GCAlloc::GetBit(b, i, GCAlloc::kMark);
02184                         if (!marked) {
02185                             void* item = (char*)b->items + b->alloc->m_itemSize*i;
02186                             if(GCAlloc::ContainsPointers(item)) {
02187                                 UnmarkedScan(GetUserPointer(item), b->alloc->m_itemSize - DebugSize());
02188                             }
02189                         }
02190                     }
02191                     
02192                     m += GCHeap::kBlockSize;
02193                 }                
02194             }
02195         }
02196     }
02197 
02198 /* macro to stack allocate a string containing 3*i (indent) spaces */
02199 #define ALLOCA_AND_FILL_WITH_SPACES(b, i) \
02200     { b = (char*)alloca((3*(i))+1); \
02201     int n = 0; \
02202     for(; n<3*(i); n++) b[n] = ' '; \
02203     b[n] = '\0'; }
02204 
02205     void GC::ProbeForMatch(const void *mem, size_t size, uintptr value, int recurseDepth, int currentDepth)
02206     {
02207         uintptr lowerBound = memStart;
02208         uintptr upperBound = memEnd;
02209         
02210         uintptr *p = (uintptr *) mem;
02211         uintptr *end = p + (size / sizeof(void*));
02212 
02213         int bits = GetPageMapValue((uintptr)mem);
02214 
02215         while(p < end)
02216         {
02217             uintptr val = *p++;
02218             
02219             if(val < lowerBound || val >= upperBound)
02220                 continue;
02221 
02222             // did we hit ?
02223             if (val == value)
02224             {
02225                 // ok so let's see where we are 
02226                 uintptr* where = p-1;
02227                 GCHeap::HeapBlock* block = heap->AddrToBlock(where);
02228                 //GCAssertMsg(block->inUse(), "Not sure how we got here if the block is not in use?");
02229                 GCAssertMsg(block->committed, "Means we are probing uncommitted memory. not good");
02230                 int* ptr;             
02231 
02232                 switch(bits)
02233                 {
02234                 case kNonGC:
02235                     {
02236                         if (block->size == 1)
02237                         {
02238                             // fixed sized entries find out the size of the block
02239                             FixedAlloc::FixedBlock* fixed = (FixedAlloc::FixedBlock*) block->baseAddr;
02240                             int fixedsize = fixed->size;
02241 
02242                             // now compute which element we are 
02243                             uintptr startAt = (uintptr) &(fixed->items[0]);
02244                             uintptr item = ((uintptr)where-startAt) / fixedsize;
02245 
02246                             ptr = (int*) ( startAt + (item*fixedsize) );
02247                         }
02248                         else
02249                         {
02250                             // fixed large allocs ; start is after the block 
02251                             ptr = (int*) block->baseAddr;
02252                         }
02253                     }
02254                     break;
02255 
02256                 default:
02257                     ptr = ((int*)FindBeginning(where)) - 2;
02258                     break;
02259                 }
02260 
02261                 int  taggedSize = *ptr;
02262                 int  traceIndex = *(ptr+1);
02263                 int* real = (ptr+2);
02264 
02265                 char* buffer = 0;
02266                 ALLOCA_AND_FILL_WITH_SPACES(buffer, currentDepth);
02267 
02268                 if (buffer) GCDebugMsg(false, buffer);
02269                 GCDebugMsg(false, "Location: 0x%08x  Object: 0x%08x (size %d)\n", where, real, taggedSize);
02270                 if (buffer) GCDebugMsg(false, buffer);
02271 #ifdef MEMORY_INFO
02272                 PrintStackTraceByIndex(traceIndex);
02273 #else
02274                 (void)traceIndex;
02275 #endif
02276 
02277                 if (recurseDepth > 0)
02278                     WhosPointingAtMe(real, recurseDepth-1, currentDepth+1);
02279             }
02280         }
02281     }
02282 
02291     void GC::WhosPointingAtMe(void* me, int recurseDepth, int currentDepth)
02292     {
02293 #ifdef MMGC_THREADSAFE
02294         if (currentDepth == 0)
02295             pageMapLock.Acquire();
02296 #endif
02297 
02298         uintptr val = (uintptr)me;
02299         uintptr m = memStart;
02300 
02301         char* buffer = 0;
02302         ALLOCA_AND_FILL_WITH_SPACES(buffer, currentDepth);
02303 
02304         if (buffer) GCDebugMsg(false, buffer);
02305         GCDebugMsg(false, "[%d] Probing for pointers to : 0x%08x\n", currentDepth, me);
02306         while(m < memEnd)
02307         {
02308 #ifdef WIN32
02309             // first skip uncommitted memory
02310             MEMORY_BASIC_INFORMATION mib;
02311             VirtualQuery((void*) m, &mib, sizeof(MEMORY_BASIC_INFORMATION));
02312             if((mib.Protect & PAGE_READWRITE) == 0) 
02313             {
02314                 m += mib.RegionSize;
02315                 continue;
02316             }
02317 #endif
02318 
02319             // divide by 4K to get index
02320             int bits = GetPageMapValueAlreadyLocked(m);
02321             if(bits == kNonGC) 
02322             {
02323                 ProbeForMatch((const void*)m, GCHeap::kBlockSize, val, recurseDepth, currentDepth);
02324                 m += GCHeap::kBlockSize;
02325             } 
02326             else if(bits == kGCLargeAllocPageFirst) 
02327             {
02328                 GCLargeAlloc::LargeBlock *lb = (GCLargeAlloc::LargeBlock*)m;
02329                 const void *item = GetUserPointer((const void*)(lb+1));
02330                 bool marked = GCLargeAlloc::GetMark(item);
02331                 if (marked)
02332                 {
02333                     if(GCLargeAlloc::ContainsPointers(GetRealPointer(item))) 
02334                     {
02335                         ProbeForMatch(item, GC::Size(item), val, recurseDepth, currentDepth);
02336                     }
02337                 }
02338                 m += lb->GetNumBlocks() * GCHeap::kBlockSize;
02339             } 
02340             else if(bits == kGCAllocPage) 
02341             {
02342                 // go through all marked objects
02343                 GCAlloc::GCBlock *b = (GCAlloc::GCBlock *) m;
02344                 for (int i=0; i < b->alloc->m_itemsPerBlock; i++) 
02345                 {
02346                     int marked = GCAlloc::GetBit(b, i, GCAlloc::kMark);
02347                     if (marked) 
02348                     {
02349                         void* item = (char*)b->items + b->alloc->m_itemSize*i;
02350                         if(GCAlloc::ContainsPointers(item)) 
02351                         {
02352                             ProbeForMatch(GetUserPointer(item), b->alloc->m_itemSize - DebugSize(), val, recurseDepth, currentDepth);
02353                         }
02354                     }
02355                 }               
02356                 m += GCHeap::kBlockSize;
02357             }
02358             else
02359             {
02360                 GCAssertMsg(false, "Oh seems we missed a case...Tom any ideas here?");
02361             
02362             }
02363         }
02364 
02365 #ifdef MMGC_THREADSAFE
02366         if (currentDepth == 0)
02367             pageMapLock.Release();
02368 #endif
02369     }
02370 #undef ALLOCA_AND_FILL_WITH_SPACES
02371 #endif
02372 
02373 
02374     void GC::StartIncrementalMark()
02375     {
02376         GCAssert(!marking);
02377         GCAssert(!collecting);
02378 
02379         lastStartMarkIncrementCount = markIncrements;
02380 
02381         // set the stack cleaning trigger
02382         stackCleaned = false;
02383 
02384         marking = true;
02385 
02386         GCAssert(m_incrementalWork.Count() == 0);
02387     
02388         uint64 start = GetPerformanceCounter();
02389 
02390         // clean up any pages that need sweeping
02391         for(int i=0; i < kNumSizeClasses; i++) {
02392 #ifdef MMGC_DRC
02393             containsPointersRCAllocs[i]->SweepNeedsSweeping();
02394 #endif
02395             containsPointersAllocs[i]->SweepNeedsSweeping();
02396             noPointersAllocs[i]->SweepNeedsSweeping();
02397         }
02398 
02399         // at this point every object should have no marks or be marked kFreelist
02400 #ifdef _DEBUG       
02401         for(int i=0; i < kNumSizeClasses; i++) {
02402 #ifdef MMGC_DRC
02403             containsPointersRCAllocs[i]->CheckMarks();
02404 #endif
02405             containsPointersAllocs[i]->CheckMarks();
02406             noPointersAllocs[i]->CheckMarks();
02407         }
02408 #endif
02409     
02410         {
02411 #ifdef GCHEAP_LOCK
02412             GCAcquireSpinlock lock(m_rootListLock);
02413 #endif
02414             GCRoot *r = m_roots;
02415             while(r) {
02416                 GCWorkItem item = r->GetWorkItem();
02417                 if(item.ptr)
02418                     MarkItem(item, m_incrementalWork);
02419                 r = r->next;
02420             }
02421         }
02422         markTicks += GetPerformanceCounter() - start;
02423         IncrementalMark();
02424     }
02425 
02426 #if 0
02427     // TODO: SSE2 version
02428     void GC::MarkItem_MMX(const void *ptr, size_t size, GCStack<GCWorkItem> &work)
02429     {
02430          uintptr *p = (uintptr*) ptr;
02431         // deleted things are removed from the queue by setting them to null
02432         if(!p)
02433             return;
02434 
02435         bytesMarked += size;
02436         marks++;
02437 
02438         uintptr *end = p + (size / sizeof(void*));
02439         uintptr thisPage = (uintptr)p & ~0xfff;
02440 
02441         // since MarkItem recurses we have to do this before entering the loop
02442         if(IsPointerToGCPage(ptr)) 
02443         {
02444             int b = SetMark(ptr);
02445 #if defined(_DEBUG) && !defined(DEBUGGER) // sampler does some marking which triggers this
02446             // def ref validation does a Trace which can 
02447             // cause things on the work queue to be already marked
02448             // in incremental GC
02449             if(!validateDefRef) {
02450                 GCAssert(!b);
02451             }
02452 #endif
02453         }
02454 
02455 
02456         _asm {
02457             // load memStart and End into mm0
02458             movq mm0,memStart           
02459         }
02460 
02461         while(p < end) 
02462         {       
02463             _asm {
02464                 mov       ebx, [p]
02465                 mov       ecx, [count]
02466                 sar       ecx, 1
02467                 mov       eax, dword ptr [lowerBound]
02468                 dec       eax
02469                 movd      mm1, eax
02470                 movd      mm2, dword ptr [upperBound]
02471                 punpckldq mm1, mm1
02472                 punpckldq mm2, mm2
02473                 mov       eax, 3
02474                 movd      mm5, eax
02475                 punpckldq mm5, mm5              
02476               MarkLoop:
02477                 movq      mm0, qword ptr [ebx]
02478                 movq      mm3, mm0
02479                 pcmpgtd   mm3, mm1
02480                 movq      mm4, mm2
02481                 pcmpgtd   mm4, mm0
02482                 pand      mm3, mm4
02483                 packssdw  mm3, mm3
02484                 movd      eax, mm3
02485                 or        eax, eax
02486                 jz        Advance
02487 
02488                 // normalize and divide by 4K to get index
02489                 psubd     mm0, mm1
02490                 psrld     mm0, 12
02491 
02492                 // shift amount to determine position in the byte (times 2 b/c 2 bits per page)
02493                 movq      mm6, mm0
02494                 pand      mm6, mm5
02495                 pslld     mm6, 1
02496                 packssdw  mm6, mm6
02497 
02498                 // index = index >> 2 for pageMap index
02499                 psrld     mm0, 2
02500                 packssdw  mm0, mm0
02501 
02502                 // check 
02503                 push      ecx
02504 
02505                 
02506 
02507                 push      [workAddr]
02508                 movd      edx, mm6
02509                 push      edx // packShiftAmount
02510                 movd      edx, mm0
02511                 push      edx // packIndex4
02512                 push      eax // packTest
02513                 push      dword ptr [ebx+4] // val2
02514                 push      dword ptr [ebx] // val1
02515                 mov       ecx, [this]
02516                 call      ConservativeMarkMMXHelper
02517                     
02518                 pop       ecx
02519 
02520             Advance:
02521                 add       ebx, 8
02522                 loop      MarkLoop
02523                 mov       dword ptr [p], ebx                
02524             }
02525         }
02526     }
02527 
02528 #endif
02529 
02530 #ifdef MMGC_INTERIOR_PTRS
02531     inline bool IsLargeAllocPage(int bits) {
02532         return (bits == GC::kGCLargeAllocPageFirst
02533                 || bits == GC::kGCLargeAllocPageRest);
02534     }
02535 #else
02536     inline bool IsLargeAllocPage(int bits) {
02537         return bits == GC::kGCLargeAllocPageFirst;
02538     }
02539 #endif
02540 
02541 #ifdef WERNER_MODE
02542 #define RECURSIVE_MARK
02543 
02544     class MarkList
02545     {
02546     public:
02547         static MarkList *current;
02548         static int offset;
02549         MarkList(GCWorkItem &item) : prev(current), wi(item), off(offset)
02550         {
02551             current = this;
02552         }
02553         ~MarkList() { current = prev; }
02554         MarkList *prev;
02555         GCWorkItem &wi;
02556         int off;
02557     };
02558     MarkList *MarkList::current = NULL;
02559     int MarkList::offset = -1;
02560 #endif
02561 
02562     void GC::MarkItem(GCWorkItem &wi, GCStack<GCWorkItem> &work)
02563     {
02564         size_t size = wi.GetSize();
02565         uintptr *p = (uintptr*) wi.ptr;
02566 
02567 #ifdef WERNER_MODE
02568         MarkList me(wi);
02569         
02570         if(p == shouldGo) {
02571             MarkList *wl = MarkList::current;
02572             while(wl) {
02573                 const char *name = "";
02574 // To enable RTTI, you must change all your projects to use RTTI first.  
02575 #if 0
02576                 static bool tryit = true;
02577                 if (tryit)
02578                 {
02579                     try {
02580                         const std::type_info *ti = &typeid(*(MMgc::GCFinalizedObject*)wl->wi.ptr);
02581                         if (ti->name() && (int(ti->name()) > 0x10000))
02582                             name = ti->name();
02583                     } catch(...) {
02584                         name = "unknown";
02585                     }
02586                 }
02587 #endif
02588 
02589                 if(wl->prev)
02590                     sprintf(statusBuffer, "0x%x+%d -> 0x%x size=%d (%s)\n",  (unsigned int)wl->prev->wi.ptr, wl->off, (unsigned int)wl->wi.ptr, wl->wi.GetSize(), name);
02591                 else
02592                     sprintf(statusBuffer, "0x%x : %d (%s)\n", (unsigned int)wl->wi.ptr, wl->wi.GetSize(), name);
02593                 wl = wl->prev;
02594                 OutputDebugString(statusBuffer);
02595             }
02596             sprintf(statusBuffer, "\n");
02597             OutputDebugString(statusBuffer);
02598             //shouldGo = NULL;
02599         }
02600 #endif
02601         bytesMarked += size;
02602         marks++;
02603 
02604         uintptr *end = p + (size / sizeof(void*));
02605         uintptr thisPage = (uintptr)p & ~0xfff;
02606 
02607         // set the mark bits on this guy
02608         if(wi.IsGCItem())
02609         {
02610             int b = SetMark(wi.ptr);
02611             (void)b;
02612 #ifdef _DEBUG
02613             // def ref validation does a Trace which can 
02614             // cause things on the work queue to be already marked
02615             // in incremental GC
02616             if(!validateDefRef) {
02617                 GCAssert(!b);
02618             }
02619 #endif          
02620         }
02621         else
02622         {
02623             GCAssert(!IsPointerToGCPage(wi.ptr));
02624         }
02625 
02626         uintptr _memStart = memStart;
02627         uintptr _memEnd = memEnd;
02628         
02629 #ifdef DEBUGGER
02630         numObjects++;
02631         objSize+=(uint32)size;
02632 #endif
02633 
02634         while(p < end) 
02635         {
02636 #ifdef WERNER_MODE
02637             MarkList::offset = (int)p - (int)wi.ptr;
02638 #endif
02639 
02640             uintptr val = *p++;  
02641 
02642             if(val < _memStart || val >= _memEnd)
02643                 continue;
02644 
02645             // normalize and divide by 4K to get index
02646             int bits = GetPageMapValue(val); 
02647             
02648             if (bits == kGCAllocPage)
02649             {
02650                 const void *item;
02651                 GCAlloc::GCBlock *block = (GCAlloc::GCBlock*) (val & ~0xFFF);
02652 
02653 #ifdef MMGC_INTERIOR_PTRS
02654                 item = (void*) val;
02655 #else
02656                 // back up to real beginning
02657                 item = GetRealPointer((const void*) (val & ~7));
02658 #endif
02659 
02660                 // guard against bogus pointers to the block header
02661                 if(item < block->items)
02662                     continue;
02663 
02664                 int itemNum = GCAlloc::GetIndex(block, item);
02665 #ifdef MMGC_INTERIOR_PTRS
02666                 // adjust |item| to the beginning of the allocation
02667                 item = block->items + itemNum * block->size;
02668 #else
02669                 // if |item| doesn't point to the beginning of an allocation,
02670                 // it's not considered a pointer.
02671                 if (block->items + itemNum * block->size != item)
02672                 {
02673 #ifdef MMGC_64BIT
02674 // Doubly-inherited classes have two vtables so are offset 8 more bytes than normal. 
02675 // Handle that here (shows up with PlayerScriptBufferImpl object in the Flash player)
02676                     if ((block->items + itemNum * block->size + sizeof(void *)) == item)
02677                         item = block->items + itemNum * block->size;
02678                     else
02679 #endif // MMGC_64BIT
02680                         continue;
02681                 }
02682 #endif
02683 
02684                 // inline IsWhite/SetBit
02685                 // FIXME: see if using 32 bit values is faster
02686                 uint32 *pbits = &block->GetBits()[itemNum>>3];
02687                 int shift = (itemNum&0x7)<<2;
02688                 int bits2 = *pbits;
02689                 //if(GCAlloc::IsWhite(block, itemNum)) 
02690                 if((bits2 & ((GCAlloc::kMark|GCAlloc::kQueued)<<shift)) == 0)
02691                 {
02692                     if(block->alloc->ContainsPointers())
02693                     {
02694                         const void *realItem = item;
02695                         uint32 itemSize = block->size;
02696                         #ifdef MEMORY_INFO
02697                         realItem = GetUserPointer(realItem);
02698                         itemSize -= (uint32)DebugSize();
02699                         #endif
02700                         if(((uintptr)realItem & ~0xfff) != thisPage)
02701                         {                           
02702                             *pbits = bits2 | (GCAlloc::kQueued << shift);
02703                             block->gc->PushWorkItem(work, GCWorkItem(realItem, itemSize, true));
02704                         }
02705                         else
02706                         {
02707                             // clear queued bit
02708                             *pbits = bits2 & ~(GCAlloc::kQueued << shift);
02709                             // skip stack for same page items, this recursion is naturally limited by
02710                             // how many item can appear on a page, worst case is 8 byte linked list or
02711                             // 512
02712                             GCWorkItem newItem(realItem, itemSize, true);
02713                             MarkItem(newItem, work);
02714                         }
02715                     }
02716                     else
02717                     {
02718                         //GCAlloc::SetBit(block, itemNum, GCAlloc::kMark);
02719                         *pbits = bits2 | (GCAlloc::kMark << shift);
02720                     }
02721                     #if defined(MEMORY_INFO)
02722                     GC::WriteBackPointer(item, (end==(void*)0x130000) ? p-1 : wi.ptr, block->size);
02723                     #endif
02724                 }
02725             }
02726             else if (IsLargeAllocPage(bits))
02727             {
02728                 //largeAlloc->ConservativeMark(work, (void*) (val&~7), workitem.ptr);
02729                 const void* item;
02730 
02731 #ifdef MMGC_INTERIOR_PTRS
02732                 if (bits == kGCLargeAllocPageFirst)
02733                 {
02734                     // guard against bogus pointers to the block header
02735                     if ((val & 0xffff) < sizeof(GCLargeAlloc::LargeBlock))
02736                         continue;
02737 
02738                     item = (void *) ((val & ~0xfff) |
02739                                      sizeof(GCLargeAlloc::LargeBlock));
02740                 }
02741                 else
02742                 {
02743                     item = GetRealPointer(FindBeginning((void *) val));
02744                 }
02745 #else
02746                 // back up to real beginning
02747                 item = GetRealPointer((const void*) (val & ~7));
02748 
02749                 // If |item| doesn't point to the start of the page, it's not
02750                 // really a pointer.
02751                 if(((uintptr) item & 0xfff) != sizeof(GCLargeAlloc::LargeBlock))
02752                     continue;
02753 #endif
02754 
02755                 GCLargeAlloc::LargeBlock *b = GCLargeAlloc::GetBlockHeader(item);
02756                 if((b->flags & (GCLargeAlloc::kQueuedFlag|GCLargeAlloc::kMarkFlag)) == 0) 
02757                 {
02758                     uint32 usize = b->usableSize;
02759                     if((b->flags & GCLargeAlloc::kContainsPointers) != 0) 
02760                     {
02761                         b->flags |= GCLargeAlloc::kQueuedFlag;
02762                         const void *realItem = item;
02763                         #ifdef MEMORY_INFO
02764                         realItem = GetUserPointer(item);
02765                         usize -= (uint32)DebugSize();
02766                         #endif
02767                         b->gc->PushWorkItem(work, GCWorkItem(realItem, usize, true));
02768                     } 
02769                     else
02770                     {
02771                         // doesn't need marking go right to black
02772                         b->flags |= GCLargeAlloc::kMarkFlag;
02773                     }
02774                     #if defined(MEMORY_INFO)
02775                     GC::WriteBackPointer(item, end==(void*)0x130000 ? p-1 : wi.ptr, usize);
02776                     #endif
02777                 }
02778             }
02779         }
02780     }
02781 
02782     uint64 GC::GetPerformanceCounter()
02783     {
02784     #if defined(MMGC_PORTING_API)
02785         return MMGC_PortAPI_Time();
02786     #else
02787         #ifdef WIN32
02788         LARGE_INTEGER value;
02789         QueryPerformanceCounter(&value);
02790         return value.QuadPart;
02791         #elif defined SOLARIS
02792         uint64 retval = gethrtime();
02793         return retval;
02794         #elif defined(_MAC)
02795         return mach_absolute_time();
02796         #elif defined(AVMPLUS_UNIX)
02797         struct timeval tv;
02798         ::gettimeofday(&tv, NULL);
02799 
02800         uint64 seconds = (uint64)(tv.tv_sec * 1000000);
02801         uint64 microseconds = (uint64)tv.tv_usec;
02802         uint64 result = seconds + microseconds;
02803         
02804         return result;
02805         #else
02806         #error "Need high res timer"
02807         #endif
02808     #endif // MMMGC_PORTING_API
02809     }
02810 
02811     uint64 GC::GetPerformanceFrequency()
02812     {
02813     #if defined(MMGC_PORTING_API)
02814         return MMGC_PortAPI_Frequency();
02815     #else
02816         #ifdef WIN32
02817         static uint64 gPerformanceFrequency = 0;        
02818         if (gPerformanceFrequency == 0) {
02819             QueryPerformanceFrequency((LARGE_INTEGER*)&gPerformanceFrequency);
02820         }
02821         return gPerformanceFrequency;
02822         #elif defined(_MAC)
02823         static mach_timebase_info_data_t info;
02824         static uint64 frequency = 0;
02825         if ( frequency == 0 ) {
02826             (void) mach_timebase_info(&info);
02827             frequency = (uint64) ( 1.0 / ( 1e-9 * (double) info.numer / (double) info.denom ) );
02828         }
02829         return frequency;
02830         #elif defined(AVMPLUS_UNIX)
02831         return 1000000;
02832         #else
02833         #error "need high res time impl"
02834         #endif
02835     #endif // MMGC_PORTING_API
02836     }
02837 
02838     void GC::IncrementalMark()
02839     {
02840         MMGC_ASSERT_EXCLUSIVE_GC(this);
02841         uint32 time = incrementalValidation ? 1 : 5;
02842 #ifdef _DEBUG
02843         time = 1;
02844 #endif
02845 
02846         SAMPLE_FRAME("[mark]", core());
02847         if(m_incrementalWork.Count() == 0 || hitZeroObjects) {
02848             FinishIncrementalMark();
02849             return;
02850         } 
02851 
02852 #ifdef DEBUGGER
02853         StartGCActivity();
02854 #endif
02855         
02856         markIncrements++;
02857         // FIXME: tune this so that getPerformanceCounter() overhead is noise
02858         static unsigned int checkTimeIncrements = 100;
02859         uint64 start = GetPerformanceCounter();
02860 
02861 #ifdef DEBUGGER
02862         numObjects=0;
02863         objSize=0;
02864 #endif
02865 
02866         uint64 ticks = start + time * GetPerformanceFrequency() / 1000;
02867         do {
02868             unsigned int count = m_incrementalWork.Count();
02869             if (count == 0) {
02870                 hitZeroObjects = true;
02871                 break;
02872             }
02873             if (count > checkTimeIncrements) {
02874                 count = checkTimeIncrements;
02875             }
02876             for(unsigned int i=0; i<count; i++) 
02877             {
02878                 MarkItem(m_incrementalWork);
02879             }
02880             SAMPLE_CHECK();
02881         } while(GetPerformanceCounter() < ticks);
02882 
02883         lastMarkTicks = GetPerformanceCounter();
02884         markTicks += lastMarkTicks - start;
02885 
02886         if(gcstats) {
02887             double millis = duration(start);
02888             uint32 kb = objSize>>10;
02889             gclog("[mem] mark(%d) %d objects (%d kb %d mb/s) in %.2f millis (%.4f s)\n", 
02890                 markIncrements-lastStartMarkIncrementCount, numObjects, kb, 
02891                 uint32(double(kb)/millis), millis, duration(t0)/1000);
02892         }
02893     }
02894 
02895     void GC::FinishIncrementalMark()
02896     {
02897         MMGC_ASSERT_EXCLUSIVE_GC(this);
02898 
02899         // Don't finish an incremental mark (i.e., sweep) if we
02900         // are in the midst of a ZCT reap.
02901         if (zct.reaping)
02902         {
02903             return;
02904         }
02905         
02906         hitZeroObjects = false;
02907 
02908         // finished in Sweep
02909         sweepStart = GetPerformanceCounter();
02910         
02911         // mark roots again, could have changed (alternative is to put WB's on the roots
02912         // which we may need to do if we find FinishIncrementalMark taking too long)
02913         
02914         {
02915 #ifdef GCHEAP_LOCK
02916             GCAcquireSpinlock lock(m_rootListLock);
02917 #endif
02918             GCRoot *r = m_roots;
02919             while(r) {                  
02920                 GCWorkItem item = r->GetWorkItem();
02921                 // need to do this while holding the root lock so we don't end 
02922                 // up trying to scan a deleted item later, another reason to keep
02923                 // the root set small
02924                 if(item.ptr) {
02925                     MarkItem(item, m_incrementalWork);
02926                 }
02927                 r = r->next;
02928             }
02929         }
02930         MarkQueueAndStack(m_incrementalWork);
02931 
02932 #ifdef _DEBUG
02933         // need to traverse all marked objects and make sure they don't contain
02934         // pointers to unmarked objects
02935         FindMissingWriteBarriers();
02936 #endif
02937 
02938         GCAssert(!collecting);
02939         collecting = true;
02940         GCAssert(m_incrementalWork.Count() == 0);
02941         Sweep();
02942         GCAssert(m_incrementalWork.Count() == 0);
02943         collecting = false;
02944         marking = false;
02945     }
02946 
02947     int GC::IsWhite(const void *item)
02948     {
02949         // back up to real beginning
02950         item = GetRealPointer((const void*) item);
02951 
02952         // normalize and divide by 4K to get index
02953         if(!IsPointerToGCPage(item))
02954             return false;
02955         int bits = GetPageMapValue((uintptr)item);  
02956         switch(bits) {
02957         case 1:
02958             return GCAlloc::IsWhite(item);
02959         case 3:
02960             // FIXME: we only want pointers to the first page for large items, fix
02961             // this by marking the first page and subsequent pages of large items differently
02962             // in the page map (ie use 2).
02963             return GCLargeAlloc::IsWhite(item);
02964         }
02965         return false;
02966     }
02967     
02968     // TODO: fix headers so this can be declared there and inlined
02969     void GC::WriteBarrierWrite(const void *address, const void *value)
02970     {
02971         GCAssert(!IsRCObject(value));
02972         *(uintptr*)address = (uintptr) value;
02973     }
02974 
02975     // optimized version with no RC checks or pointer swizzling
02976     void GC::writeBarrierRC(const void *container, const void *address, const void *value)
02977     {
02978         GCAssert(IsPointerToGCPage(container));
02979         GCAssert(((uintptr)container & 3) == 0);
02980         GCAssert(((uintptr)address & 2) == 0);
02981         GCAssert(address >= container);
02982         GCAssert(address < (char*)container + Size(container));
02983 
02984         WriteBarrierNoSubstitute(container, value);
02985         WriteBarrierWriteRC(address, value);
02986     }
02987 
02988     // TODO: fix headers so this can be declared there and inlined
02989     void GC::WriteBarrierWriteRC(const void *address, const void *value)
02990     {
02991         #ifdef MMGC_DRC
02992             RCObject *rc = (RCObject*)Pointer(*(RCObject**)address);
02993             if(rc != NULL) {
02994                 GCAssert(rc == FindBeginning(rc));
02995                 GCAssert(IsRCObject(rc));
02996                 rc->DecrementRef();
02997             }
02998         #endif
02999         *(uintptr*)address = (uintptr) value;
03000         #ifdef MMGC_DRC     
03001             rc = (RCObject*)Pointer(value);
03002             if(rc != NULL) {
03003                 GCAssert(IsRCObject(rc));
03004                 GCAssert(rc == FindBeginning(value));
03005                 rc->IncrementRef();
03006             }
03007         #endif
03008     }
03009 
03010     void GC::WriteBarrier(const void *address, const void *value)
03011     {
03012         GC* gc = GC::GetGC(address);
03013         if(Pointer(value) != NULL && gc->marking) {
03014             void *container = gc->FindBeginning(address);
03015             gc->WriteBarrierNoSubstitute(container, value);
03016         }
03017         gc->WriteBarrierWrite(address, value);
03018     }
03019 
03020     void GC::WriteBarrierNoSub(const void *address, const void *value)
03021     {
03022         GC *gc = NULL;
03023         if(value != NULL && (gc = GC::GetGC(address))->marking) {
03024             void *container = gc->FindBeginning(address);
03025             gc->WriteBarrierNoSubstitute(container, value);     
03026         }
03027     }
03028 
03029     void GC::TrapWrite(const void *black, const void *white)
03030     {
03031         // assert fast path preconditions
03032         (void)black;
03033         GCAssert(marking);
03034         GCAssert(GetMark(black));
03035         GCAssert(IsWhite(white));
03036         // currently using the simplest finest grained implementation,
03037         // which could result in huge work queues.  should try the
03038         // more granular approach of moving the black object to grey
03039         // (smaller work queue, less frequent wb slow path) but if the
03040         // black object is big we end up doing useless redundant
03041         // marking.  optimal approach from lit is card marking (mark a
03042         // region of the black object as needing to be re-marked)
03043         if(ContainsPointers(white)) {
03044             SetQueued(white);
03045             PushWorkItem(m_incrementalWork, GCWorkItem(white, (uint32)Size(white), true));
03046         } else {
03047             SetMark(white);
03048         }
03049     }
03050 
03051     bool GC::ContainsPointers(const void *item)
03052     {
03053         item = GetRealPointer(item);
03054         if (GCLargeAlloc::IsLargeBlock(item)) {
03055             return GCLargeAlloc::ContainsPointers(item);
03056         } else {
03057             return GCAlloc::ContainsPointers(item);
03058         }
03059     }
03060 
03061     bool GC::IsGCMemory (const void *item)
03062     {
03063         int bits = GetPageMapValue((uintptr)item);
03064         return (bits != 0);
03065     }
03066 
03067     bool GC::IsQueued(const void *item)
03068     {
03069         return !GetMark(item) && !IsWhite(item);
03070     }
03071 
03072     uint32 *GC::GetBits(int numBytes, int sizeClass)
03073     {
03074         uint32 *bits;
03075 
03076         MMGC_ASSERT_GC_LOCK(this);
03077         GCAssert(numBytes % 4 == 0);
03078 
03079         #ifdef MMGC_64BIT // we use first 8-byte slot for the free list
03080         if (numBytes == 4)
03081             numBytes = 8;
03082         #endif
03083 
03084         // hit freelists first
03085         if(m_bitsFreelists[sizeClass]) {
03086             bits = m_bitsFreelists[sizeClass];
03087             m_bitsFreelists[sizeClass] = *(uint32**)bits;
03088             memset(bits, 0, sizeof(uint32*));
03089             return bits;
03090         }
03091 
03092         if(!m_bitsNext)
03093             m_bitsNext = (uint32*)heap->Alloc(1);
03094 
03095         int leftOver = GCHeap::kBlockSize - ((uintptr)m_bitsNext & 0xfff);
03096         if(leftOver >= numBytes) {
03097             bits = m_bitsNext;
03098             if(leftOver == numBytes) 
03099                 m_bitsNext = 0;
03100             else 
03101                 m_bitsNext += numBytes/sizeof(uint32);
03102         } else {
03103             if(leftOver>=int(sizeof(void*))) {
03104                 // put waste in freelist
03105                 for(int i=0, n=kNumSizeClasses; i<n; i++) {
03106                     GCAlloc *a = noPointersAllocs[i];
03107                     if(!a->m_bitsInPage && a->m_numBitmapBytes <= leftOver) {
03108                         FreeBits(m_bitsNext, a->m_sizeClassIndex);
03109                         break;
03110                     }
03111                 }
03112             }
03113             m_bitsNext = 0;
03114             // recurse rather than duplicating code
03115             return GetBits(numBytes, sizeClass);
03116         }
03117         return bits;
03118     }
03119 
03120     void GC::AddRoot(GCRoot *root)
03121     {
03122 #ifdef GCHEAP_LOCK
03123         GCAcquireSpinlock lock(m_rootListLock);
03124 #endif
03125         root->prev = NULL;
03126         root->next = m_roots;
03127         if(m_roots)
03128             m_roots->prev = root;
03129         m_roots = root;
03130     }
03131 
03132     void GC::RemoveRoot(GCRoot *root)
03133     {       
03134 #ifdef GCHEAP_LOCK
03135         GCAcquireSpinlock lock(m_rootListLock);
03136 #endif
03137         if( m_roots == root )
03138             m_roots = root->next;
03139         else
03140             root->prev->next = root->next;
03141 
03142         if(root->next)
03143             root->next->prev = root->prev;
03144     }
03145     
03146     void GC::AddCallback(GCCallback *cb)
03147     {
03148         USING_CALLBACK_LIST(this);
03149 
03150         cb->prevCB = NULL;
03151         cb->nextCB = m_callbacks;
03152         if(m_callbacks)
03153             m_callbacks->prevCB = cb;
03154         m_callbacks = cb;
03155     }
03156 
03157     void GC::RemoveCallback(GCCallback *cb)
03158     {
03159         USING_CALLBACK_LIST(this);
03160 
03161         if( m_callbacks == cb )
03162             m_callbacks = cb->nextCB;
03163         else
03164             cb->prevCB->nextCB = cb->nextCB;
03165 
03166         if(cb->nextCB)
03167             cb->nextCB->prevCB = cb->prevCB;
03168     }
03169 
03170     void GC::PushWorkItem(GCStack<GCWorkItem> &stack, GCWorkItem item)
03171     {
03172 #ifdef RECURSIVE_MARK
03173         MarkItem(item, stack);
03174 #else
03175         if(item.ptr) {
03176             stack.Push(item);
03177         }
03178 #endif
03179     }
03180 
03181     GCWeakRef* GC::GetWeakRef(const void *item) 
03182     {
03183         GC *gc = GetGC(item);
03184 #ifdef MMGC_THREADSAFE
03185         GCAutoLock _lock(gc->m_lock);
03186 #endif
03187         GCWeakRef *ref = (GCWeakRef*) gc->weakRefs.get(item);
03188 
03189         if(ref == NULL) {
03190             ref = new (gc) GCWeakRef(item);
03191             gc->weakRefs.put(item, ref);
03192             item = GetRealPointer(item);
03193             if (GCLargeAlloc::IsLargeBlock(item)) {
03194                 GCLargeAlloc::SetHasWeakRef(item, true);
03195             } else {
03196                 GCAlloc::SetHasWeakRef(item, true);
03197             }
03198         } else {
03199             GCAssert(ref->get() == item);
03200         }
03201         return ref;
03202     }
03203 
03204     void GC::ClearWeakRef(const void *item)
03205     {
03206         GCWeakRef *ref = (GCWeakRef*) weakRefs.remove(item);
03207         GCAssert(weakRefs.get(item) == NULL);
03208         GCAssert(ref != NULL);
03209         GCAssert(ref->get() == item || ref->get() == NULL);
03210         if(ref) {
03211             ref->m_obj = NULL;
03212             item = GetRealPointer(item);
03213             if (GCLargeAlloc::IsLargeBlock(item)) {
03214                 GCLargeAlloc::SetHasWeakRef(item, false);
03215             } else {
03216                 GCAlloc::SetHasWeakRef(item, false);
03217             }
03218         }
03219     }
03220 
03221 #ifdef _DEBUG
03222     
03223     void GC::WhitePointerScan(const void *mem, size_t size)
03224     {       
03225         uintptr *p = (uintptr *) mem;
03226         // the minus 8 skips the deadbeef and back pointer 
03227         uintptr *end = p + ((size) / sizeof(void*));
03228 
03229         while(p < end)
03230         {
03231             uintptr val = *p;       
03232             if(val == 0xdeadbeef)
03233                 break;
03234             if(IsWhite((const void*) (val&~7)) && 
03235                *(((int32*)(val&~7))+1) != (int32)0xcacacaca && // Free'd
03236                *(((int32*)(val&~7))+1) != (int32)0xbabababa) // Swept
03237             {
03238 #ifdef MEMORY_INFO
03239                 GCDebugMsg(false, "Object 0x%x allocated here:\n", mem);
03240                 PrintStackTrace(mem);
03241                 GCDebugMsg(false, "Didn't mark pointer at 0x%x, object 0x%x allocated here:\n", p, val);
03242                 PrintStackTrace((const void*)(val&~7));
03243 #endif
03244                 GCAssert(false);
03245             }
03246             p++;
03247         }
03248     }
03249 
03250     void GC::FindMissingWriteBarriers()
03251     {
03252         MMGC_ASSERT_EXCLUSIVE_GC(this);
03253 
03254         if(!incrementalValidation)
03255             return;
03256 
03257         uintptr m = memStart;
03258         while(m < memEnd)
03259         {
03260             // divide by 4K to get index
03261             int bits = GetPageMapValue(m);
03262             switch(bits)
03263             {
03264             case 0:
03265                 m += GCHeap::kBlockSize;
03266                 break;
03267             case 3:
03268                 {
03269                     GCLargeAlloc::LargeBlock *lb = (GCLargeAlloc::LargeBlock*)m;
03270                     const void *item = GetUserPointer((const void*)(lb+1));
03271                     if(GCLargeAlloc::GetMark(item) && GCLargeAlloc::ContainsPointers(item)) {
03272                         WhitePointerScan(item, lb->usableSize - DebugSize());
03273                     }
03274                     m += lb->GetNumBlocks() * GCHeap::kBlockSize;
03275                 }
03276                 break;
03277             case 1:
03278                 {
03279                     // go through all marked objects in this page
03280                     GCAlloc::GCBlock *b = (GCAlloc::GCBlock *) m;
03281                     for (int i=0; i< b->alloc->m_itemsPerBlock; i++) {
03282                         // find all marked objects and search them
03283                         if(!GCAlloc::GetBit(b, i, GCAlloc::kMark))
03284                             continue;
03285 
03286                         if(b->alloc->ContainsPointers()) {
03287                             void* item = (char*)b->items + b->alloc->m_itemSize*i;
03288                             WhitePointerScan(GetUserPointer(item), b->alloc->m_itemSize - DebugSize());
03289                         }
03290                     }
03291                     m += GCHeap::kBlockSize;
03292                 }
03293                 break;
03294             default:
03295                 GCAssert(false);
03296                 break;
03297             }
03298         }
03299     }
03300 #endif
03301 
03302 #ifdef DEBUGGER
03303     void GC::StartGCActivity()
03304     {
03305         // invoke postsweep callback
03306         USING_CALLBACK_LIST(this);
03307         GCCallback *cb = m_callbacks;
03308         while(cb) {
03309             cb->startGCActivity();
03310             cb = cb->nextCB;
03311         } 
03312     }
03313 
03314     void GC::StopGCActivity()
03315     {
03316         // invoke postsweep callback
03317         USING_CALLBACK_LIST(this);
03318         GCCallback *cb = m_callbacks;
03319         while(cb) {
03320             cb->stopGCActivity();
03321             cb = cb->nextCB;
03322         }
03323     }
03324 
03325     void GC::AllocActivity(int blocks)
03326     {
03327         // invoke postsweep callback
03328         USING_CALLBACK_LIST(this);
03329         GCCallback *cb = m_callbacks;
03330         while(cb) {
03331             cb->allocActivity(blocks);
03332             cb = cb->nextCB;
03333         }
03334     }
03335 #endif  /* DEBUGGER*/
03336 
03337 #if defined(MMGC_PORTING_API)
03338 uintptr_t   GC::GetStackTop() const
03339 {
03340     return MMGC_PortAPI_GetStackTop();
03341 }
03342 #else
03343 #if defined(_MAC) && (defined(MMGC_IA32) || defined(MMGC_AMD64)) || defined(MMGC_MAC_NO_CARBON)
03344     uintptr GC::GetStackTop() const
03345     {
03346         return (uintptr)pthread_get_stackaddr_np(pthread_self());
03347     }
03348 #endif
03349 
03350 #if defined(LINUX) && defined(MMGC_ARM) && !defined(AVMPLUS_UNIX)
03351     uintptr GC::GetStackTop() const
03352     {
03353         void* sp;
03354         pthread_attr_t attr;
03355         pthread_getattr_np(pthread_self(), &attr);
03356         pthread_attr_getstackaddr(&attr, &sp);
03357         return (uintptr)sp;
03358     }
03359 #endif
03360 #endif /*<<GC_PORTING_API*/
03361 
03362     void *GC::heapAlloc(size_t siz, bool expand, bool zero)
03363     {
03364         void *ptr = heap->Alloc((int)siz, expand, zero);
03365         if(ptr)
03366             totalGCPages += siz;
03367         return ptr;
03368     }
03369     
03370     void GC::heapFree(void *ptr, size_t siz)
03371     {
03372         if(!siz)
03373             siz = heap->Size(ptr);
03374         totalGCPages -= siz;
03375         heap->Free(ptr);
03376     }   
03377     
03378     void GC::log_mem(const char *name, size_t bytes, size_t bytes_compare)
03379     {
03380         bytes_compare = size_t((bytes*100.0)/bytes_compare);
03381         if(bytes > 1<<20) {
03382             gclog("%s %d (%.1fM) %d%%\n", name, bytes / GCHeap::kBlockSize, bytes * 1.0 / (1024*1024), bytes_compare);
03383         } else {
03384             gclog("%s %d (%dK) %d%%\n", name, bytes / GCHeap::kBlockSize, bytes / 1024, bytes_compare);
03385         }
03386     }
03387     
03388     size_t GC::GetBytesInUse()
03389     {
03390         size_t bytes=0;
03391         for(int i=0; i < kNumSizeClasses; i++) {
03392 #ifdef MMGC_DRC
03393             bytes += containsPointersRCAllocs[i]->GetBytesInUse();
03394 #endif
03395             bytes += containsPointersAllocs[i]->GetBytesInUse();
03396             bytes += noPointersAllocs[i]->GetBytesInUse();
03397         }
03398         bytes += largeAlloc->GetBytesInUse();
03399         return bytes;
03400     }
03401 
03402     void GC::updateGrossMemoryStats()
03403     {
03404         size_t priv = GCHeap::GetPrivateBytes() * GCHeap::kBlockSize;
03405         size_t mmgc = heap->GetTotalHeapSize() * GCHeap::kBlockSize;
03406         size_t unmanaged = FixedMalloc::GetInstance()->GetTotalSize() * GCHeap::kBlockSize;
03407         size_t jit = (size_t)stats.get("jit") * GCHeap::kBlockSize;
03408         size_t gc_alloced = GetBytesInUse();
03409         size_t fixed_alloced = FixedMalloc::GetInstance()->GetBytesInUse();
03410         size_t gc = totalGCPages * GCHeap::kBlockSize;
03411         gclog("[mem] ------- gross stats -----\n");
03412         log_mem("[mem] private", priv, priv);
03413         log_mem("[mem]\t mmgc", mmgc, priv);
03414         log_mem("[mem]\t\t unmanaged", unmanaged, priv);
03415         log_mem("[mem]\t\t managed", gc, priv);
03416         log_mem("[mem]\t\t free",  (size_t)heap->GetFreeHeapSize() * GCHeap::kBlockSize, priv);
03417         log_mem("[mem]\t jit", jit, priv);
03418         log_mem("[mem]\t other",  priv - jit - mmgc, priv);
03419         log_mem("[mem] bytes (interal fragmentation)", fixed_alloced + gc_alloced, gc + unmanaged);
03420         log_mem("[mem] \tmanaged bytes ", gc_alloced, gc);
03421         log_mem("[mem] \tunmanaged bytes ", fixed_alloced, unmanaged);
03422         gclog("[mem] -------- gross stats end -----\n");
03423     }
03424 
03425 #if defined (FEATURE_SAMPLER)
03426     // For sampling support
03427     GCThreadLocal<avmplus::Sampler*> m_sampler;
03428     bool sampling = false;
03429 
03430     void recordAllocationSample(void* item, size_t size, bool in_lock)
03431     {
03432         avmplus::Sampler* sampler = m_sampler;
03433         if( sampler && sampler->sampling )
03434             sampler->recordAllocationSample(item, size, !in_lock);
03435     }
03436 
03437     void recordDeallocationSample(const void* item, size_t size)
03438     {
03439         avmplus::Sampler* sampler = m_sampler;
03440         if( sampler /*&& sampler->sampling*/ )
03441             sampler->recordDeallocationSample(item, size);
03442     }
03443 #endif
03444 
03445 }

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