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.mmtk;
014    
015    import org.mmtk.plan.TraceLocal;
016    import org.mmtk.plan.TransitiveClosure;
017    import org.mmtk.utility.Constants;
018    
019    import org.jikesrvm.jni.JNIEnvironment;
020    import org.jikesrvm.jni.JNIGlobalRefTable;
021    import org.jikesrvm.mm.mminterface.Selected;
022    import org.jikesrvm.mm.mminterface.CollectorThread;
023    import org.jikesrvm.mm.mminterface.MemoryManagerConstants;
024    import org.jikesrvm.mm.mminterface.SpecializedScanMethod;
025    import org.jikesrvm.VM;
026    import org.jikesrvm.classloader.RVMClass;
027    import org.jikesrvm.classloader.RVMType;
028    import org.jikesrvm.objectmodel.ObjectModel;
029    import org.jikesrvm.runtime.Entrypoints;
030    import org.jikesrvm.runtime.Magic;
031    import org.jikesrvm.scheduler.RVMThread;
032    
033    import org.vmmagic.unboxed.*;
034    import org.vmmagic.pragma.*;
035    
036    @Uninterruptible
037    public final class Scanning extends org.mmtk.vm.Scanning implements Constants {
038      /****************************************************************************
039       *
040       * Class variables
041       */
042      private static final boolean TRACE_PRECOPY = false; // DEBUG
043    
044      /** Counter to track index into thread table for root tracing.  */
045      private static final SynchronizedCounter threadCounter = new SynchronizedCounter();
046    
047      /** Status flag used to determine if stacks were scanned in this collection increment */
048      private static boolean threadStacksScanned = false;
049    
050      /**
051       * Were thread stacks scanned in this collection increment.
052       */
053      public static boolean threadStacksScanned() {
054        return threadStacksScanned;
055      }
056    
057      /**
058       * Clear the flag that indicates thread stacks have been scanned.
059       */
060      public static void clearThreadStacksScanned() {
061        threadStacksScanned = false;
062      }
063    
064      /**
065       * Scanning of a object, processing each pointer field encountered.
066       *
067       * @param trace The closure being used.
068       * @param object The object to be scanned.
069       */
070      @Inline
071      public void scanObject(TransitiveClosure trace, ObjectReference object) {
072        SpecializedScanMethod.fallback(object.toObject(), trace);
073      }
074    
075      /**
076       * Invoke a specialized scan method. Note that these methods must have been allocated
077       * explicitly through Plan and PlanConstraints.
078       *
079       * @param id The specialized method id
080       * @param trace The trace the method has been specialized for
081       * @param object The object to be scanned
082       */
083      @Inline
084      public void specializedScanObject(int id, TransitiveClosure trace, ObjectReference object) {
085        if (SpecializedScanMethod.ENABLED) {
086          SpecializedScanMethod.invoke(id, object.toObject(), trace);
087        } else {
088          SpecializedScanMethod.fallback(object.toObject(), trace);
089        }
090      }
091    
092    
093    
094      /**
095       * Precopying of a object's fields, processing each pointer field encountered.
096       *
097       * @param trace The trace being used.
098       * @param object The object to be scanned.
099       */
100      @Inline
101      public void precopyChildren(TraceLocal trace, ObjectReference object) {
102        RVMType type = ObjectModel.getObjectType(object.toObject());
103        if (type.isClassType()) {
104          RVMClass klass = type.asClass();
105          int[] offsets = klass.getReferenceOffsets();
106          for(int i=0; i < offsets.length; i++) {
107            trace.processPrecopyEdge(object.toAddress().plus(offsets[i]), false);
108          }
109        } else if (type.isArrayType() && type.asArray().getElementType().isReferenceType()) {
110          for(int i=0; i < ObjectModel.getArrayLength(object.toObject()); i++) {
111            trace.processPrecopyEdge(object.toAddress().plus(i << LOG_BYTES_IN_ADDRESS), false);
112          }
113        }
114      }
115    
116      /**
117       * Prepares for using the <code>computeAllRoots</code> method.  The
118       * thread counter allows multiple GC threads to co-operatively
119       * iterate through the thread data structure (if load balancing
120       * parallel GC threads were not important, the thread counter could
121       * simply be replaced by a for loop).
122       */
123      public void resetThreadCounter() {
124        threadCounter.reset();
125      }
126    
127      /**
128       * Pre-copy all potentially movable instances used in the course of
129       * GC.  This includes the thread objects representing the GC threads
130       * themselves.  It is crucial that these instances are forwarded
131       * <i>prior</i> to the GC proper.  Since these instances <i>are
132       * not</i> enqueued for scanning, it is important that when roots
133       * are computed the same instances are explicitly scanned and
134       * included in the set of roots.  The existence of this method
135       * allows the actions of calculating roots and forwarding GC
136       * instances to be decoupled.
137       *
138       * The thread table is scanned in parallel by each processor, by striding
139       * through the table at a gap of chunkSize*numProcs.  Feel free to adjust
140       * chunkSize if you want to tune a parallel collector.
141       *
142       * Explicitly no-inlined to prevent over-inlining of collectionPhase.
143       *
144       * TODO Experiment with specialization to remove virtual dispatch ?
145       */
146      @NoInline
147      public void preCopyGCInstances(TraceLocal trace) {
148        int chunkSize = 2;
149        int threadIndex, start, end, stride;
150        CollectorThread ct;
151    
152        stride = chunkSize * CollectorThread.numCollectors();
153        ct = Magic.threadAsCollectorThread(RVMThread.getCurrentThread());
154        start = (ct.getGCOrdinal() - 1) * chunkSize;
155    
156        int numThreads = RVMThread.numThreads;
157        if (TRACE_PRECOPY)
158          VM.sysWriteln(ct.getGCOrdinal()," preCopying ",numThreads," threads");
159    
160        ObjectReference threadTable = ObjectReference.fromObject(RVMThread.threads);
161        while (start < numThreads) {
162          end = start + chunkSize;
163          if (end > numThreads)
164            end = numThreads;      // End of the table - partial chunk
165          if (TRACE_PRECOPY) {
166            VM.sysWriteln(ct.getGCOrdinal()," Chunk start",start);
167            VM.sysWriteln(ct.getGCOrdinal()," Chunk end  ",end);
168          }
169          for (threadIndex = start; threadIndex < end; threadIndex++) {
170            RVMThread thread = RVMThread.threads[threadIndex];
171            if (thread != null) {
172              // FIXME: is this even remotely needed?
173              /* Copy the thread object - use address arithmetic to get the address
174               * of the array entry */
175              if (TRACE_PRECOPY) {
176                VM.sysWriteln(ct.getGCOrdinal()," Forwarding thread ",threadIndex);
177                VM.sysWriteln(ct.getGCOrdinal()," with slot number ",thread.getThreadSlot());
178                VM.sysWrite(ct.getGCOrdinal()," Old address ");
179                VM.sysWriteln(ObjectReference.fromObject(thread).toAddress());
180              }
181              Address threadTableSlot = threadTable.toAddress().plus(threadIndex<<LOG_BYTES_IN_ADDRESS);
182              if (VM.VerifyAssertions) {
183                Address a = ObjectReference.fromObject(thread).toAddress();
184                Address b = Selected.Plan.get().loadObjectReference(threadTableSlot).toAddress();
185                VM._assert(a.EQ(b), "Thread table address arithmetic is wrong!");
186              }
187              trace.processPrecopyEdge(threadTableSlot, false);
188              // don't need to reload thread from thread table slot because threads
189              // are non-moving
190              if (TRACE_PRECOPY) {
191                VM.sysWrite(ct.getGCOrdinal()," New address ");
192                VM.sysWriteln(ObjectReference.fromObject(thread).toAddress());
193              }
194              precopyChildren(trace, ObjectReference.fromObject(thread));
195    
196              /* Registers */
197              if (TRACE_PRECOPY) {
198                VM.sysWriteln(ct.getGCOrdinal()," old cr address: ",Magic.objectAsAddress(thread.getContextRegisters()));
199              }
200              trace.processPrecopyEdge(Magic.objectAsAddress(thread).plus(Entrypoints.threadContextRegistersField.getOffset()), true);
201              if (TRACE_PRECOPY) {
202                VM.sysWriteln(ct.getGCOrdinal()," for thread ",Magic.objectAsAddress(thread));
203                VM.sysWriteln(ct.getGCOrdinal()," new cr address: ",Magic.objectAsAddress(thread.getContextRegisters()));
204              }
205    
206              trace.processPrecopyEdge(Magic.objectAsAddress(thread).plus(Entrypoints.threadContextRegistersSaveField.getOffset()), true);
207              trace.processPrecopyEdge(Magic.objectAsAddress(thread).plus(Entrypoints.threadExceptionRegistersField.getOffset()), true);
208    
209              if (thread.getJNIEnv() != null) {
210                // Right now, jniEnv are Java-visible objects (not C-visible)
211                // if (VM.VerifyAssertions)
212                //   VM._assert(Plan.willNotMove(Magic.objectAsAddress(thread.jniEnv)));
213                trace.processPrecopyEdge(Magic.objectAsAddress(thread).plus(Entrypoints.jniEnvField.getOffset()), true);
214                trace.processPrecopyEdge(Magic.objectAsAddress(thread.getJNIEnv()).plus(Entrypoints.JNIRefsField.getOffset()), true);
215                trace.processPrecopyEdge(Magic.objectAsAddress(thread.getJNIEnv()).plus(Entrypoints.JNIEnvSavedTRField.getOffset()), true);
216                trace.processPrecopyEdge(Magic.objectAsAddress(thread.getJNIEnv()).plus(Entrypoints.JNIPendingExceptionField.getOffset()), true);
217              }
218            }
219          } // end of for loop
220          start = start + stride;
221        }
222      }
223    
224      /**
225       * Computes static roots.  This method establishes all such roots for
226       * collection and places them in the root locations queue.  This method
227       * should not have side effects (such as copying or forwarding of
228       * objects).  There are a number of important preconditions:
229       *
230       * <ul>
231       * <li> All objects used in the course of GC (such as the GC thread
232       * objects) need to be "pre-copied" prior to calling this method.
233       * <li> The <code>threadCounter</code> must be reset so that load
234       * balancing parallel GC can share the work of scanning threads.
235       * </ul>
236       *
237       * @param trace The trace to use for computing roots.
238       */
239      public void computeStaticRoots(TraceLocal trace) {
240        /* scan statics */
241        ScanStatics.scanStatics(trace);
242      }
243    
244      /**
245       * Computes global roots.  This method establishes all such roots for
246       * collection and places them in the root locations queue.  This method
247       * should not have side effects (such as copying or forwarding of
248       * objects).  There are a number of important preconditions:
249       *
250       * <ul>
251       * <li> All objects used in the course of GC (such as the GC thread
252       * objects) need to be "pre-copied" prior to calling this method.
253       * <li> The <code>threadCounter</code> must be reset so that load
254       * balancing parallel GC can share the work of scanning threads.
255       * </ul>
256       *
257       * @param trace The trace to use for computing roots.
258       */
259      public void computeGlobalRoots(TraceLocal trace) {
260        /* scan jni functions */
261        CollectorThread ct = Magic.threadAsCollectorThread(RVMThread.getCurrentThread());
262        Address jniFunctions = Magic.objectAsAddress(JNIEnvironment.JNIFunctions);
263        int threads = CollectorThread.numCollectors();
264        int size = JNIEnvironment.JNIFunctions.length();
265        int chunkSize = size / threads;
266        int start = (ct.getGCOrdinal() - 1) * chunkSize;
267        int end = (ct.getGCOrdinal() == threads) ? size : ct.getGCOrdinal() * chunkSize;
268    
269        for(int i=start; i < end; i++) {
270          trace.processRootEdge(jniFunctions.plus(i << LOG_BYTES_IN_ADDRESS), true);
271        }
272    
273        Address linkageTriplets = Magic.objectAsAddress(JNIEnvironment.LinkageTriplets);
274        if (linkageTriplets != null) {
275          for(int i=start; i < end; i++) {
276            trace.processRootEdge(linkageTriplets.plus(i << LOG_BYTES_IN_ADDRESS), true);
277          }
278        }
279    
280        /* scan jni global refs */
281        Address jniGlobalRefs = Magic.objectAsAddress(JNIGlobalRefTable.JNIGlobalRefs);
282        size = JNIGlobalRefTable.JNIGlobalRefs.length();
283        chunkSize = size / threads;
284        start = (ct.getGCOrdinal() - 1) * chunkSize;
285        end = (ct.getGCOrdinal() == threads) ? size : ct.getGCOrdinal() * chunkSize;
286    
287        for(int i=start; i < end; i++) {
288          trace.processRootEdge(jniGlobalRefs.plus(i << LOG_BYTES_IN_ADDRESS), true);
289        }
290      }
291    
292      /**
293       * Computes roots pointed to by threads, their associated registers
294       * and stacks.  This method places these roots in the root values,
295       * root locations and interior root locations queues.  This method
296       * should not have side effects (such as copying or forwarding of
297       * objects).  There are a number of important preconditions:
298       *
299       * <ul>
300       * <li> All objects used in the course of GC (such as the GC thread
301       * objects) need to be "pre-copied" prior to calling this method.
302       * <li> The <code>threadCounter</code> must be reset so that load
303       * balancing parallel GC can share the work of scanning threads.
304       * </ul>
305       *
306       * TODO rewrite to avoid the per-thread synchronization, like precopy.
307       *
308       * @param trace The trace to use for computing roots.
309       */
310      public void computeThreadRoots(TraceLocal trace) {
311        boolean processCodeLocations = MemoryManagerConstants.MOVES_CODE;
312    
313        /* Set status flag */
314        threadStacksScanned = true;
315    
316        /* scan all threads */
317        while (true) {
318          int threadIndex = threadCounter.increment();
319          if (threadIndex > RVMThread.numThreads) break;
320    
321          RVMThread thread = RVMThread.threads[threadIndex];
322          if (thread == null) continue;
323    
324          /* scan the thread (stack etc.) */
325          ScanThread.scanThread(thread, trace, processCodeLocations);
326    
327          /* identify this thread as a root */
328          trace.processRootEdge(Magic.objectAsAddress(RVMThread.threads).plus(threadIndex<<LOG_BYTES_IN_ADDRESS), false);
329        }
330    
331        /* flush out any remset entries generated during the above activities */
332        Selected.Mutator.get().flushRememberedSets();
333      }
334    
335      /**
336       * Compute all roots out of the VM's boot image (if any).  This method is a no-op
337       * in the case where the VM does not maintain an MMTk-visible Java space.   However,
338       * when the VM does maintain a space (such as a boot image) which is visible to MMTk,
339       * that space could either be scanned by MMTk as part of its transitive closure over
340       * the whole heap, or as a (considerable) performance optimization, MMTk could avoid
341       * scanning the space if it is aware of all pointers out of that space.  This method
342       * is used to establish the root set out of the scannable space in the case where
343       * such a space exists.
344       *
345       * @param trace The trace object to use to report root locations.
346       */
347      public void computeBootImageRoots(TraceLocal trace) {
348        ScanBootImage.scanBootImage(trace);
349      }
350    }
351