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