Home | History | Annotate | Download | only in alloc
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <errno.h>
     18 #include <limits.h>
     19 #include <sys/mman.h>
     20 
     21 #include "Dalvik.h"
     22 #include "alloc/Heap.h"
     23 #include "alloc/HeapBitmap.h"
     24 #include "alloc/HeapInternal.h"
     25 #include "alloc/HeapSource.h"
     26 #include "alloc/Verify.h"
     27 #include "alloc/clz.h"
     28 
     29 /*
     30  * A "mostly copying", generational, garbage collector.
     31  *
     32  * TODO: we allocate our own contiguous tract of page frames to back
     33  * object allocations.  To cooperate with other heaps active in the
     34  * virtual machine we need to move the responsibility of allocating
     35  * pages someplace outside of this code.
     36  *
     37  * The other major data structures that maintain the state of the heap
     38  * are the block space table and the block queue.
     39  *
     40  * The block space table records the state of a block.  We must track
     41  * whether a block is:
     42  *
     43  * - Free or allocated in some space.
     44  *
     45  * - If the block holds part of a large object allocation, whether the
     46  *   block is the initial or a continued block of the allocation.
     47  *
     48  * - Whether the block is pinned, that is to say whether at least one
     49  *   object in the block must remain stationary.  Only needed during a
     50  *   GC.
     51  *
     52  * - Which space the object belongs to.  At present this means
     53  *   from-space or to-space.
     54  *
     55  * The block queue is used during garbage collection.  Unlike Cheney's
     56  * algorithm, from-space and to-space are not contiguous.  Therefore,
     57  * one cannot maintain the state of the copy with just two pointers.
     58  * The block queue exists to thread lists of blocks from the various
     59  * spaces together.
     60  *
     61  * Additionally, we record the free space frontier of the heap, as
     62  * well as the address of the first object within a block, which is
     63  * required to copy objects following a large object (not currently
     64  * implemented).  This is stored in the heap source structure.  This
     65  * should be moved elsewhere to support in-line allocations from Java
     66  * threads.
     67  *
     68  * Allocation requests are satisfied by reserving storage from one or
     69  * more contiguous blocks.  Objects that are small enough to fit
     70  * inside a block are packed together within a block.  Objects that
     71  * are larger than a block are allocated from contiguous sequences of
     72  * blocks.  When half the available blocks are filled, a garbage
     73  * collection occurs.  We "flip" spaces (exchange from- and to-space),
     74  * copy live objects into to space, and perform pointer adjustment.
     75  *
     76  * Copying is made more complicated by the requirement that some
     77  * objects must not be moved.  This property is known as "pinning".
     78  * These objects must be dealt with specially.  We use Bartlett's
     79  * scheme; blocks containing such objects are grayed (promoted) at the
     80  * start of a garbage collection.  By virtue of this trick, tracing
     81  * from the roots proceeds as usual but all objects on those pages are
     82  * considered promoted and therefore not moved.
     83  *
     84  * TODO: there is sufficient information within the garbage collector
     85  * to implement Attardi's scheme for evacuating unpinned objects from
     86  * a page that is otherwise pinned.  This would eliminate false
     87  * retention caused by the large pinning granularity.
     88  *
     89  * We need a scheme for medium and large objects.  Ignore that for
     90  * now, we can return to this later.
     91  *
     92  * Eventually we need to worry about promoting objects out of the
     93  * copy-collected heap (tenuring) into a less volatile space.  Copying
     94  * may not always be the best policy for such spaces.  We should
     95  * consider a variant of mark, sweep, compact.
     96  *
     97  * The block scheme allows us to use VM page faults to maintain a
     98  * write barrier.  Consider having a special leaf state for a page.
     99  *
    100  * Bibliography:
    101  *
    102  * C. J. Cheney. 1970. A non-recursive list compacting
    103  * algorithm. CACM. 13-11 pp677--678.
    104  *
    105  * Joel F. Bartlett. 1988. Compacting Garbage Collection with
    106  * Ambiguous Roots. Digital Equipment Corporation.
    107  *
    108  * Joel F. Bartlett. 1989. Mostly-Copying Garbage Collection Picks Up
    109  * Generations and C++. Digital Equipment Corporation.
    110  *
    111  * G. May Yip. 1991. Incremental, Generational Mostly-Copying Garbage
    112  * Collection in Uncooperative Environments. Digital Equipment
    113  * Corporation.
    114  *
    115  * Giuseppe Attardi, Tito Flagella. 1994. A Customisable Memory
    116  * Management Framework. TR-94-010
    117  *
    118  * Giuseppe Attardi, Tito Flagella, Pietro Iglio. 1998. A customisable
    119  * memory management framework for C++. Software -- Practice and
    120  * Experience. 28(11), 1143-1183.
    121  *
    122  */
    123 
    124 #define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
    125 
    126 #if 0
    127 #define LOG_ALLOC LOGI
    128 #define LOG_PIN LOGI
    129 #define LOG_PROM LOGI
    130 #define LOG_REF LOGI
    131 #define LOG_SCAV LOGI
    132 #define LOG_TRAN LOGI
    133 #define LOG_VER LOGI
    134 #else
    135 #define LOG_ALLOC(...) ((void)0)
    136 #define LOG_PIN(...) ((void)0)
    137 #define LOG_PROM(...) ((void)0)
    138 #define LOG_REF(...) ((void)0)
    139 #define LOG_SCAV(...) ((void)0)
    140 #define LOG_TRAN(...) ((void)0)
    141 #define LOG_VER(...) ((void)0)
    142 #endif
    143 
    144 static void enqueueBlock(HeapSource *heapSource, size_t block);
    145 static void scavengeReference(Object **obj);
    146 static bool toSpaceContains(const void *addr);
    147 static bool fromSpaceContains(const void *addr);
    148 static size_t sumHeapBitmap(const HeapBitmap *bitmap);
    149 static size_t objectSize(const Object *obj);
    150 static void scavengeDataObject(Object *obj);
    151 static void scavengeBlockQueue(void);
    152 
    153 /*
    154  * We use 512-byte blocks.
    155  */
    156 enum { BLOCK_SHIFT = 9 };
    157 enum { BLOCK_SIZE = 1 << BLOCK_SHIFT };
    158 
    159 /*
    160  * Space identifiers, stored into the blockSpace array.
    161  */
    162 enum {
    163     BLOCK_FREE = 0,
    164     BLOCK_FROM_SPACE = 1,
    165     BLOCK_TO_SPACE = 2,
    166     BLOCK_CONTINUED = 7
    167 };
    168 
    169 /*
    170  * Alignment for all allocations, in bytes.
    171  */
    172 enum { ALLOC_ALIGNMENT = 8 };
    173 
    174 /*
    175  * Sentinel value for the queue end.
    176  */
    177 #define QUEUE_TAIL (~(size_t)0)
    178 
    179 struct HeapSource {
    180 
    181     /* The base address of backing store. */
    182     u1 *blockBase;
    183 
    184     /* Total number of blocks available for allocation. */
    185     size_t totalBlocks;
    186     size_t allocBlocks;
    187 
    188     /*
    189      * The scavenger work queue.  Implemented as an array of index
    190      * values into the queue.
    191      */
    192     size_t *blockQueue;
    193 
    194     /*
    195      * Base and limit blocks.  Basically the shifted start address of
    196      * the block.  We convert blocks to a relative number when
    197      * indexing in the block queue.  TODO: make the block queue base
    198      * relative rather than the index into the block queue.
    199      */
    200     size_t baseBlock, limitBlock;
    201 
    202     size_t queueHead;
    203     size_t queueTail;
    204     size_t queueSize;
    205 
    206     /* The space of the current block 0 (free), 1 or 2. */
    207     char *blockSpace;
    208 
    209     /* Start of free space in the current block. */
    210     u1 *allocPtr;
    211     /* Exclusive limit of free space in the current block. */
    212     u1 *allocLimit;
    213 
    214     HeapBitmap allocBits;
    215 
    216     /*
    217      * The starting size of the heap.  This value is the same as the
    218      * value provided to the -Xms flag.
    219      */
    220     size_t minimumSize;
    221 
    222     /*
    223      * The maximum size of the heap.  This value is the same as the
    224      * -Xmx flag.
    225      */
    226     size_t maximumSize;
    227 
    228     /*
    229      * The current, committed size of the heap.  At present, this is
    230      * equivalent to the maximumSize.
    231      */
    232     size_t currentSize;
    233 
    234     size_t bytesAllocated;
    235 };
    236 
    237 static unsigned long alignDown(unsigned long x, unsigned long n)
    238 {
    239     return x & -n;
    240 }
    241 
    242 static unsigned long alignUp(unsigned long x, unsigned long n)
    243 {
    244     return alignDown(x + (n - 1), n);
    245 }
    246 
    247 static void describeBlocks(const HeapSource *heapSource)
    248 {
    249     size_t i;
    250 
    251     for (i = 0; i < heapSource->totalBlocks; ++i) {
    252         if ((i % 32) == 0) putchar('\n');
    253         printf("%d ", heapSource->blockSpace[i]);
    254     }
    255     putchar('\n');
    256 }
    257 
    258 /*
    259  * Virtual memory interface.
    260  */
    261 
    262 static void *virtualAlloc(size_t length)
    263 {
    264     void *addr;
    265     int flags, prot;
    266 
    267     flags = MAP_PRIVATE | MAP_ANONYMOUS;
    268     prot = PROT_READ | PROT_WRITE;
    269     addr = mmap(NULL, length, prot, flags, -1, 0);
    270     if (addr == MAP_FAILED) {
    271         LOGE_HEAP("mmap: %s", strerror(errno));
    272         addr = NULL;
    273     }
    274     return addr;
    275 }
    276 
    277 static void virtualFree(void *addr, size_t length)
    278 {
    279     int res;
    280 
    281     assert(addr != NULL);
    282     assert((uintptr_t)addr % SYSTEM_PAGE_SIZE == 0);
    283     res = munmap(addr, length);
    284     if (res == -1) {
    285         LOGE_HEAP("munmap: %s", strerror(errno));
    286     }
    287 }
    288 
    289 #ifndef NDEBUG
    290 static int isValidAddress(const HeapSource *heapSource, const u1 *addr)
    291 {
    292     size_t block;
    293 
    294     block = (uintptr_t)addr >> BLOCK_SHIFT;
    295     return heapSource->baseBlock <= block &&
    296            heapSource->limitBlock > block;
    297 }
    298 #endif
    299 
    300 /*
    301  * Iterate over the block map looking for a contiguous run of free
    302  * blocks.
    303  */
    304 static void *allocateBlocks(HeapSource *heapSource, size_t blocks)
    305 {
    306     void *addr;
    307     size_t allocBlocks, totalBlocks;
    308     size_t i, j;
    309 
    310     allocBlocks = heapSource->allocBlocks;
    311     totalBlocks = heapSource->totalBlocks;
    312     /* Check underflow. */
    313     assert(blocks != 0);
    314     /* Check overflow. */
    315     if (allocBlocks + blocks > totalBlocks / 2) {
    316         return NULL;
    317     }
    318     /* Scan block map. */
    319     for (i = 0; i < totalBlocks; ++i) {
    320         /* Check fit. */
    321         for (j = 0; j < blocks; ++j) { /* runs over totalBlocks */
    322             if (heapSource->blockSpace[i+j] != BLOCK_FREE) {
    323                 break;
    324             }
    325         }
    326         /* No fit? */
    327         if (j != blocks) {
    328             i += j;
    329             continue;
    330         }
    331         /* Fit, allocate. */
    332         heapSource->blockSpace[i] = BLOCK_TO_SPACE; /* why to-space? */
    333         for (j = 1; j < blocks; ++j) {
    334             heapSource->blockSpace[i+j] = BLOCK_CONTINUED;
    335         }
    336         heapSource->allocBlocks += blocks;
    337         addr = &heapSource->blockBase[i*BLOCK_SIZE];
    338         memset(addr, 0, blocks*BLOCK_SIZE);
    339         /* Collecting? */
    340         if (heapSource->queueHead != QUEUE_TAIL) {
    341             LOG_ALLOC("allocateBlocks allocBlocks=%zu,block#=%zu", heapSource->allocBlocks, i);
    342             /*
    343              * This allocated was on behalf of the transporter when it
    344              * shaded a white object gray.  We enqueue the block so
    345              * the scavenger can further shade the gray objects black.
    346              */
    347             enqueueBlock(heapSource, i);
    348         }
    349 
    350         return addr;
    351     }
    352     /* Insufficient space, fail. */
    353     LOGE("Insufficient space, %zu blocks, %zu blocks allocated and %zu bytes allocated",
    354          heapSource->totalBlocks,
    355          heapSource->allocBlocks,
    356          heapSource->bytesAllocated);
    357     return NULL;
    358 }
    359 
    360 /* Converts an absolute address to a relative block number. */
    361 static size_t addressToBlock(const HeapSource *heapSource, const void *addr)
    362 {
    363     assert(heapSource != NULL);
    364     assert(isValidAddress(heapSource, addr));
    365     return (((uintptr_t)addr) >> BLOCK_SHIFT) - heapSource->baseBlock;
    366 }
    367 
    368 /* Converts a relative block number to an absolute address. */
    369 static u1 *blockToAddress(const HeapSource *heapSource, size_t block)
    370 {
    371     u1 *addr;
    372 
    373     addr = (u1 *) (((uintptr_t) heapSource->baseBlock + block) * BLOCK_SIZE);
    374     assert(isValidAddress(heapSource, addr));
    375     return addr;
    376 }
    377 
    378 static void clearBlock(HeapSource *heapSource, size_t block)
    379 {
    380     u1 *addr;
    381     size_t i;
    382 
    383     assert(heapSource != NULL);
    384     assert(block < heapSource->totalBlocks);
    385     addr = heapSource->blockBase + block*BLOCK_SIZE;
    386     memset(addr, 0xCC, BLOCK_SIZE);
    387     for (i = 0; i < BLOCK_SIZE; i += 8) {
    388         dvmHeapBitmapClearObjectBit(&heapSource->allocBits, addr + i);
    389     }
    390 }
    391 
    392 static void clearFromSpace(HeapSource *heapSource)
    393 {
    394     size_t i, count;
    395 
    396     assert(heapSource != NULL);
    397     i = count = 0;
    398     while (i < heapSource->totalBlocks) {
    399         if (heapSource->blockSpace[i] != BLOCK_FROM_SPACE) {
    400             ++i;
    401             continue;
    402         }
    403         heapSource->blockSpace[i] = BLOCK_FREE;
    404         clearBlock(heapSource, i);
    405         ++i;
    406         ++count;
    407         while (i < heapSource->totalBlocks &&
    408                heapSource->blockSpace[i] == BLOCK_CONTINUED) {
    409             heapSource->blockSpace[i] = BLOCK_FREE;
    410             clearBlock(heapSource, i);
    411             ++i;
    412             ++count;
    413         }
    414     }
    415     LOG_SCAV("freed %zu blocks (%zu bytes)", count, count*BLOCK_SIZE);
    416 }
    417 
    418 /*
    419  * Appends the given block to the block queue.  The block queue is
    420  * processed in-order by the scavenger.
    421  */
    422 static void enqueueBlock(HeapSource *heapSource, size_t block)
    423 {
    424     assert(heapSource != NULL);
    425     assert(block < heapSource->totalBlocks);
    426     if (heapSource->queueHead != QUEUE_TAIL) {
    427         heapSource->blockQueue[heapSource->queueTail] = block;
    428     } else {
    429         heapSource->queueHead = block;
    430     }
    431     heapSource->blockQueue[block] = QUEUE_TAIL;
    432     heapSource->queueTail = block;
    433     ++heapSource->queueSize;
    434 }
    435 
    436 /*
    437  * Grays all objects within the block corresponding to the given
    438  * address.
    439  */
    440 static void promoteBlockByAddr(HeapSource *heapSource, const void *addr)
    441 {
    442     size_t block;
    443 
    444     block = addressToBlock(heapSource, (const u1 *)addr);
    445     if (heapSource->blockSpace[block] != BLOCK_TO_SPACE) {
    446         // LOG_PROM("promoting block %zu %d @ %p", block, heapSource->blockSpace[block], obj);
    447         heapSource->blockSpace[block] = BLOCK_TO_SPACE;
    448         enqueueBlock(heapSource, block);
    449         /* TODO(cshapiro): count continued blocks?*/
    450         heapSource->allocBlocks += 1;
    451     } else {
    452         // LOG_PROM("NOT promoting block %zu %d @ %p", block, heapSource->blockSpace[block], obj);
    453     }
    454 }
    455 
    456 GcHeap *dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
    457 {
    458     GcHeap* gcHeap;
    459     HeapSource *heapSource;
    460 
    461     assert(startSize <= absoluteMaxSize);
    462 
    463     heapSource = malloc(sizeof(*heapSource));
    464     assert(heapSource != NULL);
    465     memset(heapSource, 0, sizeof(*heapSource));
    466 
    467     heapSource->minimumSize = alignUp(startSize, BLOCK_SIZE);
    468     heapSource->maximumSize = alignUp(absoluteMaxSize, BLOCK_SIZE);
    469 
    470     heapSource->currentSize = heapSource->maximumSize;
    471 
    472     /* Allocate underlying storage for blocks. */
    473     heapSource->blockBase = virtualAlloc(heapSource->maximumSize);
    474     assert(heapSource->blockBase != NULL);
    475     heapSource->baseBlock = (uintptr_t) heapSource->blockBase >> BLOCK_SHIFT;
    476     heapSource->limitBlock = ((uintptr_t) heapSource->blockBase + heapSource->maximumSize) >> BLOCK_SHIFT;
    477 
    478     heapSource->allocBlocks = 0;
    479     heapSource->totalBlocks = (heapSource->limitBlock - heapSource->baseBlock);
    480 
    481     assert(heapSource->totalBlocks = heapSource->maximumSize / BLOCK_SIZE);
    482 
    483     {
    484         size_t size = sizeof(heapSource->blockQueue[0]);
    485         heapSource->blockQueue = malloc(heapSource->totalBlocks*size);
    486         assert(heapSource->blockQueue != NULL);
    487         memset(heapSource->blockQueue, 0xCC, heapSource->totalBlocks*size);
    488         heapSource->queueHead = QUEUE_TAIL;
    489     }
    490 
    491     /* Byte indicating space residence or free status of block. */
    492     {
    493         size_t size = sizeof(heapSource->blockSpace[0]);
    494         heapSource->blockSpace = malloc(heapSource->totalBlocks*size);
    495         assert(heapSource->blockSpace != NULL);
    496         memset(heapSource->blockSpace, 0, heapSource->totalBlocks*size);
    497     }
    498 
    499     dvmHeapBitmapInit(&heapSource->allocBits,
    500                       heapSource->blockBase,
    501                       heapSource->maximumSize,
    502                       "blockBase");
    503 
    504     /* Initialize allocation pointers. */
    505     heapSource->allocPtr = allocateBlocks(heapSource, 1);
    506     heapSource->allocLimit = heapSource->allocPtr + BLOCK_SIZE;
    507 
    508     gcHeap = malloc(sizeof(*gcHeap));
    509     assert(gcHeap != NULL);
    510     memset(gcHeap, 0, sizeof(*gcHeap));
    511     gcHeap->heapSource = heapSource;
    512 
    513     return gcHeap;
    514 }
    515 
    516 /*
    517  * Perform any required heap initializations after forking from the
    518  * zygote process.  This is a no-op for the time being.  Eventually
    519  * this will demarcate the shared region of the heap.
    520  */
    521 bool dvmHeapSourceStartupAfterZygote(void)
    522 {
    523     return true;
    524 }
    525 
    526 bool dvmHeapSourceStartupBeforeFork(void)
    527 {
    528     assert(!"implemented");
    529     return false;
    530 }
    531 
    532 void dvmHeapSourceShutdown(GcHeap **gcHeap)
    533 {
    534     if (*gcHeap == NULL || (*gcHeap)->heapSource == NULL)
    535         return;
    536     free((*gcHeap)->heapSource->blockQueue);
    537     free((*gcHeap)->heapSource->blockSpace);
    538     virtualFree((*gcHeap)->heapSource->blockBase,
    539                 (*gcHeap)->heapSource->maximumSize);
    540     free((*gcHeap)->heapSource);
    541     (*gcHeap)->heapSource = NULL;
    542     free(*gcHeap);
    543     *gcHeap = NULL;
    544 }
    545 
    546 size_t dvmHeapSourceGetValue(enum HeapSourceValueSpec spec,
    547                              size_t perHeapStats[],
    548                              size_t arrayLen)
    549 {
    550     HeapSource *heapSource;
    551     size_t value;
    552 
    553     heapSource = gDvm.gcHeap->heapSource;
    554     switch (spec) {
    555     case HS_EXTERNAL_BYTES_ALLOCATED:
    556         value = 0;
    557         break;
    558     case HS_EXTERNAL_LIMIT:
    559         value = 0;
    560         break;
    561     case HS_FOOTPRINT:
    562         value = heapSource->maximumSize;
    563         break;
    564     case HS_ALLOWED_FOOTPRINT:
    565         value = heapSource->maximumSize;
    566         break;
    567     case HS_BYTES_ALLOCATED:
    568         value = heapSource->bytesAllocated;
    569         break;
    570     case HS_OBJECTS_ALLOCATED:
    571         value = sumHeapBitmap(&heapSource->allocBits);
    572         break;
    573     default:
    574         assert(!"implemented");
    575         value = 0;
    576     }
    577     if (perHeapStats) {
    578         *perHeapStats = value;
    579     }
    580     return value;
    581 }
    582 
    583 /*
    584  * Performs a shallow copy of the allocation bitmap into the given
    585  * vector of heap bitmaps.
    586  */
    587 void dvmHeapSourceGetObjectBitmaps(HeapBitmap objBits[], HeapBitmap markBits[],
    588                                    size_t numHeaps)
    589 {
    590     assert(!"implemented");
    591 }
    592 
    593 HeapBitmap *dvmHeapSourceGetLiveBits(void)
    594 {
    595     return &gDvm.gcHeap->heapSource->allocBits;
    596 }
    597 
    598 /*
    599  * Allocate the specified number of bytes from the heap.  The
    600  * allocation cursor points into a block of free storage.  If the
    601  * given allocation fits in the remaining space of the block, we
    602  * advance the cursor and return a pointer to the free storage.  If
    603  * the allocation cannot fit in the current block but is smaller than
    604  * a block we request a new block and allocate from it instead.  If
    605  * the allocation is larger than a block we must allocate from a span
    606  * of contiguous blocks.
    607  */
    608 void *dvmHeapSourceAlloc(size_t length)
    609 {
    610     HeapSource *heapSource;
    611     unsigned char *addr;
    612     size_t aligned, available, blocks;
    613 
    614     heapSource = gDvm.gcHeap->heapSource;
    615     assert(heapSource != NULL);
    616     assert(heapSource->allocPtr != NULL);
    617     assert(heapSource->allocLimit != NULL);
    618 
    619     aligned = alignUp(length, ALLOC_ALIGNMENT);
    620     available = heapSource->allocLimit - heapSource->allocPtr;
    621 
    622     /* Try allocating inside the current block. */
    623     if (aligned <= available) {
    624         addr = heapSource->allocPtr;
    625         heapSource->allocPtr += aligned;
    626         heapSource->bytesAllocated += aligned;
    627         dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
    628         return addr;
    629     }
    630 
    631     /* Try allocating in a new block. */
    632     if (aligned <= BLOCK_SIZE) {
    633         addr =  allocateBlocks(heapSource, 1);
    634         if (addr != NULL) {
    635             heapSource->allocLimit = addr + BLOCK_SIZE;
    636             heapSource->allocPtr = addr + aligned;
    637             heapSource->bytesAllocated += aligned;
    638             dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
    639             /* TODO(cshapiro): pad out the current block. */
    640         }
    641         return addr;
    642     }
    643 
    644     /* Try allocating in a span of blocks. */
    645     blocks = alignUp(aligned, BLOCK_SIZE) / BLOCK_SIZE;
    646 
    647     addr = allocateBlocks(heapSource, blocks);
    648     /* Propagate failure upward. */
    649     if (addr != NULL) {
    650         heapSource->bytesAllocated += aligned;
    651         dvmHeapBitmapSetObjectBit(&heapSource->allocBits, addr);
    652         /* TODO(cshapiro): pad out free space in the last block. */
    653     }
    654     return addr;
    655 }
    656 
    657 void *dvmHeapSourceAllocAndGrow(size_t size)
    658 {
    659     return dvmHeapSourceAlloc(size);
    660 }
    661 
    662 /* TODO: refactor along with dvmHeapSourceAlloc */
    663 void *allocateGray(size_t size)
    664 {
    665     HeapSource *heapSource;
    666     void *addr;
    667     size_t block;
    668 
    669     /* TODO: add a check that we are in a GC. */
    670     heapSource = gDvm.gcHeap->heapSource;
    671     addr = dvmHeapSourceAlloc(size);
    672     assert(addr != NULL);
    673     block = addressToBlock(heapSource, (const u1 *)addr);
    674     if (heapSource->queueHead == QUEUE_TAIL) {
    675         /*
    676          * Forcibly append the underlying block to the queue.  This
    677          * condition occurs when referents are transported following
    678          * the initial trace.
    679          */
    680         enqueueBlock(heapSource, block);
    681         LOG_PROM("forced promoting block %zu %d @ %p", block, heapSource->blockSpace[block], addr);
    682     }
    683     return addr;
    684 }
    685 
    686 bool dvmHeapSourceContainsAddress(const void *ptr)
    687 {
    688     HeapSource *heapSource = gDvm.gcHeap->heapSource;
    689     return dvmHeapBitmapCoversAddress(&heapSource->allocBits, ptr);
    690 }
    691 
    692 /*
    693  * Returns true if the given address is within the heap and points to
    694  * the header of a live object.
    695  */
    696 bool dvmHeapSourceContains(const void *addr)
    697 {
    698     HeapSource *heapSource;
    699     HeapBitmap *bitmap;
    700 
    701     heapSource = gDvm.gcHeap->heapSource;
    702     bitmap = &heapSource->allocBits;
    703     if (!dvmHeapBitmapCoversAddress(bitmap, addr)) {
    704         return false;
    705     } else {
    706         return dvmHeapBitmapIsObjectBitSet(bitmap, addr);
    707     }
    708 }
    709 
    710 bool dvmHeapSourceGetPtrFlag(const void *ptr, enum HeapSourcePtrFlag flag)
    711 {
    712     assert(!"implemented");
    713     return false;
    714 }
    715 
    716 size_t dvmHeapSourceChunkSize(const void *ptr)
    717 {
    718     assert(!"implemented");
    719     return 0;
    720 }
    721 
    722 size_t dvmHeapSourceFootprint(void)
    723 {
    724     assert(!"implemented");
    725     return 0;
    726 }
    727 
    728 /*
    729  * Returns the "ideal footprint" which appears to be the number of
    730  * bytes currently committed to the heap.  This starts out at the
    731  * start size of the heap and grows toward the maximum size.
    732  */
    733 size_t dvmHeapSourceGetIdealFootprint(void)
    734 {
    735     return gDvm.gcHeap->heapSource->currentSize;
    736 }
    737 
    738 float dvmGetTargetHeapUtilization(void)
    739 {
    740     return 0.5f;
    741 }
    742 
    743 void dvmSetTargetHeapUtilization(float newTarget)
    744 {
    745     assert(newTarget > 0.0f && newTarget < 1.0f);
    746 }
    747 
    748 size_t dvmMinimumHeapSize(size_t size, bool set)
    749 {
    750     return gDvm.gcHeap->heapSource->minimumSize;
    751 }
    752 
    753 /*
    754  * Expands the size of the heap after a collection.  At present we
    755  * commit the pages for maximum size of the heap so this routine is
    756  * just a no-op.  Eventually, we will either allocate or commit pages
    757  * on an as-need basis.
    758  */
    759 void dvmHeapSourceGrowForUtilization(void)
    760 {
    761     /* do nothing */
    762 }
    763 
    764 void dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen)
    765 {
    766     /* do nothing */
    767 }
    768 
    769 void dvmHeapSourceWalk(void (*callback)(const void *chunkptr, size_t chunklen,
    770                                         const void *userptr, size_t userlen,
    771                                         void *arg),
    772                        void *arg)
    773 {
    774     assert(!"implemented");
    775 }
    776 
    777 size_t dvmHeapSourceGetNumHeaps(void)
    778 {
    779     return 1;
    780 }
    781 
    782 bool dvmTrackExternalAllocation(size_t n)
    783 {
    784     /* do nothing */
    785     return true;
    786 }
    787 
    788 void dvmTrackExternalFree(size_t n)
    789 {
    790     /* do nothing */
    791 }
    792 
    793 size_t dvmGetExternalBytesAllocated(void)
    794 {
    795     assert(!"implemented");
    796     return 0;
    797 }
    798 
    799 void dvmHeapSourceFlip(void)
    800 {
    801     HeapSource *heapSource;
    802     size_t i;
    803 
    804     heapSource = gDvm.gcHeap->heapSource;
    805 
    806     /* Reset the block queue. */
    807     heapSource->allocBlocks = 0;
    808     heapSource->queueSize = 0;
    809     heapSource->queueHead = QUEUE_TAIL;
    810 
    811     /* TODO(cshapiro): pad the current (prev) block. */
    812 
    813     heapSource->allocPtr = NULL;
    814     heapSource->allocLimit = NULL;
    815 
    816     /* Whiten all allocated blocks. */
    817     for (i = 0; i < heapSource->totalBlocks; ++i) {
    818         if (heapSource->blockSpace[i] == BLOCK_TO_SPACE) {
    819             heapSource->blockSpace[i] = BLOCK_FROM_SPACE;
    820         }
    821     }
    822 }
    823 
    824 static void room(size_t *alloc, size_t *avail, size_t *total)
    825 {
    826     HeapSource *heapSource;
    827 
    828     heapSource = gDvm.gcHeap->heapSource;
    829     *total = heapSource->totalBlocks*BLOCK_SIZE;
    830     *alloc = heapSource->allocBlocks*BLOCK_SIZE;
    831     *avail = *total - *alloc;
    832 }
    833 
    834 static bool isSpaceInternal(u1 *addr, int space)
    835 {
    836     HeapSource *heapSource;
    837     u1 *base, *limit;
    838     size_t offset;
    839     char space2;
    840 
    841     heapSource = gDvm.gcHeap->heapSource;
    842     base = heapSource->blockBase;
    843     assert(addr >= base);
    844     limit = heapSource->blockBase + heapSource->maximumSize;
    845     assert(addr < limit);
    846     offset = addr - base;
    847     space2 = heapSource->blockSpace[offset >> BLOCK_SHIFT];
    848     return space == space2;
    849 }
    850 
    851 static bool fromSpaceContains(const void *addr)
    852 {
    853     return isSpaceInternal((u1 *)addr, BLOCK_FROM_SPACE);
    854 }
    855 
    856 static bool toSpaceContains(const void *addr)
    857 {
    858     return isSpaceInternal((u1 *)addr, BLOCK_TO_SPACE);
    859 }
    860 
    861 /*
    862  * Notifies the collector that the object at the given address must
    863  * remain stationary during the current collection.
    864  */
    865 static void pinObject(const Object *obj)
    866 {
    867     promoteBlockByAddr(gDvm.gcHeap->heapSource, obj);
    868 }
    869 
    870 static size_t sumHeapBitmap(const HeapBitmap *bitmap)
    871 {
    872     size_t i, sum;
    873 
    874     sum = 0;
    875     for (i = 0; i < bitmap->bitsLen >> 2; ++i) {
    876         sum += CLZ(bitmap->bits[i]);
    877     }
    878     return sum;
    879 }
    880 
    881 /*
    882  * Miscellaneous functionality.
    883  */
    884 
    885 static int isForward(const void *addr)
    886 {
    887     return (uintptr_t)addr & 0x1;
    888 }
    889 
    890 static void setForward(const void *toObj, void *fromObj)
    891 {
    892     *(unsigned long *)fromObj = (uintptr_t)toObj | 0x1;
    893 }
    894 
    895 static void* getForward(const void *fromObj)
    896 {
    897     return (void *)((uintptr_t)fromObj & ~0x1);
    898 }
    899 
    900 /* Beware, uses the same encoding as a forwarding pointers! */
    901 static int isPermanentString(const StringObject *obj) {
    902     return (uintptr_t)obj & 0x1;
    903 }
    904 
    905 static void* getPermanentString(const StringObject *obj)
    906 {
    907     return (void *)((uintptr_t)obj & ~0x1);
    908 }
    909 
    910 
    911 /*
    912  * Scavenging and transporting routines follow.  A transporter grays
    913  * an object.  A scavenger blackens an object.  We define these
    914  * routines for each fundamental object type.  Dispatch is performed
    915  * in scavengeObject.
    916  */
    917 
    918 /*
    919  * Class object scavenging.
    920  */
    921 static void scavengeClassObject(ClassObject *obj)
    922 {
    923     int i;
    924 
    925     LOG_SCAV("scavengeClassObject(obj=%p)", obj);
    926     assert(obj != NULL);
    927     assert(obj->obj.clazz != NULL);
    928     assert(obj->obj.clazz->descriptor != NULL);
    929     assert(!strcmp(obj->obj.clazz->descriptor, "Ljava/lang/Class;"));
    930     assert(obj->descriptor != NULL);
    931     LOG_SCAV("scavengeClassObject: descriptor='%s',vtableCount=%zu",
    932              obj->descriptor, obj->vtableCount);
    933     /* Delegate class object and instance field scavenging. */
    934     scavengeDataObject((Object *)obj);
    935     /* Scavenge the array element class object. */
    936     if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) {
    937         scavengeReference((Object **)(void *)&obj->elementClass);
    938     }
    939     /* Scavenge the superclass. */
    940     scavengeReference((Object **)(void *)&obj->super);
    941     /* Scavenge the class loader. */
    942     scavengeReference(&obj->classLoader);
    943     /* Scavenge static fields. */
    944     for (i = 0; i < obj->sfieldCount; ++i) {
    945         char ch = obj->sfields[i].field.signature[0];
    946         if (ch == '[' || ch == 'L') {
    947             scavengeReference((Object **)(void *)&obj->sfields[i].value.l);
    948         }
    949     }
    950     /* Scavenge interface class objects. */
    951     for (i = 0; i < obj->interfaceCount; ++i) {
    952         scavengeReference((Object **) &obj->interfaces[i]);
    953     }
    954 }
    955 
    956 /*
    957  * Array object scavenging.
    958  */
    959 static size_t scavengeArrayObject(ArrayObject *array)
    960 {
    961     size_t i, length;
    962 
    963     LOG_SCAV("scavengeArrayObject(array=%p)", array);
    964     /* Scavenge the class object. */
    965     assert(toSpaceContains(array));
    966     assert(array != NULL);
    967     assert(array->obj.clazz != NULL);
    968     scavengeReference((Object **) array);
    969     length = dvmArrayObjectSize(array);
    970     /* Scavenge the array contents. */
    971     if (IS_CLASS_FLAG_SET(array->obj.clazz, CLASS_ISOBJECTARRAY)) {
    972         Object **contents = (Object **)array->contents;
    973         for (i = 0; i < array->length; ++i) {
    974             scavengeReference(&contents[i]);
    975         }
    976     }
    977     return length;
    978 }
    979 
    980 /*
    981  * Reference object scavenging.
    982  */
    983 
    984 static int getReferenceFlags(const Object *obj)
    985 {
    986     int flags;
    987 
    988     flags = CLASS_ISREFERENCE |
    989             CLASS_ISWEAKREFERENCE |
    990             CLASS_ISPHANTOMREFERENCE;
    991     return GET_CLASS_FLAG_GROUP(obj->clazz, flags);
    992 }
    993 
    994 static int isSoftReference(const Object *obj)
    995 {
    996     return getReferenceFlags(obj) == CLASS_ISREFERENCE;
    997 }
    998 
    999 static int isWeakReference(const Object *obj)
   1000 {
   1001     return getReferenceFlags(obj) & CLASS_ISWEAKREFERENCE;
   1002 }
   1003 
   1004 #ifndef NDEBUG
   1005 static bool isPhantomReference(const Object *obj)
   1006 {
   1007     return getReferenceFlags(obj) & CLASS_ISPHANTOMREFERENCE;
   1008 }
   1009 #endif
   1010 
   1011 /*
   1012  * Returns true if the reference was registered with a reference queue
   1013  * but has not yet been appended to it.
   1014  */
   1015 static bool isReferenceEnqueuable(const Object *ref)
   1016 {
   1017     Object *queue, *queueNext;
   1018 
   1019     queue = dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue);
   1020     queueNext = dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext);
   1021     if (queue == NULL || queueNext != NULL) {
   1022         /*
   1023          * There is no queue, or the reference has already
   1024          * been enqueued.  The Reference.enqueue() method
   1025          * will do nothing even if we call it.
   1026          */
   1027         return false;
   1028     }
   1029 
   1030     /*
   1031      * We need to call enqueue(), but if we called it from
   1032      * here we'd probably deadlock.  Schedule a call.
   1033      */
   1034     return true;
   1035 }
   1036 
   1037 /*
   1038  * Schedules a reference to be appended to its reference queue.
   1039  */
   1040 static void enqueueReference(Object *ref)
   1041 {
   1042     assert(ref != NULL);
   1043     assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
   1044     assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
   1045     if (!dvmHeapAddRefToLargeTable(&gDvm.gcHeap->referenceOperations, ref)) {
   1046         LOGE("no room for any more reference operations");
   1047         dvmAbort();
   1048     }
   1049 }
   1050 
   1051 /*
   1052  * Sets the referent field of a reference object to NULL.
   1053  */
   1054 static void clearReference(Object *obj)
   1055 {
   1056     dvmSetFieldObject(obj, gDvm.offJavaLangRefReference_referent, NULL);
   1057 }
   1058 
   1059 /*
   1060  * Clears reference objects with white referents.
   1061  */
   1062 void clearWhiteReferences(Object **list)
   1063 {
   1064     size_t referentOffset, queueNextOffset;
   1065     bool doSignal;
   1066 
   1067     queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
   1068     referentOffset = gDvm.offJavaLangRefReference_referent;
   1069     doSignal = false;
   1070     while (*list != NULL) {
   1071         Object *ref = *list;
   1072         JValue *field = dvmFieldPtr(ref, referentOffset);
   1073         Object *referent = field->l;
   1074         *list = dvmGetFieldObject(ref, queueNextOffset);
   1075         dvmSetFieldObject(ref, queueNextOffset, NULL);
   1076         assert(referent != NULL);
   1077         if (isForward(referent->clazz)) {
   1078             field->l = referent = getForward(referent->clazz);
   1079             continue;
   1080         }
   1081         if (fromSpaceContains(referent)) {
   1082             /* Referent is white, clear it. */
   1083             clearReference(ref);
   1084             if (isReferenceEnqueuable(ref)) {
   1085                 enqueueReference(ref);
   1086                 doSignal = true;
   1087             }
   1088         }
   1089     }
   1090     /*
   1091      * If we cleared a reference with a reference queue we must notify
   1092      * the heap worker to append the reference.
   1093      */
   1094     if (doSignal) {
   1095         dvmSignalHeapWorker(false);
   1096     }
   1097     assert(*list == NULL);
   1098 }
   1099 
   1100 /*
   1101  * Blackens referents subject to the soft reference preservation
   1102  * policy.
   1103  */
   1104 void preserveSoftReferences(Object **list)
   1105 {
   1106     Object *ref;
   1107     Object *prev, *next;
   1108     size_t referentOffset, queueNextOffset;
   1109     unsigned counter;
   1110     bool white;
   1111 
   1112     queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
   1113     referentOffset = gDvm.offJavaLangRefReference_referent;
   1114     counter = 0;
   1115     prev = next = NULL;
   1116     ref = *list;
   1117     while (ref != NULL) {
   1118         JValue *field = dvmFieldPtr(ref, referentOffset);
   1119         Object *referent = field->l;
   1120         next = dvmGetFieldObject(ref, queueNextOffset);
   1121         assert(referent != NULL);
   1122         if (isForward(referent->clazz)) {
   1123             /* Referent is black. */
   1124             field->l = referent = getForward(referent->clazz);
   1125             white = false;
   1126         } else {
   1127             white = fromSpaceContains(referent);
   1128         }
   1129         if (!white && ((++counter) & 1)) {
   1130             /* Referent is white and biased toward saving, gray it. */
   1131             scavengeReference((Object **)(void *)&field->l);
   1132             white = true;
   1133         }
   1134         if (white) {
   1135             /* Referent is black, unlink it. */
   1136             if (prev != NULL) {
   1137                 dvmSetFieldObject(ref, queueNextOffset, NULL);
   1138                 dvmSetFieldObject(prev, queueNextOffset, next);
   1139             }
   1140         } else {
   1141             /* Referent is white, skip over it. */
   1142             prev = ref;
   1143         }
   1144         ref = next;
   1145     }
   1146     /*
   1147      * Restart the trace with the newly gray references added to the
   1148      * root set.
   1149      */
   1150     scavengeBlockQueue();
   1151 }
   1152 
   1153 void processFinalizableReferences(void)
   1154 {
   1155     HeapRefTable newPendingRefs;
   1156     LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs;
   1157     Object **ref;
   1158     Object **lastRef;
   1159     size_t totalPendCount;
   1160 
   1161     /*
   1162      * All strongly, reachable objects are black.
   1163      * Any white finalizable objects need to be finalized.
   1164      */
   1165 
   1166     /* Create a table that the new pending refs will
   1167      * be added to.
   1168      */
   1169     if (!dvmHeapInitHeapRefTable(&newPendingRefs)) {
   1170         //TODO: mark all finalizable refs and hope that
   1171         //      we can schedule them next time.  Watch out,
   1172         //      because we may be expecting to free up space
   1173         //      by calling finalizers.
   1174         LOG_REF("no room for pending finalizations\n");
   1175         dvmAbort();
   1176     }
   1177 
   1178     /*
   1179      * Walk through finalizableRefs and move any white references to
   1180      * the list of new pending refs.
   1181      */
   1182     totalPendCount = 0;
   1183     while (finRefs != NULL) {
   1184         Object **gapRef;
   1185         size_t newPendCount = 0;
   1186 
   1187         gapRef = ref = finRefs->refs.table;
   1188         lastRef = finRefs->refs.nextEntry;
   1189         while (ref < lastRef) {
   1190             if (fromSpaceContains(*ref)) {
   1191                 if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) {
   1192                     //TODO: add the current table and allocate
   1193                     //      a new, smaller one.
   1194                     LOG_REF("no room for any more pending finalizations: %zd\n",
   1195                             dvmHeapNumHeapRefTableEntries(&newPendingRefs));
   1196                     dvmAbort();
   1197                 }
   1198                 newPendCount++;
   1199             } else {
   1200                 /* This ref is black, so will remain on finalizableRefs.
   1201                  */
   1202                 if (newPendCount > 0) {
   1203                     /* Copy it up to fill the holes.
   1204                      */
   1205                     *gapRef++ = *ref;
   1206                 } else {
   1207                     /* No holes yet; don't bother copying.
   1208                      */
   1209                     gapRef++;
   1210                 }
   1211             }
   1212             ref++;
   1213         }
   1214         finRefs->refs.nextEntry = gapRef;
   1215         //TODO: if the table is empty when we're done, free it.
   1216         totalPendCount += newPendCount;
   1217         finRefs = finRefs->next;
   1218     }
   1219     LOG_REF("%zd finalizers triggered.\n", totalPendCount);
   1220     if (totalPendCount == 0) {
   1221         /* No objects required finalization.
   1222          * Free the empty temporary table.
   1223          */
   1224         dvmClearReferenceTable(&newPendingRefs);
   1225         return;
   1226     }
   1227 
   1228     /* Add the new pending refs to the main list.
   1229      */
   1230     if (!dvmHeapAddTableToLargeTable(&gDvm.gcHeap->pendingFinalizationRefs,
   1231                 &newPendingRefs))
   1232     {
   1233         LOG_REF("can't insert new pending finalizations\n");
   1234         dvmAbort();
   1235     }
   1236 
   1237     //TODO: try compacting the main list with a memcpy loop
   1238 
   1239     /* Blacken the refs we just moved; we don't want them or their
   1240      * children to get swept yet.
   1241      */
   1242     ref = newPendingRefs.table;
   1243     lastRef = newPendingRefs.nextEntry;
   1244     assert(ref < lastRef);
   1245     HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
   1246     while (ref < lastRef) {
   1247         scavengeReference(ref);
   1248         ref++;
   1249     }
   1250     HPROF_CLEAR_GC_SCAN_STATE();
   1251     scavengeBlockQueue();
   1252     dvmSignalHeapWorker(false);
   1253 }
   1254 
   1255 /*
   1256  * If a reference points to from-space and has been forwarded, we snap
   1257  * the pointer to its new to-space address.  If the reference points
   1258  * to an unforwarded from-space address we must enqueue the reference
   1259  * for later processing.  TODO: implement proper reference processing
   1260  * and move the referent scavenging elsewhere.
   1261  */
   1262 static void scavengeReferenceObject(Object *obj)
   1263 {
   1264     Object *referent;
   1265     Object **queue;
   1266     size_t referentOffset, queueNextOffset;
   1267 
   1268     assert(obj != NULL);
   1269     LOG_SCAV("scavengeReferenceObject(obj=%p),'%s'", obj, obj->clazz->descriptor);
   1270     scavengeDataObject(obj);
   1271     referentOffset = gDvm.offJavaLangRefReference_referent;
   1272     referent = dvmGetFieldObject(obj, referentOffset);
   1273     if (referent == NULL || toSpaceContains(referent)) {
   1274         return;
   1275     }
   1276     if (isSoftReference(obj)) {
   1277         queue = &gDvm.gcHeap->softReferences;
   1278     } else if (isWeakReference(obj)) {
   1279         queue = &gDvm.gcHeap->weakReferences;
   1280     } else {
   1281         assert(isPhantomReference(obj));
   1282         queue = &gDvm.gcHeap->phantomReferences;
   1283     }
   1284     queueNextOffset = gDvm.offJavaLangRefReference_queueNext;
   1285     dvmSetFieldObject(obj, queueNextOffset, *queue);
   1286     *queue = obj;
   1287     LOG_SCAV("scavengeReferenceObject: enqueueing %p", obj);
   1288 }
   1289 
   1290 /*
   1291  * Data object scavenging.
   1292  */
   1293 static void scavengeDataObject(Object *obj)
   1294 {
   1295     ClassObject *clazz;
   1296     int i;
   1297 
   1298     // LOG_SCAV("scavengeDataObject(obj=%p)", obj);
   1299     assert(obj != NULL);
   1300     assert(obj->clazz != NULL);
   1301     assert(obj->clazz->objectSize != 0);
   1302     assert(toSpaceContains(obj));
   1303     /* Scavenge the class object. */
   1304     clazz = obj->clazz;
   1305     scavengeReference((Object **) obj);
   1306     /* Scavenge instance fields. */
   1307     if (clazz->refOffsets != CLASS_WALK_SUPER) {
   1308         size_t refOffsets = clazz->refOffsets;
   1309         while (refOffsets != 0) {
   1310             size_t rshift = CLZ(refOffsets);
   1311             size_t offset = CLASS_OFFSET_FROM_CLZ(rshift);
   1312             Object **ref = (Object **)((u1 *)obj + offset);
   1313             scavengeReference(ref);
   1314             refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
   1315         }
   1316     } else {
   1317         for (; clazz != NULL; clazz = clazz->super) {
   1318             InstField *field = clazz->ifields;
   1319             for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
   1320                 size_t offset = field->byteOffset;
   1321                 Object **ref = (Object **)((u1 *)obj + offset);
   1322                 scavengeReference(ref);
   1323             }
   1324         }
   1325     }
   1326 }
   1327 
   1328 static Object *transportObject(const Object *fromObj)
   1329 {
   1330     Object *toObj;
   1331     size_t allocSize, copySize;
   1332 
   1333     LOG_TRAN("transportObject(fromObj=%p) allocBlocks=%zu",
   1334                   fromObj,
   1335                   gDvm.gcHeap->heapSource->allocBlocks);
   1336     assert(fromObj != NULL);
   1337     assert(fromSpaceContains(fromObj));
   1338     allocSize = copySize = objectSize(fromObj);
   1339     if (LW_HASH_STATE(fromObj->lock) != LW_HASH_STATE_UNHASHED) {
   1340         /*
   1341          * The object has been hashed or hashed and moved.  We must
   1342          * reserve an additional word for a hash code.
   1343          */
   1344         allocSize += sizeof(u4);
   1345     }
   1346     if (LW_HASH_STATE(fromObj->lock) == LW_HASH_STATE_HASHED_AND_MOVED) {
   1347         /*
   1348          * The object has its hash code allocated.  Ensure the hash
   1349          * code is copied along with the instance data.
   1350          */
   1351         copySize += sizeof(u4);
   1352     }
   1353     /* TODO(cshapiro): don't copy, re-map large data objects. */
   1354     assert(copySize <= allocSize);
   1355     toObj = allocateGray(allocSize);
   1356     assert(toObj != NULL);
   1357     assert(toSpaceContains(toObj));
   1358     memcpy(toObj, fromObj, copySize);
   1359     if (LW_HASH_STATE(fromObj->lock) == LW_HASH_STATE_HASHED) {
   1360         /*
   1361          * The object has had its hash code exposed.  Append it to the
   1362          * instance and set a bit so we know to look for it there.
   1363          */
   1364         *(u4 *)(((char *)toObj) + copySize) = (u4)fromObj >> 3;
   1365         toObj->lock |= LW_HASH_STATE_HASHED_AND_MOVED << LW_HASH_STATE_SHIFT;
   1366     }
   1367     LOG_TRAN("transportObject: from %p/%zu to %p/%zu (%zu,%zu) %s",
   1368              fromObj, addressToBlock(gDvm.gcHeap->heapSource,fromObj),
   1369              toObj, addressToBlock(gDvm.gcHeap->heapSource,toObj),
   1370              copySize, allocSize, copySize < allocSize ? "DIFFERENT" : "");
   1371     return toObj;
   1372 }
   1373 
   1374 /*
   1375  * Generic reference scavenging.
   1376  */
   1377 
   1378 /*
   1379  * Given a reference to an object, the scavenge routine will gray the
   1380  * reference.  Any objects pointed to by the scavenger object will be
   1381  * transported to new space and a forwarding pointer will be installed
   1382  * in the header of the object.
   1383  */
   1384 
   1385 /*
   1386  * Blacken the given pointer.  If the pointer is in from space, it is
   1387  * transported to new space.  If the object has a forwarding pointer
   1388  * installed it has already been transported and the referent is
   1389  * snapped to the new address.
   1390  */
   1391 static void scavengeReference(Object **obj)
   1392 {
   1393     ClassObject *clazz;
   1394     Object *fromObj, *toObj;
   1395 
   1396     assert(obj);
   1397 
   1398     if (*obj == NULL) return;
   1399 
   1400     assert(dvmIsValidObject(*obj));
   1401 
   1402     /* The entire block is black. */
   1403     if (toSpaceContains(*obj)) {
   1404         LOG_SCAV("scavengeReference skipping pinned object @ %p", *obj);
   1405         return;
   1406     }
   1407     LOG_SCAV("scavengeReference(*obj=%p)", *obj);
   1408 
   1409     assert(fromSpaceContains(*obj));
   1410 
   1411     clazz = (*obj)->clazz;
   1412 
   1413     if (isForward(clazz)) {
   1414         // LOG_SCAV("forwarding %p @ %p to %p", *obj, obj, (void *)((uintptr_t)clazz & ~0x1));
   1415         *obj = (Object *)getForward(clazz);
   1416         return;
   1417     }
   1418     fromObj = *obj;
   1419     if (clazz == NULL) {
   1420         // LOG_SCAV("scavangeReference %p has a NULL class object", fromObj);
   1421         assert(!"implemented");
   1422         toObj = NULL;
   1423     } else {
   1424         toObj = transportObject(fromObj);
   1425     }
   1426     setForward(toObj, fromObj);
   1427     *obj = (Object *)toObj;
   1428 }
   1429 
   1430 /*
   1431  * Generic object scavenging.
   1432  */
   1433 static void scavengeObject(Object *obj)
   1434 {
   1435     ClassObject *clazz;
   1436 
   1437     assert(obj != NULL);
   1438     assert(obj->clazz != NULL);
   1439     assert(!((uintptr_t)obj->clazz & 0x1));
   1440     clazz = obj->clazz;
   1441     if (clazz == gDvm.classJavaLangClass) {
   1442         scavengeClassObject((ClassObject *)obj);
   1443     } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
   1444         scavengeArrayObject((ArrayObject *)obj);
   1445     } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISREFERENCE)) {
   1446         scavengeReferenceObject(obj);
   1447     } else {
   1448         scavengeDataObject(obj);
   1449     }
   1450 }
   1451 
   1452 /*
   1453  * External root scavenging routines.
   1454  */
   1455 
   1456 static void pinHashTableEntries(HashTable *table)
   1457 {
   1458     HashEntry *entry;
   1459     void *obj;
   1460     int i;
   1461 
   1462     LOG_PIN(">>> pinHashTableEntries(table=%p)", table);
   1463     if (table == NULL) {
   1464         return;
   1465     }
   1466     dvmHashTableLock(table);
   1467     for (i = 0; i < table->tableSize; ++i) {
   1468         entry = &table->pEntries[i];
   1469         obj = entry->data;
   1470         if (obj == NULL || obj == HASH_TOMBSTONE) {
   1471             continue;
   1472         }
   1473         pinObject(entry->data);
   1474     }
   1475     dvmHashTableUnlock(table);
   1476     LOG_PIN("<<< pinHashTableEntries(table=%p)", table);
   1477 }
   1478 
   1479 static void pinPrimitiveClasses(void)
   1480 {
   1481     size_t length;
   1482     size_t i;
   1483 
   1484     length = ARRAYSIZE(gDvm.primitiveClass);
   1485     for (i = 0; i < length; i++) {
   1486         if (gDvm.primitiveClass[i] != NULL) {
   1487             pinObject((Object *)gDvm.primitiveClass[i]);
   1488         }
   1489     }
   1490 }
   1491 
   1492 /*
   1493  * Scavenge interned strings.  Permanent interned strings will have
   1494  * been pinned and are therefore ignored.  Non-permanent strings that
   1495  * have been forwarded are snapped.  All other entries are removed.
   1496  */
   1497 static void scavengeInternedStrings(void)
   1498 {
   1499     HashTable *table;
   1500     HashEntry *entry;
   1501     Object *obj;
   1502     int i;
   1503 
   1504     table = gDvm.internedStrings;
   1505     if (table == NULL) {
   1506         return;
   1507     }
   1508     dvmHashTableLock(table);
   1509     for (i = 0; i < table->tableSize; ++i) {
   1510         entry = &table->pEntries[i];
   1511         obj = (Object *)entry->data;
   1512         if (obj == NULL || obj == HASH_TOMBSTONE) {
   1513             continue;
   1514         } else if (!isPermanentString((StringObject *)obj)) {
   1515             // LOG_SCAV("entry->data=%p", entry->data);
   1516             LOG_SCAV(">>> string obj=%p", entry->data);
   1517             /* TODO(cshapiro): detach white string objects */
   1518             scavengeReference((Object **)(void *)&entry->data);
   1519             LOG_SCAV("<<< string obj=%p", entry->data);
   1520         }
   1521     }
   1522     dvmHashTableUnlock(table);
   1523 }
   1524 
   1525 static void pinInternedStrings(void)
   1526 {
   1527     HashTable *table;
   1528     HashEntry *entry;
   1529     Object *obj;
   1530     int i;
   1531 
   1532     table = gDvm.internedStrings;
   1533     if (table == NULL) {
   1534         return;
   1535     }
   1536     dvmHashTableLock(table);
   1537     for (i = 0; i < table->tableSize; ++i) {
   1538         entry = &table->pEntries[i];
   1539         obj = (Object *)entry->data;
   1540         if (obj == NULL || obj == HASH_TOMBSTONE) {
   1541             continue;
   1542         } else if (isPermanentString((StringObject *)obj)) {
   1543             obj = (Object *)getPermanentString((StringObject*)obj);
   1544             LOG_PROM(">>> pin string obj=%p", obj);
   1545             pinObject(obj);
   1546             LOG_PROM("<<< pin string obj=%p", obj);
   1547         }
   1548      }
   1549     dvmHashTableUnlock(table);
   1550 }
   1551 
   1552 /*
   1553  * At present, reference tables contain references that must not be
   1554  * moved by the collector.  Instead of scavenging each reference in
   1555  * the table we pin each referenced object.
   1556  */
   1557 static void pinReferenceTable(const ReferenceTable *table)
   1558 {
   1559     Object **entry;
   1560 
   1561     assert(table != NULL);
   1562     assert(table->table != NULL);
   1563     assert(table->nextEntry != NULL);
   1564     for (entry = table->table; entry < table->nextEntry; ++entry) {
   1565         assert(entry != NULL);
   1566         assert(!isForward(*entry));
   1567         pinObject(*entry);
   1568     }
   1569 }
   1570 
   1571 static void scavengeLargeHeapRefTable(LargeHeapRefTable *table)
   1572 {
   1573     for (; table != NULL; table = table->next) {
   1574         Object **ref = table->refs.table;
   1575         for (; ref < table->refs.nextEntry; ++ref) {
   1576             scavengeReference(ref);
   1577         }
   1578     }
   1579 }
   1580 
   1581 /* This code was copied from Thread.c */
   1582 static void scavengeThreadStack(Thread *thread)
   1583 {
   1584     const u4 *framePtr;
   1585 #if WITH_EXTRA_GC_CHECKS > 1
   1586     bool first = true;
   1587 #endif
   1588 
   1589     framePtr = (const u4 *)thread->curFrame;
   1590     while (framePtr != NULL) {
   1591         const StackSaveArea *saveArea;
   1592         const Method *method;
   1593 
   1594         saveArea = SAVEAREA_FROM_FP(framePtr);
   1595         method = saveArea->method;
   1596         if (method != NULL && !dvmIsNativeMethod(method)) {
   1597 #ifdef COUNT_PRECISE_METHODS
   1598             /* the GC is running, so no lock required */
   1599             if (dvmPointerSetAddEntry(gDvm.preciseMethods, method))
   1600                 LOG_SCAV("PGC: added %s.%s %p\n",
   1601                              method->clazz->descriptor, method->name, method);
   1602 #endif
   1603 #if WITH_EXTRA_GC_CHECKS > 1
   1604             /*
   1605              * May also want to enable the memset() in the "invokeMethod"
   1606              * goto target in the portable interpreter.  That sets the stack
   1607              * to a pattern that makes referring to uninitialized data
   1608              * very obvious.
   1609              */
   1610 
   1611             if (first) {
   1612                 /*
   1613                  * First frame, isn't native, check the "alternate" saved PC
   1614                  * as a sanity check.
   1615                  *
   1616                  * It seems like we could check the second frame if the first
   1617                  * is native, since the PCs should be the same.  It turns out
   1618                  * this doesn't always work.  The problem is that we could
   1619                  * have calls in the sequence:
   1620                  *   interp method #2
   1621                  *   native method
   1622                  *   interp method #1
   1623                  *
   1624                  * and then GC while in the native method after returning
   1625                  * from interp method #2.  The currentPc on the stack is
   1626                  * for interp method #1, but thread->currentPc2 is still
   1627                  * set for the last thing interp method #2 did.
   1628                  *
   1629                  * This can also happen in normal execution:
   1630                  * - sget-object on not-yet-loaded class
   1631                  * - class init updates currentPc2
   1632                  * - static field init is handled by parsing annotations;
   1633                  *   static String init requires creation of a String object,
   1634                  *   which can cause a GC
   1635                  *
   1636                  * Essentially, any pattern that involves executing
   1637                  * interpreted code and then causes an allocation without
   1638                  * executing instructions in the original method will hit
   1639                  * this.  These are rare enough that the test still has
   1640                  * some value.
   1641                  */
   1642                 if (saveArea->xtra.currentPc != thread->currentPc2) {
   1643                     LOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p\n",
   1644                         saveArea->xtra.currentPc, thread->currentPc2,
   1645                         method->clazz->descriptor, method->name, method->insns);
   1646                     if (saveArea->xtra.currentPc != NULL)
   1647                         LOGE("  pc inst = 0x%04x\n", *saveArea->xtra.currentPc);
   1648                     if (thread->currentPc2 != NULL)
   1649                         LOGE("  pc2 inst = 0x%04x\n", *thread->currentPc2);
   1650                     dvmDumpThread(thread, false);
   1651                 }
   1652             } else {
   1653                 /*
   1654                  * It's unusual, but not impossible, for a non-first frame
   1655                  * to be at something other than a method invocation.  For
   1656                  * example, if we do a new-instance on a nonexistent class,
   1657                  * we'll have a lot of class loader activity on the stack
   1658                  * above the frame with the "new" operation.  Could also
   1659                  * happen while we initialize a Throwable when an instruction
   1660                  * fails.
   1661                  *
   1662                  * So there's not much we can do here to verify the PC,
   1663                  * except to verify that it's a GC point.
   1664                  */
   1665             }
   1666             assert(saveArea->xtra.currentPc != NULL);
   1667 #endif
   1668 
   1669             const RegisterMap* pMap;
   1670             const u1* regVector;
   1671             int i;
   1672 
   1673             Method* nonConstMethod = (Method*) method;  // quiet gcc
   1674             pMap = dvmGetExpandedRegisterMap(nonConstMethod);
   1675 
   1676             //LOG_SCAV("PGC: %s.%s\n", method->clazz->descriptor, method->name);
   1677 
   1678             if (pMap != NULL) {
   1679                 /* found map, get registers for this address */
   1680                 int addr = saveArea->xtra.currentPc - method->insns;
   1681                 regVector = dvmRegisterMapGetLine(pMap, addr);
   1682                 /*
   1683                 if (regVector == NULL) {
   1684                     LOG_SCAV("PGC: map but no entry for %s.%s addr=0x%04x\n",
   1685                                  method->clazz->descriptor, method->name, addr);
   1686                 } else {
   1687                     LOG_SCAV("PGC: found map for %s.%s 0x%04x (t=%d)\n",
   1688                                  method->clazz->descriptor, method->name, addr,
   1689                                  thread->threadId);
   1690                 }
   1691                 */
   1692             } else {
   1693                 /*
   1694                  * No map found.  If precise GC is disabled this is
   1695                  * expected -- we don't create pointers to the map data even
   1696                  * if it's present -- but if it's enabled it means we're
   1697                  * unexpectedly falling back on a conservative scan, so it's
   1698                  * worth yelling a little.
   1699                  */
   1700                 if (gDvm.preciseGc) {
   1701                     LOG_SCAV("PGC: no map for %s.%s\n", method->clazz->descriptor, method->name);
   1702                 }
   1703                 regVector = NULL;
   1704             }
   1705             if (regVector == NULL) {
   1706                 /*
   1707                  * There are no roots to scavenge.  Skip over the entire frame.
   1708                  */
   1709                 framePtr += method->registersSize;
   1710             } else {
   1711                 /*
   1712                  * Precise scan.  v0 is at the lowest address on the
   1713                  * interpreted stack, and is the first bit in the register
   1714                  * vector, so we can walk through the register map and
   1715                  * memory in the same direction.
   1716                  *
   1717                  * A '1' bit indicates a live reference.
   1718                  */
   1719                 u2 bits = 1 << 1;
   1720                 for (i = method->registersSize - 1; i >= 0; i--) {
   1721                     u4 rval = *framePtr;
   1722 
   1723                     bits >>= 1;
   1724                     if (bits == 1) {
   1725                         /* set bit 9 so we can tell when we're empty */
   1726                         bits = *regVector++ | 0x0100;
   1727                     }
   1728 
   1729                     if (rval != 0 && (bits & 0x01) != 0) {
   1730                         /*
   1731                          * Non-null, register marked as live reference.  This
   1732                          * should always be a valid object.
   1733                          */
   1734 #if WITH_EXTRA_GC_CHECKS > 0
   1735                         if ((rval & 0x3) != 0 || !dvmIsValidObject((Object*) rval)) {
   1736                             /* this is very bad */
   1737                             LOGE("PGC: invalid ref in reg %d: 0x%08x\n",
   1738                                 method->registersSize-1 - i, rval);
   1739                         } else
   1740 #endif
   1741                         {
   1742 
   1743                             // LOG_SCAV("stack reference %u@%p", *framePtr, framePtr);
   1744                             /* dvmMarkObjectNonNull((Object *)rval); */
   1745                             scavengeReference((Object **) framePtr);
   1746                         }
   1747                     } else {
   1748                         /*
   1749                          * Null or non-reference, do nothing at all.
   1750                          */
   1751 #if WITH_EXTRA_GC_CHECKS > 1
   1752                         if (dvmIsValidObject((Object*) rval)) {
   1753                             /* this is normal, but we feel chatty */
   1754                             LOGD("PGC: ignoring valid ref in reg %d: 0x%08x\n",
   1755                                  method->registersSize-1 - i, rval);
   1756                         }
   1757 #endif
   1758                     }
   1759                     ++framePtr;
   1760                 }
   1761                 dvmReleaseRegisterMapLine(pMap, regVector);
   1762             }
   1763         }
   1764         /* else this is a break frame and there is nothing to gray, or
   1765          * this is a native method and the registers are just the "ins",
   1766          * copied from various registers in the caller's set.
   1767          */
   1768 
   1769 #if WITH_EXTRA_GC_CHECKS > 1
   1770         first = false;
   1771 #endif
   1772 
   1773         /* Don't fall into an infinite loop if things get corrupted.
   1774          */
   1775         assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
   1776                saveArea->prevFrame == NULL);
   1777         framePtr = saveArea->prevFrame;
   1778     }
   1779 }
   1780 
   1781 static void scavengeThread(Thread *thread)
   1782 {
   1783     // LOG_SCAV("scavengeThread(thread=%p)", thread);
   1784 
   1785     // LOG_SCAV("Scavenging threadObj=%p", thread->threadObj);
   1786     scavengeReference(&thread->threadObj);
   1787 
   1788     // LOG_SCAV("Scavenging exception=%p", thread->exception);
   1789     scavengeReference(&thread->exception);
   1790 
   1791     scavengeThreadStack(thread);
   1792 }
   1793 
   1794 static void scavengeThreadList(void)
   1795 {
   1796     Thread *thread;
   1797 
   1798     dvmLockThreadList(dvmThreadSelf());
   1799     thread = gDvm.threadList;
   1800     while (thread) {
   1801         scavengeThread(thread);
   1802         thread = thread->next;
   1803     }
   1804     dvmUnlockThreadList();
   1805 }
   1806 
   1807 static void pinThreadStack(const Thread *thread)
   1808 {
   1809     const u4 *framePtr;
   1810     const StackSaveArea *saveArea;
   1811     Method *method;
   1812     const char *shorty;
   1813     Object *obj;
   1814     int i;
   1815 
   1816     saveArea = NULL;
   1817     framePtr = (const u4 *)thread->curFrame;
   1818     for (; framePtr != NULL; framePtr = saveArea->prevFrame) {
   1819         saveArea = SAVEAREA_FROM_FP(framePtr);
   1820         method = (Method *)saveArea->method;
   1821         if (method != NULL && dvmIsNativeMethod(method)) {
   1822             /*
   1823              * This is native method, pin its arguments.
   1824              *
   1825              * For purposes of graying references, we don't need to do
   1826              * anything here, because all of the native "ins" were copied
   1827              * from registers in the caller's stack frame and won't be
   1828              * changed (an interpreted method can freely use registers
   1829              * with parameters like any other register, but natives don't
   1830              * work that way).
   1831              *
   1832              * However, we need to ensure that references visible to
   1833              * native methods don't move around.  We can do a precise scan
   1834              * of the arguments by examining the method signature.
   1835              */
   1836             LOG_PIN("+++ native scan %s.%s\n",
   1837                     method->clazz->descriptor, method->name);
   1838             assert(method->registersSize == method->insSize);
   1839             if (!dvmIsStaticMethod(method)) {
   1840                 /* grab the "this" pointer */
   1841                 obj = (Object *)*framePtr++;
   1842                 if (obj == NULL) {
   1843                     /*
   1844                      * This can happen for the "fake" entry frame inserted
   1845                      * for threads created outside the VM.  There's no actual
   1846                      * call so there's no object.  If we changed the fake
   1847                      * entry method to be declared "static" then this
   1848                      * situation should never occur.
   1849                      */
   1850                 } else {
   1851                     assert(dvmIsValidObject(obj));
   1852                     pinObject(obj);
   1853                 }
   1854             }
   1855             shorty = method->shorty+1;      // skip return value
   1856             for (i = method->registersSize - 1; i >= 0; i--, framePtr++) {
   1857                 switch (*shorty++) {
   1858                 case 'L':
   1859                     obj = (Object *)*framePtr;
   1860                     if (obj != NULL) {
   1861                         assert(dvmIsValidObject(obj));
   1862                         pinObject(obj);
   1863                     }
   1864                     break;
   1865                 case 'D':
   1866                 case 'J':
   1867                     framePtr++;
   1868                     break;
   1869                 default:
   1870                     /* 32-bit non-reference value */
   1871                     obj = (Object *)*framePtr;          // debug, remove
   1872                     if (dvmIsValidObject(obj)) {        // debug, remove
   1873                         /* if we see a lot of these, our scan might be off */
   1874                         LOG_PIN("+++ did NOT pin obj %p\n", obj);
   1875                     }
   1876                     break;
   1877                 }
   1878             }
   1879         } else if (method != NULL && !dvmIsNativeMethod(method)) {
   1880             const RegisterMap* pMap = dvmGetExpandedRegisterMap(method);
   1881             const u1* regVector = NULL;
   1882 
   1883             LOGI("conservative : %s.%s\n", method->clazz->descriptor, method->name);
   1884 
   1885             if (pMap != NULL) {
   1886                 int addr = saveArea->xtra.currentPc - method->insns;
   1887                 regVector = dvmRegisterMapGetLine(pMap, addr);
   1888             }
   1889             if (regVector == NULL) {
   1890                 /*
   1891                  * No register info for this frame, conservatively pin.
   1892                  */
   1893                 for (i = 0; i < method->registersSize; ++i) {
   1894                     u4 regValue = framePtr[i];
   1895                     if (regValue != 0 && (regValue & 0x3) == 0 && dvmIsValidObject((Object *)regValue)) {
   1896                         pinObject((Object *)regValue);
   1897                     }
   1898                 }
   1899             }
   1900         }
   1901         /*
   1902          * Don't fall into an infinite loop if things get corrupted.
   1903          */
   1904         assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
   1905                saveArea->prevFrame == NULL);
   1906     }
   1907 }
   1908 
   1909 static void pinThread(const Thread *thread)
   1910 {
   1911     assert(thread != NULL);
   1912     LOG_PIN("pinThread(thread=%p)", thread);
   1913 
   1914     LOG_PIN("Pin native method arguments");
   1915     pinThreadStack(thread);
   1916 
   1917     LOG_PIN("Pin internalLocalRefTable");
   1918     pinReferenceTable(&thread->internalLocalRefTable);
   1919 
   1920     LOG_PIN("Pin jniLocalRefTable");
   1921     pinReferenceTable(&thread->jniLocalRefTable);
   1922 
   1923     /* Can the check be pushed into the promote routine? */
   1924     if (thread->jniMonitorRefTable.table) {
   1925         LOG_PIN("Pin jniMonitorRefTable");
   1926         pinReferenceTable(&thread->jniMonitorRefTable);
   1927     }
   1928 }
   1929 
   1930 static void pinThreadList(void)
   1931 {
   1932     Thread *thread;
   1933 
   1934     dvmLockThreadList(dvmThreadSelf());
   1935     thread = gDvm.threadList;
   1936     while (thread) {
   1937         pinThread(thread);
   1938         thread = thread->next;
   1939     }
   1940     dvmUnlockThreadList();
   1941 }
   1942 
   1943 /*
   1944  * Heap block scavenging.
   1945  */
   1946 
   1947 /*
   1948  * Scavenge objects in the current block.  Scavenging terminates when
   1949  * the pointer reaches the highest address in the block or when a run
   1950  * of zero words that continues to the highest address is reached.
   1951  */
   1952 static void scavengeBlock(HeapSource *heapSource, size_t block)
   1953 {
   1954     u1 *cursor;
   1955     u1 *end;
   1956     size_t size;
   1957 
   1958     LOG_SCAV("scavengeBlock(heapSource=%p,block=%zu)", heapSource, block);
   1959 
   1960     assert(heapSource != NULL);
   1961     assert(block < heapSource->totalBlocks);
   1962     assert(heapSource->blockSpace[block] == BLOCK_TO_SPACE);
   1963 
   1964     cursor = blockToAddress(heapSource, block);
   1965     end = cursor + BLOCK_SIZE;
   1966     LOG_SCAV("scavengeBlock start=%p, end=%p", cursor, end);
   1967 
   1968     /* Parse and scavenge the current block. */
   1969     size = 0;
   1970     while (cursor < end) {
   1971         u4 word = *(u4 *)cursor;
   1972         if (word != 0) {
   1973             scavengeObject((Object *)cursor);
   1974             size = objectSize((Object *)cursor);
   1975             size = alignUp(size, ALLOC_ALIGNMENT);
   1976             cursor += size;
   1977         } else {
   1978             /* Check for padding. */
   1979             while (*(u4 *)cursor == 0) {
   1980                 cursor += 4;
   1981                 if (cursor == end) break;
   1982             }
   1983             /* Punt if something went wrong. */
   1984             assert(cursor == end);
   1985         }
   1986     }
   1987 }
   1988 
   1989 static size_t objectSize(const Object *obj)
   1990 {
   1991     size_t size;
   1992 
   1993     assert(obj != NULL);
   1994     assert(obj->clazz != NULL);
   1995     if (obj->clazz == gDvm.classJavaLangClass) {
   1996         size = dvmClassObjectSize((ClassObject *)obj);
   1997     } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
   1998         size = dvmArrayObjectSize((ArrayObject *)obj);
   1999     } else {
   2000         assert(obj->clazz->objectSize != 0);
   2001         size = obj->clazz->objectSize;
   2002     }
   2003     if (LW_HASH_STATE(obj->lock) == LW_HASH_STATE_HASHED_AND_MOVED) {
   2004         size += sizeof(u4);
   2005     }
   2006     return size;
   2007 }
   2008 
   2009 static void verifyBlock(HeapSource *heapSource, size_t block)
   2010 {
   2011     u1 *cursor;
   2012     u1 *end;
   2013     size_t size;
   2014 
   2015     // LOG_VER("verifyBlock(heapSource=%p,block=%zu)", heapSource, block);
   2016 
   2017     assert(heapSource != NULL);
   2018     assert(block < heapSource->totalBlocks);
   2019     assert(heapSource->blockSpace[block] == BLOCK_TO_SPACE);
   2020 
   2021     cursor = blockToAddress(heapSource, block);
   2022     end = cursor + BLOCK_SIZE;
   2023     // LOG_VER("verifyBlock start=%p, end=%p", cursor, end);
   2024 
   2025     /* Parse and scavenge the current block. */
   2026     size = 0;
   2027     while (cursor < end) {
   2028         u4 word = *(u4 *)cursor;
   2029         if (word != 0) {
   2030             dvmVerifyObject((Object *)cursor);
   2031             size = objectSize((Object *)cursor);
   2032             size = alignUp(size, ALLOC_ALIGNMENT);
   2033             cursor += size;
   2034         } else {
   2035             /* Check for padding. */
   2036             while (*(unsigned long *)cursor == 0) {
   2037                 cursor += 4;
   2038                 if (cursor == end) break;
   2039             }
   2040             /* Punt if something went wrong. */
   2041             assert(cursor == end);
   2042         }
   2043     }
   2044 }
   2045 
   2046 static void describeBlockQueue(const HeapSource *heapSource)
   2047 {
   2048     size_t block, count;
   2049     char space;
   2050 
   2051     block = heapSource->queueHead;
   2052     count = 0;
   2053     LOG_SCAV(">>> describeBlockQueue(heapSource=%p)", heapSource);
   2054     /* Count the number of blocks enqueued. */
   2055     while (block != QUEUE_TAIL) {
   2056         block = heapSource->blockQueue[block];
   2057         ++count;
   2058     }
   2059     LOG_SCAV("blockQueue %zu elements, enqueued %zu",
   2060                  count, heapSource->queueSize);
   2061     block = heapSource->queueHead;
   2062     while (block != QUEUE_TAIL) {
   2063         space = heapSource->blockSpace[block];
   2064         LOG_SCAV("block=%zu@%p,space=%zu", block, blockToAddress(heapSource,block), space);
   2065         block = heapSource->blockQueue[block];
   2066     }
   2067 
   2068     LOG_SCAV("<<< describeBlockQueue(heapSource=%p)", heapSource);
   2069 }
   2070 
   2071 /*
   2072  * Blackens promoted objects.
   2073  */
   2074 static void scavengeBlockQueue(void)
   2075 {
   2076     HeapSource *heapSource;
   2077     size_t block;
   2078 
   2079     LOG_SCAV(">>> scavengeBlockQueue()");
   2080     heapSource = gDvm.gcHeap->heapSource;
   2081     describeBlockQueue(heapSource);
   2082     while (heapSource->queueHead != QUEUE_TAIL) {
   2083         block = heapSource->queueHead;
   2084         LOG_SCAV("Dequeueing block %zu\n", block);
   2085         scavengeBlock(heapSource, block);
   2086         heapSource->queueHead = heapSource->blockQueue[block];
   2087         LOG_SCAV("New queue head is %zu\n", heapSource->queueHead);
   2088     }
   2089     LOG_SCAV("<<< scavengeBlockQueue()");
   2090 }
   2091 
   2092 /*
   2093  * Scan the block list and verify all blocks that are marked as being
   2094  * in new space.  This should be parametrized so we can invoke this
   2095  * routine outside of the context of a collection.
   2096  */
   2097 static void verifyNewSpace(void)
   2098 {
   2099     HeapSource *heapSource;
   2100     size_t i;
   2101     size_t c0, c1, c2, c7;
   2102 
   2103     c0 = c1 = c2 = c7 = 0;
   2104     heapSource = gDvm.gcHeap->heapSource;
   2105     for (i = 0; i < heapSource->totalBlocks; ++i) {
   2106         switch (heapSource->blockSpace[i]) {
   2107         case BLOCK_FREE: ++c0; break;
   2108         case BLOCK_TO_SPACE: ++c1; break;
   2109         case BLOCK_FROM_SPACE: ++c2; break;
   2110         case BLOCK_CONTINUED: ++c7; break;
   2111         default: assert(!"reached");
   2112         }
   2113     }
   2114     LOG_VER("Block Demographics: "
   2115             "Free=%zu,ToSpace=%zu,FromSpace=%zu,Continued=%zu",
   2116             c0, c1, c2, c7);
   2117     for (i = 0; i < heapSource->totalBlocks; ++i) {
   2118         if (heapSource->blockSpace[i] != BLOCK_TO_SPACE) {
   2119             continue;
   2120         }
   2121         verifyBlock(heapSource, i);
   2122     }
   2123 }
   2124 
   2125 static void scavengeGlobals(void)
   2126 {
   2127     scavengeReference((Object **)(void *)&gDvm.classJavaLangClass);
   2128     scavengeReference((Object **)(void *)&gDvm.classJavaLangClassArray);
   2129     scavengeReference((Object **)(void *)&gDvm.classJavaLangError);
   2130     scavengeReference((Object **)(void *)&gDvm.classJavaLangObject);
   2131     scavengeReference((Object **)(void *)&gDvm.classJavaLangObjectArray);
   2132     scavengeReference((Object **)(void *)&gDvm.classJavaLangRuntimeException);
   2133     scavengeReference((Object **)(void *)&gDvm.classJavaLangString);
   2134     scavengeReference((Object **)(void *)&gDvm.classJavaLangThread);
   2135     scavengeReference((Object **)(void *)&gDvm.classJavaLangVMThread);
   2136     scavengeReference((Object **)(void *)&gDvm.classJavaLangThreadGroup);
   2137     scavengeReference((Object **)(void *)&gDvm.classJavaLangThrowable);
   2138     scavengeReference((Object **)(void *)&gDvm.classJavaLangStackTraceElement);
   2139     scavengeReference((Object **)(void *)&gDvm.classJavaLangStackTraceElementArray);
   2140     scavengeReference((Object **)(void *)&gDvm.classJavaLangAnnotationAnnotationArray);
   2141     scavengeReference((Object **)(void *)&gDvm.classJavaLangAnnotationAnnotationArrayArray);
   2142     scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectAccessibleObject);
   2143     scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectConstructor);
   2144     scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectConstructorArray);
   2145     scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectField);
   2146     scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectFieldArray);
   2147     scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectMethod);
   2148     scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectMethodArray);
   2149     scavengeReference((Object **)(void *)&gDvm.classJavaLangReflectProxy);
   2150     scavengeReference((Object **)(void *)&gDvm.classJavaLangExceptionInInitializerError);
   2151     scavengeReference((Object **)(void *)&gDvm.classJavaLangRefReference);
   2152     scavengeReference((Object **)(void *)&gDvm.classJavaNioReadWriteDirectByteBuffer);
   2153     scavengeReference((Object **)(void *)&gDvm.classJavaSecurityAccessController);
   2154     scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory);
   2155     scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember);
   2156     scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray);
   2157     scavengeReference((Object **)(void *)&gDvm.classOrgApacheHarmonyNioInternalDirectBuffer);
   2158     scavengeReference((Object **)(void *)&gDvm.classArrayBoolean);
   2159     scavengeReference((Object **)(void *)&gDvm.classArrayChar);
   2160     scavengeReference((Object **)(void *)&gDvm.classArrayFloat);
   2161     scavengeReference((Object **)(void *)&gDvm.classArrayDouble);
   2162     scavengeReference((Object **)(void *)&gDvm.classArrayByte);
   2163     scavengeReference((Object **)(void *)&gDvm.classArrayShort);
   2164     scavengeReference((Object **)(void *)&gDvm.classArrayInt);
   2165     scavengeReference((Object **)(void *)&gDvm.classArrayLong);
   2166 }
   2167 
   2168 void describeHeap(void)
   2169 {
   2170     HeapSource *heapSource;
   2171 
   2172     heapSource = gDvm.gcHeap->heapSource;
   2173     describeBlocks(heapSource);
   2174 }
   2175 
   2176 /*
   2177  * The collection interface.  Collection has a few distinct phases.
   2178  * The first is flipping AKA condemning AKA whitening the heap.  The
   2179  * second is to promote all objects which are pointed to by pinned or
   2180  * ambiguous references.  The third phase is tracing from the stacks,
   2181  * registers and various globals.  Lastly, a verification of the heap
   2182  * is performed.  The last phase should be optional.
   2183  */
   2184 void dvmScavengeRoots(void)  /* Needs a new name badly */
   2185 {
   2186     GcHeap *gcHeap;
   2187 
   2188     {
   2189         size_t alloc, unused, total;
   2190 
   2191         room(&alloc, &unused, &total);
   2192         LOG_SCAV("BEFORE GC: %zu alloc, %zu free, %zu total.",
   2193                      alloc, unused, total);
   2194     }
   2195 
   2196     gcHeap = gDvm.gcHeap;
   2197     dvmHeapSourceFlip();
   2198 
   2199     /*
   2200      * Promote blocks with stationary objects.
   2201      */
   2202     pinThreadList();
   2203     pinReferenceTable(&gDvm.jniGlobalRefTable);
   2204     pinReferenceTable(&gDvm.jniPinRefTable);
   2205     pinHashTableEntries(gDvm.loadedClasses);
   2206     pinHashTableEntries(gDvm.dbgRegistry);
   2207     pinPrimitiveClasses();
   2208     pinInternedStrings();
   2209 
   2210     // describeBlocks(gcHeap->heapSource);
   2211 
   2212     /*
   2213      * Create first, open new-space page right here.
   2214      */
   2215 
   2216     /* Reset allocation to an unallocated block. */
   2217     gDvm.gcHeap->heapSource->allocPtr = allocateBlocks(gDvm.gcHeap->heapSource, 1);
   2218     gDvm.gcHeap->heapSource->allocLimit = gDvm.gcHeap->heapSource->allocPtr + BLOCK_SIZE;
   2219     /*
   2220      * Hack: promote the empty block allocated above.  If the
   2221      * promotions that occurred above did not actually gray any
   2222      * objects, the block queue may be empty.  We must force a
   2223      * promotion to be safe.
   2224      */
   2225     promoteBlockByAddr(gDvm.gcHeap->heapSource, gDvm.gcHeap->heapSource->allocPtr);
   2226 
   2227     /*
   2228      * Scavenge blocks and relocate movable objects.
   2229      */
   2230 
   2231     LOG_SCAV("Scavenging gDvm.threadList");
   2232     scavengeThreadList();
   2233 
   2234     LOG_SCAV("Scavenging gDvm.gcHeap->referenceOperations");
   2235     scavengeLargeHeapRefTable(gcHeap->referenceOperations);
   2236 
   2237     LOG_SCAV("Scavenging gDvm.gcHeap->pendingFinalizationRefs");
   2238     scavengeLargeHeapRefTable(gcHeap->pendingFinalizationRefs);
   2239 
   2240     LOG_SCAV("Scavenging random global stuff");
   2241     scavengeReference(&gDvm.outOfMemoryObj);
   2242     scavengeReference(&gDvm.internalErrorObj);
   2243     scavengeReference(&gDvm.noClassDefFoundErrorObj);
   2244 
   2245     // LOG_SCAV("Scavenging gDvm.internedString");
   2246     scavengeInternedStrings();
   2247 
   2248     LOG_SCAV("Root scavenge has completed.");
   2249 
   2250     scavengeBlockQueue();
   2251 
   2252     LOG_SCAV("Re-snap global class pointers.");
   2253     scavengeGlobals();
   2254 
   2255     LOG_SCAV("New space scavenge has completed.");
   2256 
   2257     /*
   2258      * Process reference objects in strength order.
   2259      */
   2260 
   2261     LOG_REF("Processing soft references...");
   2262     preserveSoftReferences(&gDvm.gcHeap->softReferences);
   2263     clearWhiteReferences(&gDvm.gcHeap->softReferences);
   2264 
   2265     LOG_REF("Processing weak references...");
   2266     clearWhiteReferences(&gDvm.gcHeap->weakReferences);
   2267 
   2268     LOG_REF("Finding finalizations...");
   2269     processFinalizableReferences();
   2270 
   2271     LOG_REF("Processing f-reachable soft references...");
   2272     clearWhiteReferences(&gDvm.gcHeap->softReferences);
   2273 
   2274     LOG_REF("Processing f-reachable weak references...");
   2275     clearWhiteReferences(&gDvm.gcHeap->weakReferences);
   2276 
   2277     LOG_REF("Processing phantom references...");
   2278     clearWhiteReferences(&gDvm.gcHeap->phantomReferences);
   2279 
   2280     /*
   2281      * Verify the stack and heap.
   2282      */
   2283     dvmVerifyRoots();
   2284     verifyNewSpace();
   2285 
   2286     //describeBlocks(gcHeap->heapSource);
   2287 
   2288     clearFromSpace(gcHeap->heapSource);
   2289 
   2290     {
   2291         size_t alloc, rem, total;
   2292 
   2293         room(&alloc, &rem, &total);
   2294         LOG_SCAV("AFTER GC: %zu alloc, %zu free, %zu total.", alloc, rem, total);
   2295     }
   2296 }
   2297 
   2298 /*
   2299  * Interface compatibility routines.
   2300  */
   2301 
   2302 void dvmClearWhiteRefs(Object **list)
   2303 {
   2304     /* do nothing */
   2305     assert(*list == NULL);
   2306 }
   2307 
   2308 void dvmHandleSoftRefs(Object **list)
   2309 {
   2310     /* do nothing */
   2311     assert(*list == NULL);
   2312 }
   2313 
   2314 bool dvmHeapBeginMarkStep(GcMode mode)
   2315 {
   2316     /* do nothing */
   2317     return true;
   2318 }
   2319 
   2320 void dvmHeapFinishMarkStep(void)
   2321 {
   2322     /* do nothing */
   2323 }
   2324 
   2325 void dvmHeapMarkRootSet(void)
   2326 {
   2327     /* do nothing */
   2328 }
   2329 
   2330 void dvmHeapScanMarkedObjects(void)
   2331 {
   2332     dvmScavengeRoots();
   2333 }
   2334 
   2335 void dvmHeapScheduleFinalizations(void)
   2336 {
   2337     /* do nothing */
   2338 }
   2339 
   2340 void dvmHeapSweepUnmarkedObjects(GcMode mode, int *numFreed, size_t *sizeFreed)
   2341 {
   2342     *numFreed = 0;
   2343     *sizeFreed = 0;
   2344     /* do nothing */
   2345 }
   2346 
   2347 void dvmMarkObjectNonNull(const Object *obj)
   2348 {
   2349     assert(!"implemented");
   2350 }
   2351 
   2352 void dvmMarkDirtyObjects(void)
   2353 {
   2354     assert(!"implemented");
   2355 }
   2356 
   2357 void dvmHeapSourceThreadShutdown(void)
   2358 {
   2359     /* do nothing */
   2360 }
   2361