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