}  if (ZERO_NURSERY_IN_PARALLEL)
} }  if (ZERO_NURSERY_IN_PARALLEL) {
  static void collect () {    if (VM.BuildForEventLogging && VM.EventLoggingEnabled)      VM_EventLogger.logGarbageCollectionEvent();     // set running threads context regs so that a scan of its stack    // will start at the caller of collect (ie. VM_CollectorThread.run)    //    VM_Address fp = VM_Magic.getFramePointer();    VM_Address caller_ip = VM_Magic.getReturnAddress(fp);    VM_Address caller_fp = VM_Magic.getCallerFramePointer(fp);    VM_Thread.getCurrentThread().contextRegisters.setInnermost( caller_ip, caller_fp );     // BEGIN SINGLE GC THREAD SECTION - GC INITIALIZATION    double tempStart = 0.0, tempEnd = 0.0;     if ( VM_GCLocks.testAndSetInitLock() ) {      // Start timers to measure time since GC requested      //      startTime.start(VM_CollectorThread.gcBarrier.rendezvousStartTime);       initTime.start(startTime);      GCTime.start(initTime.lastStart);      // Set up flags       //      if (VM.VerifyAssertions) VM.assert( initGCDone == false );        gcInProgress = true;      gcDone = false;      gcCount++;      if (verbose >= 2) VM.sysWriteln("Starting GC ", gcCount);      // Set up common workqueue for num VPs participating which varies from GC to GC.      VM_GCWorkQueue.workQueue.initialSetup(VM_CollectorThread.numCollectors());      gcInProgress = true;      gcDone = false;      // invert the mark_flag value, used for marking BootImage objects      BOOT_MARK_VALUE = BOOT_MARK_VALUE ^ VM_CommonAllocatorHeader.GC_MARK_BIT_MASK;       // Now prepare large space for collection - clears out mark array      largeHeap.startCollect();      if (VM.ParanoidGCCheck) toHeap.unprotect();            // this gc thread copies its own VM_Processor, resets processor register & processor      // local allocation pointers (before copying first object to ToSpace)      gc_initProcessor();       // Some RVM VM_Processors may      // be blocked in native C and not participating in a collection.      prepareNonParticipatingVPsForGC();      // precopy new VM_Thread objects, updating schedulers threads array      // here done by one thread. could divide among multiple collector threads      gc_copyThreads();            VM_GCLocks.resetFinishLock();  // for singlethread'ing end of collections      // must sync memory changes so GC threads on other processors see above changes      // sync before setting initGCDone flag to allow other GC threads to proceed      VM_Magic.sync();          rootTime.start(initTime);      // set Done flag to allow other GC threads to begin processing      initGCDone = true;    } // END SINGLE GC THREAD SECTION - GC INITIALIZATION    else {      // Each GC thread must wait here until initialization is complete      // this should be short, if necessary at all, so we spin instead of sysYield      //      // It is NOT required that all GC threads reach here before any can proceed      //      tempStart = RENDEZVOUS_WAIT_TIME ? VM_Time.now() : 0.0;      while( initGCDone == false ); // spin until initialization finished      VM_Magic.isync();             // prevent following inst. from moving infront of waitloop      tempEnd = RENDEZVOUS_WAIT_TIME ? VM_Time.now() : 0.0;      // each gc thread copies own VM_Processor, resets processor register & processor      // local allocation pointers, copies activeThread (itself) and resets workqueue buffers      gc_initProcessor();    }        // ALL GC THREADS IN PARALLEL    // each GC threads acquires ptr to its thread object, for accessing thread local counters    // and workqueue pointers.  If the thread object needs to be moved, it has been, in copyThreads    // above, and its ref in the threads array (in copyThreads) and the activeThread field of the    // current processors VM_Processor (in initProcessor) have been updated  This means using either    // of those fields to get "currentThread" get the copied thread object.    //    VM_CollectorThread mylocal = VM_Magic.threadAsCollectorThread(VM_Thread.getCurrentThread());    if (RENDEZVOUS_WAIT_TIME) 	mylocal.rendezvousWaitTime += VM_CollectorThread.gcBarrier.rendezvousRecord(tempStart, tempEnd);    // following rendezvous seems to be necessary, we are not sure why. Without it,    // some processors proceed into finding roots, before all gc threads have    // executed the above gc_initProcessor, and this seems related to the failure.    //     mylocal.rendezvousWaitTime += VM_CollectorThread.gcBarrier.rendezvous(RENDEZVOUS_TIMES || RENDEZVOUS_WAIT_TIME);             // Begin finding roots for this collection.    // Root refs are updated and the copied object is enqueued for later scanning.    gc_scanProcessor();           // each gc threads scans its own processor object    VM_ScanStatics.scanStatics(); // all threads scan JTOC in parallel    gc_scanThreads();             // all GC threads process thread objects & scan their stacks    // This synchronization is necessary to ensure all stacks have been scanned    // and all internal save ip values have been updated before we scan copied    // objects.  Because if we scan a VM_Method, and then update its code pointer    // we can no longer compute old ip offsets for updating saved ip values    //    //    REQUIRED SYNCHRONIZATION - WAIT FOR ALL GC THREADS TO REACH HERE    mylocal.rendezvousWaitTime += VM_CollectorThread.gcBarrier.rendezvous(RENDEZVOUS_TIMES || RENDEZVOUS_WAIT_TIME);    if (mylocal.gcOrdinal == 1)	scanTime.start(rootTime);    // each GC thread processes its own work queue until empty    if (verbose >= 2) VM.sysWriteln("  Emptying work queue");    gc_emptyWorkQueue();    if (mylocal.gcOrdinal == 1)	finalizeTime.start(scanTime);        // If counting or timing in VM_GCWorkQueue, save current counter values    //    if (VM_GCWorkQueue.WORKQUEUE_COUNTS)   VM_GCWorkQueue.saveCounters(mylocal);    if (VM_GCWorkQueue.MEASURE_WAIT_TIMES || VM_CollectorThread.MEASURE_WAIT_TIMES)      VM_GCWorkQueue.saveWaitTimes(mylocal);    // If there are not any objects with finalizers skip finalization phases.    // Otherwise, one thread scans finalizer list for dead objects and resuscitated.    //   Then, all threads process objects in parallel.    if (VM_Finalizer.existObjectsWithFinalizers()) {      /*** The following reset() will wait for previous use of workqueue to finish	   ie. all threads to leave.  So no rendezvous is necessary (we hope)         Without the reset, a rendezvous is necessary because some "slow" gc threads may still be         in emptyWorkQueue (in VM_GCWorkQueue.getBufferAndWait) and have not seen         the completionFlag==true.  The following call to reset will reset that         flag to false, possibly leaving the slow GC threads stuck.  This rendezvous         ensures that all threads have left the previous emptyWorkQueue, before         doing the reset. (We could make reset smarter, and have it wait until         the threadsWaiting count returns to 0, before doing the reset - TODO)	 One thread scans the hasFinalizer list for dead objects.  They are made live	 again, and put into that threads work queue buffers.      ***/      if (mylocal.gcOrdinal == 1) {	VM_GCWorkQueue.workQueue.reset();   // reset work queue shared control variables	VM_Finalizer.moveToFinalizable();      }      // ALL threads have to wait to see if any finalizable objects are found      mylocal.rendezvousWaitTime += VM_CollectorThread.gcBarrier.rendezvous(RENDEZVOUS_TIMES || RENDEZVOUS_WAIT_TIME);      if (VM_Finalizer.foundFinalizableObject) 	gc_emptyWorkQueue();    }  //  end of Finalization Processing    if (verbose >= 2) VM.sysWriteln("  Finished finalizer processing");    if (mylocal.gcOrdinal == 1) finishTime.start(finalizeTime);          // gcDone flag has been set to false earlier    //    if ( VM_GCLocks.testAndSetFinishLock() ) {      // BEGIN SINGLE GC THREAD SECTION FOR END OF GC      // make any access to discarded fromspace impossible      if (VM.ParanoidGCCheck) {	  VM.sysWriteln("  Protecting fromspace");	  fromHeap.protect();	  toHeap.paranoidScan(fromHeap, false);      }      // Swap sense of toHeap and fromHeap      VM_ContiguousHeap temp = fromHeap;      fromHeap = toHeap;      toHeap = temp;      // toHeap is ready to be used for the next collection      toHeap.reset();      // round up current address in fromHeap to allow using efficient zeroPages()      fromHeap.roundUpPage();      // notify large space that all live objects marked at this point      largeHeap.endCollect();      // The remainder of the current semi-space must be zero'ed before allowing      // any allocations.  This collector can be compiled to do this in either       // of three ways:      //     - 1 GC threads zeros it all (the executing thread) (BAD !!)      //     - All threads zero chunks in parallel (the executing thread      //       determines the per thread regions to be zero'ed      //     - Zeroing is deferred until processors allocate processor      //       local chunks, while mutators are running (BEST ??)      //      if (!ZERO_NURSERY_IN_PARALLEL && !ZERO_CHUNKS_ON_ALLOCATION) {	// have one processor (the executing one) do all the zeroing	fromHeap.zeroFreeSpace();      }      // reset lock for next GC before starting mutators      VM_GCLocks.reset();      prepareNonParticipatingVPsForAllocation();      gcInProgress = false;           // reset the flag used to make GC threads wait until GC initialization      // completed....for the next GC       initGCDone = false;      VM_Magic.sync();      gcDone = true;  // lets spinning GC threads continue           }  // END OF SINGLE THREAD SECTION    else {      // other GC threads spin wait until finishing thread completes      double start = RENDEZVOUS_WAIT_TIME ? VM_Time.now() : 0.0;      while( gcDone == false );      VM_Magic.isync();           // prevent from moving infront of waitloop      if (RENDEZVOUS_WAIT_TIME) 	  mylocal.rendezvousWaitTime += VM_CollectorThread.gcBarrier.rendezvousRecord(start, VM_Time.now());    }    // ALL GC THREADS IN PARALLEL - AFTER COLLECTION    // have each processor begin allocations in next mutator cycle in what    // remains of the current chunk acquired for copying objects during GC.    // Note. it must be zero'ed    //    if (PROCESSOR_LOCAL_ALLOCATE) {      if (PROCESSOR_LOCAL_MATURE_ALLOCATE) 	VM_Chunk.swapChunks(VM_Processor.getCurrentProcessor());      else 	VM_Chunk.resetChunk1(VM_Processor.getCurrentProcessor(), fromHeap, false);    }    if (ZERO_NURSERY_IN_PARALLEL)       fromHeap.zeroFreeSpaceParallel();    // Each GC thread increments adds its wait times for this collection    // into its total wait time - for printSummaryStatistics output    //    if (VM_CollectorThread.MEASURE_WAIT_TIMES)      mylocal.incrementWaitTimeTotals();    if (mylocal.gcOrdinal == 1) {	finishTime.stop();	GCTime.stop(finishTime.lastStop);    }    // generate -verbosegc output, done here after zeroing nursery.     // only approx. since other gc threads may be zeroing in parallel    // this is done by the 1 gc thread that finished the preceeding GC    //    if (mylocal.gcOrdinal == 1) {	updateGCStats(DEFAULT, fromHeap.current().diff(fromHeap.start));	printGCStats(DEFAULT);    }    return;  }  // collect