1/* 2 Title: Multi-Threaded Garbage Collector - Data sharing phase 3 4 Copyright (c) 2012, 2017 David C. J. Matthews 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 20*/ 21 22/* GC Sharing pass. 23 This pass is invoked only if the heap sizing code detects that heap space 24 is running very short because it adds a very considerable overhead to GC. 25 It aims to reduce the size of the live data in a similar way to the data 26 sharing function PolyML.shareCommonData by merging immutable cells that 27 contain data that cannot be distinguished. 28 29 This version of the code now does a deep structure merge in a similar 30 way to the full sharing function. This code first does a full pass 31 over the heap creating lists of cells that could possibly be merged. 32 There are separate lists for byte and word objects up to a fixed 33 size. Larger objects and other objects are not considered. Because 34 all the items in a list have the same length and type (flag bits) 35 we can use the length word to link the items in the list. A 36 consequence of this is that positive long precision values can be 37 shared but negative values cannot. 38 39 There is a sharing function that first distributes items into a 40 hash table. Then each hash table is sorted and as part of the 41 sorting process cells with the same contents are merged. One 42 cell is chosen and the length words on the others are set to be 43 forwarding pointers to the chosen cell. Hashing allows for easy 44 parallel processing. 45 46 The structure sharing code works by first sharing the byte 47 data which cannot contain pointers. Then the word data is processed 48 to separate out "tail" cells that contain only tagged integers or 49 pointers to cells that either cannot be merged, such as mutables, 50 or those that have already been processed, such as the byte data. 51 Any pointers to shared data are updated to point to the merged cell. 52 The tail cells are then sorted and shared using the sharing function 53 and become part of the "processed" set. This process is repeated to 54 find cells that are now tails and so on. 55 56 Compared with the full sharing code this is expensive since it 57 requires repeated scans of the list of unprocessed cells. In 58 particular there may be cells that form loops (basically closures 59 for mutually recusive functions) and if they are present they and 60 anything that points directly or indirectly at them will never 61 be removed from the list. We stop when it appears that we are 62 not making progress and simply do a final bit-wise share of the 63 remainder. 64*/ 65#ifdef HAVE_CONFIG_H 66#include "config.h" 67#elif defined(_WIN32) 68#include "winconfig.h" 69#else 70#error "No configuration file" 71#endif 72 73#ifdef HAVE_ASSERT_H 74#include <assert.h> 75#define ASSERT(x) assert(x) 76#else 77#define ASSERT(x) 78#endif 79 80#ifdef HAVE_STRING_H 81#include <string.h> 82#endif 83 84#ifdef HAVE_STDLIB_H 85#include <stdlib.h> 86#endif 87 88#include "globals.h" 89#include "processes.h" 90#include "gc.h" 91#include "scanaddrs.h" 92#include "bitmap.h" 93#include "memmgr.h" 94#include "diagnostics.h" 95#include "gctaskfarm.h" 96#include "heapsizing.h" 97 98class ObjEntry 99{ 100public: 101 ObjEntry(): objList(0), objCount(0), shareCount(0) {} 102 PolyObject *objList; 103 POLYUNSIGNED objCount; 104 POLYUNSIGNED shareCount; 105}; 106 107// There is an instance of this class for each combination of size and 108// word/byte. 109class SortVector 110{ 111public: 112 SortVector(): totalCount(0), carryOver(0) {} 113 114 void AddToVector(PolyObject *obj, POLYUNSIGNED length); 115 116 void SortData(void); 117 POLYUNSIGNED TotalCount() const { return totalCount; } 118 POLYUNSIGNED CurrentCount() const { return baseObject.objCount; } 119 POLYUNSIGNED Shared() const; 120 void SetLengthWord(POLYUNSIGNED l) { lengthWord = l; } 121 POLYUNSIGNED CarryOver() const { return carryOver; } 122 123 static void hashAndSortAllTask(GCTaskId*, void *a, void *b); 124 static void sharingTask(GCTaskId*, void *a, void *b); 125 static void wordDataTask(GCTaskId*, void *a, void *b); 126 127private: 128 void sortList(PolyObject *head, POLYUNSIGNED nItems, POLYUNSIGNED &count); 129 130 ObjEntry baseObject, processObjects[256]; 131 POLYUNSIGNED totalCount; 132 POLYUNSIGNED lengthWord; 133 POLYUNSIGNED carryOver; 134}; 135 136POLYUNSIGNED SortVector::Shared() const 137{ 138 // Add all the sharing counts 139 POLYUNSIGNED shareCount = baseObject.shareCount; 140 for (unsigned i = 0; i < 256; i++) 141 shareCount += processObjects[i].shareCount; 142 return shareCount; 143} 144 145void SortVector::AddToVector(PolyObject *obj, POLYUNSIGNED length) 146{ 147 obj->SetShareChain(baseObject.objList); 148 baseObject.objList = obj; 149 baseObject.objCount++; 150 totalCount++; 151} 152 153// The number of byte and word entries. 154// Objects of up to and including this size are shared. 155// Byte objects include strings so it is more likely that 156// larger objects will share. Word objects that share 157// are much more likely to be 2 or 3 words. 158#define NUM_BYTE_VECTORS 23 159#define NUM_WORD_VECTORS 11 160 161class GetSharing: public RecursiveScanWithStack 162{ 163public: 164 GetSharing(); 165 void SortData(void); 166 static void shareByteData(GCTaskId *, void *, void *); 167 static void shareWordData(GCTaskId *, void *, void *); 168 static void shareRemainingWordData(GCTaskId *, void *, void *); 169 170protected: 171 virtual bool TestForScan(PolyWord *); 172 virtual void MarkAsScanning(PolyObject *); 173 virtual void StackOverflow(void) { } // Ignore stack overflow 174 virtual void Completed(PolyObject *); 175 176private: 177 // The head of chains of cells of the same size 178 SortVector byteVectors[NUM_BYTE_VECTORS]; 179 SortVector wordVectors[NUM_WORD_VECTORS]; 180 181 POLYUNSIGNED largeWordCount, largeByteCount, excludedCount; 182public: 183 POLYUNSIGNED totalVisited, byteAdded, wordAdded, totalSize; 184}; 185 186GetSharing::GetSharing() 187{ 188 for (unsigned i = 0; i < NUM_BYTE_VECTORS; i++) 189 byteVectors[i].SetLengthWord((POLYUNSIGNED)i | _OBJ_BYTE_OBJ); 190 191 for (unsigned j = 0; j < NUM_WORD_VECTORS; j++) 192 wordVectors[j].SetLengthWord(j); 193 194 largeWordCount = largeByteCount = excludedCount = 0; 195 totalVisited = byteAdded = wordAdded = totalSize = 0; 196} 197 198bool GetSharing::TestForScan(PolyWord *pt) 199{ 200 PolyWord p = *pt; 201 ASSERT(p.IsDataPtr()); 202 PolyObject *obj = p.AsObjPtr(); 203 204 while (obj->ContainsForwardingPtr()) 205 { 206 obj = obj->GetForwardingPtr(); 207 *pt = obj; 208 } 209 ASSERT(obj == (*pt).AsObjPtr()); 210 211 PolyWord *lengthWord = ((PolyWord*)obj) - 1; 212 213 LocalMemSpace *space = gMem.LocalSpaceForAddress(lengthWord); 214 215 if (space == 0) 216 return false; // Ignore it if it points to a permanent area 217 218 if (space->bitmap.TestBit(space->wordNo(lengthWord))) 219 return false; 220 221 ASSERT(obj->ContainsNormalLengthWord()); 222 223 totalVisited += 1; 224 totalSize += obj->Length() + 1; 225 226 return true; 227} 228 229void GetSharing::MarkAsScanning(PolyObject *obj) 230{ 231 ASSERT(obj->ContainsNormalLengthWord()); 232 PolyWord *lengthWord = ((PolyWord*)obj) - 1; 233 LocalMemSpace *space = gMem.LocalSpaceForAddress(lengthWord); 234 ASSERT(! space->bitmap.TestBit(space->wordNo(lengthWord))); 235 space->bitmap.SetBit(space->wordNo(lengthWord)); 236} 237 238void GetSharing::Completed(PolyObject *obj) 239{ 240 // We mustn't include cells in the permanent area. 241 // We scan the permanent mutable areas for local addresses 242 // but we mustn't add the cells themselves. Normally they 243 // will be mutable so would be ignored but cells that have been 244 // locked will now be immutable. The test in TestForScan is bypassed 245 // by ScanAddressesInRegion. 246 PolyWord *lengthWord = ((PolyWord*)obj) - 1; 247 if (gMem.LocalSpaceForAddress(lengthWord) == 0) 248 return; 249 250 POLYUNSIGNED L = obj->LengthWord(); 251 // We have tables for word objects and byte objects 252 // We chain entries together using the length word so it 253 // is important that we only do this for objects that 254 // have no other bits in the header, such as the sign bit. 255 if ((L & _OBJ_PRIVATE_FLAGS_MASK) == 0) 256 { 257 POLYUNSIGNED length = obj->Length(); 258 if (length < NUM_WORD_VECTORS) 259 wordVectors[length].AddToVector(obj, length); 260 else largeWordCount++; 261 wordAdded++; 262 } 263 else if ((L & _OBJ_PRIVATE_FLAGS_MASK) == _OBJ_BYTE_OBJ) 264 { 265 POLYUNSIGNED length = obj->Length(); 266 if (length < NUM_BYTE_VECTORS) 267 byteVectors[length].AddToVector(obj, length); 268 else largeByteCount++; 269 byteAdded++; 270 } 271 else if (! OBJ_IS_CODE_OBJECT(L) && ! OBJ_IS_MUTABLE_OBJECT(L)) 272 excludedCount++; // Code and mutables can't be shared - see what could be 273} 274 275// Quicksort the list to detect cells with the same content. These are made 276// to share and removed from further sorting. 277void SortVector::sortList(PolyObject *head, POLYUNSIGNED nItems, POLYUNSIGNED &shareCount) 278{ 279 while (nItems > 2) 280 { 281 size_t bytesToCompare = OBJ_OBJECT_LENGTH(lengthWord)*sizeof(PolyWord); 282 PolyObject *median = head; 283 head = head->GetShareChain(); 284 median->SetLengthWord(lengthWord); 285 PolyObject *left = 0, *right = 0; 286 POLYUNSIGNED leftCount = 0, rightCount = 0; 287 while (head != 0) 288 { 289 PolyObject *next = head->GetShareChain(); 290 int res = memcmp(median, head, bytesToCompare); 291 if (res == 0) 292 { 293 // Equal - they can share 294 head->SetForwardingPtr(median); 295 shareCount++; 296 } 297 else if (res < 0) 298 { 299 head->SetShareChain(left); 300 left = head; 301 leftCount++; 302 } 303 else 304 { 305 head->SetShareChain(right); 306 right = head; 307 rightCount++; 308 } 309 head = next; 310 } 311 // We can now drop the median and anything that shares with it. 312 // Process the smaller partition recursively and the larger by 313 // tail recursion. 314 if (leftCount < rightCount) 315 { 316 sortList(left, leftCount, shareCount); 317 head = right; 318 nItems = rightCount; 319 } 320 else 321 { 322 sortList(right, rightCount, shareCount); 323 head = left; 324 nItems = leftCount; 325 } 326 } 327 if (nItems == 1) 328 head->SetLengthWord(lengthWord); 329 else if (nItems == 2) 330 { 331 PolyObject *next = head->GetShareChain(); 332 head->SetLengthWord(lengthWord); 333 if (memcmp(head, next, OBJ_OBJECT_LENGTH(lengthWord)*sizeof(PolyWord)) == 0) 334 { 335 next->SetForwardingPtr(head); 336 shareCount++; 337 } 338 else next->SetLengthWord(lengthWord); 339 } 340} 341 342void SortVector::sharingTask(GCTaskId*, void *a, void *b) 343{ 344 SortVector *s = (SortVector *)a; 345 ObjEntry *o = (ObjEntry*)b; 346 s->sortList(o->objList, o->objCount, o->shareCount); 347} 348 349// Process one level of the word data. 350// N.B. The length words are updated without any locking. This is safe 351// because all length words are initially chain entries and a chain entry 352// can be replaced by another chain entry, a forwarding pointer or a normal 353// length word. Forwarding pointers and normal length words are only ever 354// set once. There is a small chance that we could lose some sharing as a 355// result of a race condition if a thread defers an object because it 356// contains a pointer with a chain entry and later sees an otherwise 357// equal object where another thread has replaced the chain with a 358// normal address, adds it to the list for immediate processing and 359// so never compares the two. 360void SortVector::wordDataTask(GCTaskId*, void *a, void *) 361{ 362 SortVector *s = (SortVector*)a; 363 // Partition the objects between those that have pointers to objects that are 364 // still to be processed and those that have been processed. 365 if (s->baseObject.objList == 0) 366 return; 367 PolyObject *h = s->baseObject.objList; 368 s->baseObject.objList = 0; 369 s->baseObject.objCount = 0; 370 POLYUNSIGNED words = OBJ_OBJECT_LENGTH(s->lengthWord); 371 s->carryOver = 0; 372 373 for (unsigned i = 0; i < 256; i++) 374 { 375 // Clear the entries in the hash table but not the sharing count. 376 s->processObjects[i].objList = 0; 377 s->processObjects[i].objCount = 0; 378 } 379 380 while (h != 0) 381 { 382 PolyObject *next = h->GetShareChain(); 383 bool deferred = false; 384 for (POLYUNSIGNED i = 0; i < words; i++) 385 { 386 PolyWord w = h->Get(i); 387 if (w.IsDataPtr()) 388 { 389 PolyObject *p = w.AsObjPtr(); 390 // Update the addresses of objects that have been merged 391 if (p->ContainsForwardingPtr()) 392 { 393 h->Set(i, p->GetForwardingPtr()); 394 s->carryOver++; 395 } 396 else if (p->ContainsShareChain()) 397 { 398 // If it is still to be shared leave it 399 deferred = true; 400 break; 401 } 402 } 403 } 404 if (deferred) 405 { 406 // We can't do it yet: add it back to the list 407 h->SetShareChain(s->baseObject.objList); 408 s->baseObject.objList = h; 409 s->baseObject.objCount++; 410 } 411 else 412 { 413 // Add it to the hash table. 414 unsigned char hash = 0; 415 for (POLYUNSIGNED i = 0; i < words*sizeof(PolyWord); i++) 416 hash += h->AsBytePtr()[i]; 417 h->SetShareChain(s->processObjects[hash].objList); 418 s->processObjects[hash].objList = h; 419 s->processObjects[hash].objCount++; 420 } 421 h = next; 422 } 423 s->SortData(); 424} 425 426// Sort the entries in the hash table. 427void SortVector::SortData() 428{ 429 for (unsigned j = 0; j < 256; j++) 430 { 431 ObjEntry *oentry = &processObjects[j]; 432 // Sort this entry. If it's very small just process it now. 433 switch (oentry->objCount) 434 { 435 case 0: break; // Nothing there 436 437 case 1: // Singleton - just restore the length word 438 oentry->objList->SetLengthWord(lengthWord); 439 break; 440 441 case 2: 442 { 443 // Two items - process now 444 PolyObject *obj1 = oentry->objList; 445 PolyObject *obj2 = obj1->GetShareChain(); 446 obj1->SetLengthWord(lengthWord); 447 if (memcmp(obj1, obj2, OBJ_OBJECT_LENGTH(lengthWord)*sizeof(PolyWord)) == 0) 448 { 449 obj2->SetForwardingPtr(obj1); 450 oentry->shareCount++; 451 } 452 else obj2->SetLengthWord(lengthWord); 453 break; 454 } 455 456 default: 457 gpTaskFarm->AddWorkOrRunNow(sharingTask, this, oentry); 458 } 459 } 460} 461 462void SortVector::hashAndSortAllTask(GCTaskId*, void *a, void *b) 463{ 464 SortVector *s = (SortVector *)a; 465 // Hash the contents of the base object then sort them. 466 for (unsigned i = 0; i < 256; i++) 467 { 468 // Clear the entries in the hash table but not the sharing count. 469 s->processObjects[i].objList = 0; 470 s->processObjects[i].objCount = 0; 471 } 472 PolyObject *h = s->baseObject.objList; 473 POLYUNSIGNED bytes = OBJ_OBJECT_LENGTH(s->lengthWord)*sizeof(PolyWord); 474 while (h != 0) 475 { 476 PolyObject *next = h->GetShareChain(); 477 unsigned char hash = 0; 478 for (POLYUNSIGNED j = 0; j < bytes; j++) 479 hash += h->AsBytePtr()[j]; 480 h->SetShareChain(s->processObjects[hash].objList); 481 s->processObjects[hash].objList = h; 482 s->processObjects[hash].objCount++; 483 h = next; 484 } 485 s->SortData(); 486} 487 488// Look for sharing between byte data. These cannot contain pointers 489// so they can all be processed together. 490void GetSharing::shareByteData(GCTaskId *, void *a, void *) 491{ 492 GetSharing *s = (GetSharing*)a; 493 for (unsigned i = 0; i < NUM_BYTE_VECTORS; i++) 494 { 495 if (s->byteVectors[i].CurrentCount() != 0) 496 gpTaskFarm->AddWorkOrRunNow(SortVector::hashAndSortAllTask, &(s->byteVectors[i]), 0); 497 } 498} 499 500// Process word data at this particular level 501void GetSharing::shareWordData(GCTaskId *, void *a, void *) 502{ 503 GetSharing *s = (GetSharing*)a; 504 for (unsigned i = 0; i < NUM_WORD_VECTORS; i++) 505 { 506 if (s->wordVectors[i].CurrentCount() != 0) 507 gpTaskFarm->AddWorkOrRunNow(SortVector::wordDataTask, &(s->wordVectors[i]), 0); 508 } 509} 510 511// Share any entries left. 512void GetSharing::shareRemainingWordData(GCTaskId *, void *a, void *) 513{ 514 GetSharing *s = (GetSharing*)a; 515 for (unsigned i = 0; i < NUM_WORD_VECTORS; i++) 516 { 517 if (s->wordVectors[i].CurrentCount() != 0) 518 gpTaskFarm->AddWorkOrRunNow(SortVector::hashAndSortAllTask, &(s->wordVectors[i]), 0); 519 } 520} 521 522void GetSharing::SortData() 523{ 524 // First process the byte objects. They cannot contain pointers. 525 // We create a task to do this so that we never have more threads 526 // running than given with --gcthreads. 527 gpTaskFarm->AddWorkOrRunNow(shareByteData, this, 0); 528 gpTaskFarm->WaitForCompletion(); 529 530 // Word data may contain pointers to other objects. If an object 531 // has been processed its header will contain either a normal length 532 // word or a forwarding pointer if it shares. We can process an 533 // object if every word in it is either a tagged integer or an 534 // address we have already processed. This works provided there 535 // are no loops so when we reach a stage where we are unable to 536 // process anything we simply run a final scan on the remainder. 537 // Loops can arise from the closures of mutually recursive functions. 538 539 // Now process the word entries until we have nothing left apart from loops. 540 POLYUNSIGNED lastCount = 0, lastShared = 0; 541 for (unsigned n = 0; n < NUM_WORD_VECTORS; n++) 542 lastCount += wordVectors[n].CurrentCount(); 543 544 for(unsigned pass = 1; lastCount != 0; pass++) 545 { 546 gpTaskFarm->AddWorkOrRunNow(shareWordData, this, 0); 547 gpTaskFarm->WaitForCompletion(); 548 549 // At each stage check that we have removed some items 550 // from the lists. 551 POLYUNSIGNED postCount = 0, postShared = 0, carryOver = 0; 552 for (unsigned i = 0; i < NUM_WORD_VECTORS; i++) 553 { 554 postCount += wordVectors[i].CurrentCount(); 555 postShared += wordVectors[i].Shared(); 556 carryOver += wordVectors[i].CarryOver(); 557 } 558 559 if (debugOptions & DEBUG_GC) 560 Log("GC: Share: Pass %u: %" POLYUFMT " removed (%1.1f%%) %" POLYUFMT " shared (%1.1f%%) %" POLYUFMT " remain. %" POLYUFMT " entries updated (%1.1f%%).\n", 561 pass, lastCount-postCount, (double)(lastCount-postCount) / (double) lastCount * 100.0, 562 postShared - lastShared, (double)(postShared - lastShared) / (double) (lastCount-postCount) * 100.0, 563 postCount, carryOver, (double)carryOver / (double)(lastCount-postCount) * 100.0); 564 565 // Condition for exiting the loop. There are some heuristics here. 566 // If we remove less than 10% in a pass it's probably not worth continuing 567 // unless the carry over is large. The "carry over" is the number of words updated as 568 // a result of the last pass. It represents the extra sharing we gained in this pass 569 // as a result of the last pass. If there are deep data structures that can be shared 570 // we get better sharing with more passes. If the data structures are shallow we will 571 // get as much sharing by just running the final pass. The first pass only carries 572 // over any sharing from the byte objects so we need to run at least one more before 573 // checking the carry over. 574 if (pass > 1 && (lastCount - postCount) * 10 < lastCount && (carryOver*2 < (lastCount-postCount) || (lastCount - postCount) * 1000 < lastCount )) 575 break; 576 577 lastCount = postCount; 578 lastShared = postShared; 579 } 580 581 // Process any remaining entries. There may be loops. 582 gpTaskFarm->AddWorkOrRunNow(shareRemainingWordData, this, 0); 583 gpTaskFarm->WaitForCompletion(); 584 585 if (debugOptions & DEBUG_GC) 586 { 587 POLYUNSIGNED postShared = 0; 588 for (unsigned i = 0; i < NUM_WORD_VECTORS; i++) 589 postShared += wordVectors[i].Shared(); 590 if (debugOptions & DEBUG_GC) 591 Log("GC: Share: Final pass %" POLYUFMT " removed %" POLYUFMT " shared (%1.1f%%).\n", 592 lastCount, postShared - lastShared, 593 (double)(postShared - lastShared) / (double) lastCount * 100.0); 594 } 595 596 // Calculate the totals. 597 POLYUNSIGNED totalSize = 0, totalShared = 0, totalRecovered = 0; 598 for (unsigned k = 0; k < NUM_BYTE_VECTORS; k++) 599 { 600 totalSize += byteVectors[k].TotalCount(); 601 POLYUNSIGNED shared = byteVectors[k].Shared(); 602 totalShared += shared; 603 totalRecovered += shared * (k+1); // Add 1 for the length word. 604 if (debugOptions & DEBUG_GC) 605 Log("GC: Share: Byte objects of size %u: %" POLYUFMT " objects %" POLYUFMT " shared\n", 606 k, byteVectors[k].TotalCount(), byteVectors[k].Shared()); 607 } 608 609 for (unsigned l = 0; l < NUM_WORD_VECTORS; l++) 610 { 611 totalSize += wordVectors[l].TotalCount(); 612 POLYUNSIGNED shared = wordVectors[l].Shared(); 613 totalShared += shared; 614 totalRecovered += shared * (l+1); 615 if (debugOptions & DEBUG_GC) 616 Log("GC: Share: Word objects of size %u: %" POLYUFMT " objects %" POLYUFMT " shared\n", 617 l, wordVectors[l].TotalCount(), wordVectors[l].Shared()); 618 } 619 620 if (debugOptions & DEBUG_GC) 621 { 622 Log("GC: Share: Total %" POLYUFMT " objects, %" POLYUFMT " shared (%1.0f%%). %" POLYUFMT " words recovered.\n", 623 totalSize, totalShared, (double)totalShared / (double)totalSize * 100.0, totalRecovered); 624 Log("GC: Share: Excluding %" POLYUFMT " large word objects %" POLYUFMT " large byte objects and %" POLYUFMT " others\n", 625 largeWordCount, largeByteCount, excludedCount); 626 } 627 628 gHeapSizeParameters.RecordSharingData(totalRecovered); 629} 630 631void GCSharingPhase(void) 632{ 633 mainThreadPhase = MTP_GCPHASESHARING; 634 635 GetSharing sharer; 636 637 for (std::vector<LocalMemSpace*>::iterator i = gMem.lSpaces.begin(); i < gMem.lSpaces.end(); i++) 638 { 639 LocalMemSpace *lSpace = *i; 640 lSpace->bitmap.ClearBits(0, lSpace->spaceSize()); 641 } 642 643 // Scan the code areas to share any constants. We don't share the code 644 // cells themselves. 645 for (std::vector<CodeSpace *>::iterator i = gMem.cSpaces.begin(); i < gMem.cSpaces.end(); i++) 646 { 647 CodeSpace *space = *i; 648 sharer.ScanAddressesInRegion(space->bottom, space->top); 649 } 650 651 if (debugOptions & DEBUG_GC) 652 Log("GC: Share: After scanning code: Total %" POLYUFMT " (%" POLYUFMT " words) byte %" POLYUFMT " word %" POLYUFMT ".\n", 653 sharer.totalVisited, sharer.totalSize, sharer.byteAdded, sharer.wordAdded); 654 655 // Process the permanent mutable areas and the code areas 656 for (std::vector<PermanentMemSpace*>::iterator i = gMem.pSpaces.begin(); i < gMem.pSpaces.end(); i++) 657 { 658 PermanentMemSpace *space = *i; 659 if (space->isMutable && ! space->byteOnly) 660 sharer.ScanAddressesInRegion(space->bottom, space->top); 661 } 662 663 if (debugOptions & DEBUG_GC) 664 Log("GC: Share: After scanning permanent: Total %" POLYUFMT " (%" POLYUFMT " words) byte %" POLYUFMT " word %" POLYUFMT ".\n", 665 sharer.totalVisited, sharer.totalSize, sharer.byteAdded, sharer.wordAdded); 666 667 // Process the RTS roots. 668 GCModules(&sharer); 669 670 if (debugOptions & DEBUG_GC) 671 Log("GC: Share: After scanning other roots: Total %" POLYUFMT " (%" POLYUFMT " words) byte %" POLYUFMT " word %" POLYUFMT ".\n", 672 sharer.totalVisited, sharer.totalSize, sharer.byteAdded, sharer.wordAdded); 673 674 gHeapSizeParameters.RecordGCTime(HeapSizeParameters::GCTimeIntermediate, "Table"); 675 676 // Sort and merge the data. 677 sharer.SortData(); 678 679 gHeapSizeParameters.RecordGCTime(HeapSizeParameters::GCTimeIntermediate, "Sort"); 680} 681