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