001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.mm.mminterface;
014    
015    import org.jikesrvm.ArchitectureSpecific;
016    import org.jikesrvm.VM;
017    import org.jikesrvm.compilers.common.CompiledMethods;
018    import org.jikesrvm.mm.mmtk.Collection;
019    import org.jikesrvm.mm.mmtk.MMTk_Events;
020    import org.jikesrvm.mm.mmtk.ScanThread;
021    import org.jikesrvm.mm.mmtk.Scanning;
022    import org.jikesrvm.runtime.Magic;
023    import org.jikesrvm.runtime.Time;
024    import org.jikesrvm.scheduler.Synchronization;
025    import org.jikesrvm.scheduler.RVMThread;
026    import org.mmtk.plan.Plan;
027    import org.mmtk.utility.heap.HeapGrowthManager;
028    import org.mmtk.utility.options.Options;
029    import org.vmmagic.pragma.BaselineNoRegisters;
030    import org.vmmagic.pragma.BaselineSaveLSRegisters;
031    import org.vmmagic.pragma.Interruptible;
032    import org.vmmagic.pragma.NoOptCompile;
033    import org.vmmagic.pragma.NonMoving;
034    import org.vmmagic.pragma.Uninterruptible;
035    import org.vmmagic.pragma.Unpreemptible;
036    import org.vmmagic.pragma.UnpreemptibleNoWarn;
037    import org.vmmagic.unboxed.Address;
038    import org.vmmagic.unboxed.Offset;
039    
040    /**
041     * System thread used to preform garbage collections.
042     *
043     * These threads are created by VM.boot() at runtime startup. One is created for
044     * each processor that will (potentially) participate in garbage collection.
045     *
046     * <pre>
047     * Its &quot;run&quot; method does the following:
048     *    1. wait for a collection request
049     *    2. synchronize with other collector threads (stop mutation)
050     *    3. reclaim space
051     *    4. synchronize with other collector threads (resume mutation)
052     *    5. goto 1
053     * </pre>
054     *
055     * Between collections, the collector threads are parked on a pthread
056     * condition variable.  A collection in initiated by a call to the static
057     * {@link #collect(Handshake,int)} method, which calls
058     * {@link Handshake#requestAndAwaitCompletion} to signal the threads.
059     * The collection commences when all
060     * scheduled collector threads arrive at the first "rendezvous" in the run
061     * methods run loop.
062     *
063     * An instance of Handshake contains state information for the "current"
064     * collection. When a collection is finished, a new Handshake is allocated
065     * for the next garbage collection.
066     *
067     * @see Handshake
068     */
069    @NonMoving
070    public final class CollectorThread extends RVMThread {
071    
072      /***********************************************************************
073       *
074       * Class variables
075       */
076      private static final int verbose = 0;
077    
078      /** Name used by toString() and when we create the associated
079       * java.lang.Thread.  */
080      private static final String myName = "CollectorThread";
081    
082      /** When true, causes RVM collectors to display heap configuration
083       * at startup */
084      static final boolean DISPLAY_OPTIONS_AT_BOOT = false;
085    
086      /**
087       * When true, causes RVM collectors to measure time spent in each
088       * phase of collection. Will also force summary statistics to be
089       * generated.
090       */
091      public static final boolean TIME_GC_PHASES = false;
092    
093      /**
094       * When true, collector threads measure time spent waiting for
095       * buffers while processing the Work Deque, and time spent waiting
096       * in Rendezvous during the collection process. Will also force
097       * summary statistics to be generated.
098       */
099      public static final boolean MEASURE_WAIT_TIMES = false;
100    
101      /** gc threads are indexed from 1 for now... */
102      public static final int GC_ORDINAL_BASE = 1;
103    
104      /** array of size 1 to count arriving collector threads */
105      static final int[] participantCount;
106    
107      /** number of collections */
108      static int collectionCount;
109    
110      /**
111       * The Handshake object that contains the state of the next or
112       * current (in progress) collection.  Read by mutators when
113       * detecting a need for a collection, and passed to the collect
114       * method when requesting a collection.
115       */
116      public static final Handshake handshake;
117    
118      /** Use by collector threads to rendezvous during collection */
119      public static SynchronizationBarrier gcBarrier;
120    
121      /** The base collection attempt */
122      public static int collectionAttemptBase = 0;
123    
124      /***********************************************************************
125       *
126       * Instance variables
127       */
128      /** arrival order of collectorThreads participating in a collection */
129      private int gcOrdinal;
130    
131      /** used by each CollectorThread when scanning stacks for references */
132      private final ScanThread threadScanner = new ScanThread();
133    
134      /** time waiting in rendezvous (milliseconds) */
135      int timeInRendezvous;
136    
137      static boolean gcThreadRunning;
138    
139      /** The thread to use to determine stack traces if Throwables are created **/
140      private Address stackTraceThread;
141    
142      /** @return the thread scanner instance associated with this instance */
143      @Uninterruptible
144      public ScanThread getThreadScanner() { return threadScanner; }
145    
146      /***********************************************************************
147       *
148       * Initialization
149       */
150    
151      /**
152       * Class initializer.  This is executed <i>prior</i> to bootstrap
153       * (i.e. at "build" time).
154       */
155      static {
156        handshake = new Handshake();
157        participantCount = new int[1]; // counter for threads starting a collection
158      }
159    
160      /**
161       * Constructor
162       *
163       * @param stack The stack this thread will run on
164       */
165      CollectorThread(byte[] stack) {
166        super(stack, myName);
167        this.collectorContext = new Selected.Collector(this);
168        this.collectorContext.initCollector(nextId++);
169        makeDaemon(true); // this is redundant, but harmless
170      }
171    
172      /** Next collector thread id. Collector threads are not created concurrently. */
173      private static int nextId = 0;
174    
175      /**
176       * Is this the GC thread?
177       * @return true
178       */
179      @Uninterruptible
180      public boolean isGCThread() {
181        return true;
182      }
183    
184      /**
185       * Get the thread to use for building stack traces.
186       */
187      @Uninterruptible
188      @Override
189      public RVMThread getThreadForStackTrace() {
190        if (stackTraceThread.isZero())
191          return this;
192        return (RVMThread)Magic.addressAsObject(stackTraceThread);
193      }
194    
195      /**
196       * Set the thread to use for building stack traces.
197       */
198      @Uninterruptible
199      public void setThreadForStackTrace(RVMThread thread) {
200        stackTraceThread = Magic.objectAsAddress(thread);
201      }
202    
203      /**
204       * Set the thread to use for building stack traces.
205       */
206      @Uninterruptible
207      public void clearThreadForStackTrace() {
208        stackTraceThread = Address.zero();
209      }
210    
211      /**
212       * Initialize for boot image.
213       */
214      @Interruptible
215      public static void init() {
216        gcBarrier = new SynchronizationBarrier();
217      }
218      public static void boot() {
219        handshake.boot();
220        gcBarrier.boot();
221      }
222    
223      /**
224       * Make a collector thread that will participate in gc.<p>
225       *
226       * Note: the new thread's stack must be in pinned memory: currently
227       * done by allocating it in immortal memory.
228       *
229       * @return a new collector thread
230       */
231      @Interruptible
232      public static CollectorThread createActiveCollectorThread() {
233        byte[] stack = MemoryManager.newStack(ArchitectureSpecific.StackframeLayoutConstants.STACK_SIZE_COLLECTOR);
234        return new CollectorThread(stack);
235      }
236    
237      /**
238       * Initiate a garbage collection.  Called by a mutator thread when
239       * its allocator runs out of space.  The caller should pass the
240       * Handshake that was referenced by the static variable "collect"
241       * at the time space was unavailable.
242       *
243       * @param handshake Handshake for the requested collection
244       */
245      @Unpreemptible("Becoming another thread interrupts the current thread, avoid preemption in the process")
246      public static void collect(Handshake handshake, int why) {
247        RVMThread.getCurrentFeedlet().addEvent(MMTk_Events.events.gcStart, why);
248        handshake.requestAndAwaitCompletion(why);
249        RVMThread.getCurrentFeedlet().addEvent(MMTk_Events.events.gcStop);
250      }
251      /**
252       * Initiate a garbage collection at next GC safe point.  Called by a
253       * mutator thread at any time.  The caller should pass the
254       * Handshake that was referenced by the static variable
255       * "collect".
256       *
257       * @param handshake Handshake for the requested collection
258       */
259      @Unpreemptible("Becoming another thread interrupts the current thread, avoid preemption in the process")
260      public static void asyncCollect(Handshake handshake, int why) {
261        handshake.requestAndContinue(why);
262      }
263    
264      /**
265       * Override Thread.toString
266       *
267       * @return A string describing this thread.
268       */
269      @Uninterruptible
270      public String toString() {
271        return myName;
272      }
273    
274      /**
275       * Returns number of collector threads participating in a collection
276       *
277       * @return The number of collector threads participating in a collection
278       */
279      @Uninterruptible
280      public static int numCollectors() {
281        return (participantCount[0]);
282      }
283    
284      /**
285       * Return the GC ordinal for this collector thread. An integer,
286       * 1,2,...  assigned to each collector thread participating in the
287       * current collection.  Only valid while GC is "InProgress".
288       *
289       * @return The GC ordinal
290       */
291      @Uninterruptible
292      public int getGCOrdinal() {
293        return gcOrdinal;
294      }
295    
296      /**
297       * Set the GC ordinal for this collector thread.  An integer,
298       * 1,2,...  assigned to each collector thread participating in the
299       * current collection.
300       *
301       * @param ord The new GC ordinal for this thread
302       */
303      @Uninterruptible
304      public void setGCOrdinal(int ord) {
305        gcOrdinal = ord;
306      }
307    
308      /**
309       * Run method for collector thread (one per processor).  Enters
310       * an infinite loop, waiting for collections to be requested,
311       * performing those collections, and then waiting again.  Calls
312       * Collection.collect to perform the collection, which will be
313       * different for the different allocators/collectors that the RVM
314       * can be configured to use.
315       */
316      @NoOptCompile
317      // refs stored in registers by opt compiler will not be relocated by GC
318      @BaselineNoRegisters
319      // refs stored in registers by baseline compiler will not be relocated by GC, so use stack only
320      @BaselineSaveLSRegisters
321      // and store all registers from previous method in prologue, so that we can stack access them while scanning this thread.
322      @Unpreemptible
323      public void run() {
324        // this is kind of stupid.
325        gcOrdinal = Synchronization.fetchAndAdd(participantCount, Offset.zero(), 1) + GC_ORDINAL_BASE;
326        RVMThread.getCurrentThread().disableYieldpoints();
327        for (int count = 0; ; count++) {
328          // wait for collection to start
329    
330          RVMThread.getCurrentThread().enableYieldpoints();
331          /* suspend this thread: it will resume when scheduled by
332           * Handshake.request(). */
333          handshake.parkCollectorThread();
334    
335          RVMThread.getCurrentThread().disableYieldpoints();
336          if (verbose >= 2) VM.sysWriteln("GC Message: CT.run waking up");
337    
338          long startTime = Time.nanoTime();
339    
340          if (verbose > 2) VM.sysWriteln("GC Message: CT.run entering first rendezvous - gcOrdinal =", gcOrdinal);
341    
342          boolean userTriggered = handshake.gcTrigger == Collection.EXTERNAL_GC_TRIGGER;
343          boolean internalPhaseTriggered = handshake.gcTrigger == Collection.INTERNAL_PHASE_GC_TRIGGER;
344          if (gcOrdinal == GC_ORDINAL_BASE) {
345            Plan.setCollectionTrigger(handshake.gcTrigger);
346          }
347          /* block all threads.  note that some threads will have already blocked
348             themselves (if they had made their own GC requests). */
349          if (gcOrdinal == GC_ORDINAL_BASE) {
350            if (verbose>=2) VM.sysWriteln("Thread #",getThreadSlot()," is about to block a bunch of threads.");
351    
352            RVMThread.hardHandshakeSuspend(RVMThread.gcBlockAdapter,RVMThread.allButGC);
353    
354            if (verbose>=2) {
355              VM.sysWriteln("Thread #",getThreadSlot()," just blocked a bunch of threads.");
356              RVMThread.dumpAcct();
357            }
358          }
359    
360          /* wait for other collector threads to arrive or be made
361           * non-participants */
362          if (verbose >= 2) VM.sysWriteln("GC Message: CT.run  initializing rendezvous");
363          gcBarrier.startupRendezvous();
364          for (;;) {
365            /* actually perform the GC... */
366            if (verbose >= 2) VM.sysWriteln("GC Message: CT.run  starting collection");
367            Selected.Collector.get().collect(); // gc
368            if (verbose >= 2) VM.sysWriteln("GC Message: CT.run  finished collection");
369    
370            gcBarrier.rendezvous(5200);
371    
372            if (gcOrdinal == GC_ORDINAL_BASE) {
373              long elapsedTime = Time.nanoTime() - startTime;
374              HeapGrowthManager.recordGCTime(Time.nanosToMillis(elapsedTime));
375              if (Selected.Plan.get().lastCollectionFullHeap() && !internalPhaseTriggered) {
376                if (Options.variableSizeHeap.getValue() && !userTriggered) {
377                  // Don't consider changing the heap size if gc was forced by System.gc()
378                  HeapGrowthManager.considerHeapSize();
379                }
380                HeapGrowthManager.reset();
381              }
382    
383              if (internalPhaseTriggered) {
384                if (Selected.Plan.get().lastCollectionFailed()) {
385                  internalPhaseTriggered = false;
386                  Plan.setCollectionTrigger(Collection.INTERNAL_GC_TRIGGER);
387                }
388              }
389    
390              if (Scanning.threadStacksScanned()) {
391                /* Snip reference to any methods that are still marked
392                 * obsolete after we've done stack scans. This allows
393                 * reclaiming them on the next GC. */
394                CompiledMethods.snipObsoleteCompiledMethods();
395                Scanning.clearThreadStacksScanned();
396    
397                collectionAttemptBase++;
398              }
399    
400              collectionCount += 1;
401            }
402    
403            startTime = Time.nanoTime();
404            gcBarrier.rendezvous(5201);
405            boolean cont=Selected.Plan.get().lastCollectionFailed() && !Plan.isEmergencyCollection();
406            if (!cont) break;
407          }
408    
409          /* wait for other collector threads to arrive here */
410          rendezvous(5210);
411          if (verbose > 2) VM.sysWriteln("CollectorThread: past rendezvous 1 after collection");
412    
413          if (gcOrdinal == GC_ORDINAL_BASE && !internalPhaseTriggered) {
414            /* If the collection failed, we may need to throw OutOfMemory errors.
415             * As we have not cleared the GC flag, allocation is not budgeted.
416             *
417             * This is not flawless in the case we physically can not allocate
418             * anything right after a GC, but that case is unlikely (we can
419             * not make it happen) and is a lot of work to get around. */
420            if (Plan.isEmergencyCollection()) {
421              RVMThread.getCurrentThread().setEmergencyAllocation();
422              boolean gcFailed = Selected.Plan.get().lastCollectionFailed();
423              // Allocate OOMEs (some of which *may* not get used)
424              for(int t=0; t < RVMThread.numThreads; t++) {
425                RVMThread thread = RVMThread.threads[t];
426                if (thread != null) {
427                  if (thread.getCollectionAttempt() > 0) {
428                    /* this thread was allocating */
429                    if (gcFailed || thread.physicalAllocationFailed()) {
430                      allocateOOMEForThread(thread);
431                    }
432                  }
433                }
434              }
435              RVMThread.getCurrentThread().clearEmergencyAllocation();
436            }
437          }
438    
439          /* Wake up mutators waiting for this gc cycle and reset
440           * the handshake object to be used for next gc cycle.
441           * Note that mutators will not run until after thread switching
442           * is enabled, so no mutators can possibly arrive at old
443           * handshake object: it's safe to replace it with a new one. */
444          if (gcOrdinal == GC_ORDINAL_BASE) {
445    
446            // reset the handshake.  this ensures that once threads are awakened,
447            // any new GC requests that they make actually result in GC activity.
448            handshake.reset();
449            if (verbose>=2) VM.sysWriteln("Thread #",getThreadSlot()," just reset the handshake.");
450    
451            Plan.collectionComplete();
452            if (verbose>=2) VM.sysWriteln("Marked the collection as complete.");
453    
454            collectionAttemptBase = 0;
455    
456            if (verbose>=2) VM.sysWriteln("Thread #",getThreadSlot()," is unblocking a bunch of threads.");
457            // and now unblock all threads
458            RVMThread.hardHandshakeResume(RVMThread.gcBlockAdapter,RVMThread.allButGC);
459            if (verbose>=2) VM.sysWriteln("Thread #",getThreadSlot()," just unblocked a bunch of threads.");
460    
461            /* schedule the FinalizerThread, if there is work to do & it is idle */
462            Collection.scheduleFinalizerThread();
463          }
464    
465          /* final cleanup for initial collector thread */
466          if (gcOrdinal == GC_ORDINAL_BASE) {
467            /* clear the GC flags */
468            gcThreadRunning = false;
469          } // if designated thread
470          rendezvous(9999);
471        }  // end of while(true) loop
472    
473      }  // run
474    
475      /**
476       * Return true if no threads are still in GC.
477       *
478       * @return <code>true</code> if no threads are still in GC.
479       */
480      @Uninterruptible
481      public static boolean noThreadsInGC() {
482        return !gcThreadRunning;
483      }
484    
485      @Uninterruptible
486      public int rendezvous(int where) {
487        return gcBarrier.rendezvous(where);
488      }
489    
490      /**
491       * Allocate an OutOfMemoryError for a given thread.
492       * @param thread
493       */
494      @UnpreemptibleNoWarn("Calls out to interruptible OOME constructor")
495      public void allocateOOMEForThread(RVMThread thread) {
496        /* We are running inside a gc thread, so we will allocate if physically possible */
497        this.setThreadForStackTrace(thread);
498        thread.setOutOfMemoryError(new OutOfMemoryError());
499        this.clearThreadForStackTrace();
500      }
501    
502    }
503