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 "run" 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