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.vmmagic.pragma.*;
016 import org.vmmagic.unboxed.*;
017
018 import org.jikesrvm.SizeConstants;
019 import org.jikesrvm.VM;
020 import org.jikesrvm.mm.mminterface.Selected;
021 import org.jikesrvm.runtime.Magic;
022 import org.jikesrvm.Services;
023 import org.mmtk.plan.TraceLocal;
024
025 /**
026 * This class manages the processing of finalizable objects.
027 */
028 // can this be a linked list?
029 @Uninterruptible
030 public final class FinalizableProcessor extends org.mmtk.vm.FinalizableProcessor implements SizeConstants {
031
032 /********************************************************************
033 * Class fields
034 */
035
036 /** The FinalizableProcessor singleton */
037 private static final FinalizableProcessor finalizableProcessor = new FinalizableProcessor();
038
039 /** Stress the system? */
040 private static final boolean STRESS = VM.ForceFrequentGC;
041
042 /** Initial size of the reference object table */
043 private static final int INITIAL_SIZE = STRESS ? 1 : 256;
044
045 /** Amount to grow the table by when it is filled */
046 private static final double GROWTH_FACTOR = 2.0;
047
048 /*************************************************************************
049 * Instance fields
050 */
051
052 /** Used to ensure mutual exclusion during table manipulation */
053 private final Lock lock = new Lock("AddressTable");
054
055 /** The table of candidates */
056 protected volatile AddressArray table = AddressArray.create(INITIAL_SIZE);
057
058 /** The table of ready objects */
059 protected volatile Object[] readyForFinalize = new Object[INITIAL_SIZE];
060
061 /** Index of first entry created since last collection */
062 protected int nurseryIndex = 0;
063
064 /** Index of the first free slot in the table. */
065 protected volatile int maxIndex = 0;
066
067 /** Next object ready to be finalized */
068 private volatile int nextReadyIndex = 0;
069
070 /** Last object ready to be finalized */
071 private volatile int lastReadyIndex = 0;
072
073 /**
074 * Create a new table.
075 */
076 protected FinalizableProcessor() {}
077
078 /**
079 * Allocate an entry in the table. This should be called from an unpreemptible
080 * context so that the entry can be filled. This method is responsible for growing
081 * the table if necessary.
082 */
083 @NoInline
084 @UnpreemptibleNoWarn("Non-preemptible but yield when table needs to be grown")
085 public void add(Object object) {
086 lock.acquire();
087 while (maxIndex>=table.length() || maxIndex >= freeReady()) {
088 int newTableSize=-1;
089 int newReadyForFinalizeSize=-1;
090 AddressArray newTable=null;
091 Object[] newReadyForFinalize=null;
092
093 if (maxIndex>=table.length()) {
094 newTableSize=STRESS ? table.length() + 1 : (int)(table.length() * GROWTH_FACTOR);
095 }
096
097 if (maxIndex>=freeReady()) {
098 newReadyForFinalizeSize=table.length() + countReady();
099 if (newReadyForFinalizeSize<=readyForFinalize.length) {
100 newReadyForFinalizeSize=-1;
101 }
102 }
103
104 {
105 lock.release();
106 if (newTableSize>=0) {
107 newTable=AddressArray.create(newTableSize);
108 }
109 if (newReadyForFinalizeSize>=0) {
110 newReadyForFinalize=new Object[newReadyForFinalizeSize];
111 }
112 lock.acquire();
113 }
114
115 if (maxIndex>=table.length() && newTable!=null) {
116 for (int i=0; i < table.length(); i++) {
117 newTable.set(i, table.get(i));
118 }
119 table = newTable;
120 }
121
122 if (maxIndex>=freeReady() && newReadyForFinalize!=null) {
123 int j = 0;
124 for(int i=nextReadyIndex; i < lastReadyIndex && i < readyForFinalize.length; i++) {
125 newReadyForFinalize[j++] = readyForFinalize[i];
126 }
127 if (lastReadyIndex < nextReadyIndex) {
128 for(int i=0; i < lastReadyIndex; i++) {
129 newReadyForFinalize[j++] = readyForFinalize[i];
130 }
131 }
132 lastReadyIndex = j;
133 nextReadyIndex = 0;
134 readyForFinalize = newReadyForFinalize;
135 }
136 }
137 table.set(maxIndex++, Magic.objectAsAddress(object));
138 lock.release();
139 }
140
141 /**
142 * Clear the contents of the table. This is called when reference types are
143 * disabled to make it easier for VMs to change this setting at runtime.
144 */
145 public void clear() {
146 maxIndex = 0;
147 }
148
149 /**
150 * Scan through all entries in the table and forward.
151 *
152 * Currently ignores the nursery hint.
153 *
154 * TODO parallelise this code?
155 *
156 * @param trace The trace
157 * @param nursery Is this a nursery collection ?
158 */
159 @Override
160 public void forward(TraceLocal trace, boolean nursery) {
161 for (int i=0 ; i < maxIndex; i++) {
162 ObjectReference ref = table.get(i).toObjectReference();
163 table.set(i, trace.getForwardedFinalizable(ref).toAddress());
164 }
165 }
166
167 /**
168 * Scan through the list of references. Calls ReferenceProcessor's
169 * processReference method for each reference and builds a new
170 * list of those references still active.
171 *
172 * Depending on the value of <code>nursery</code>, we will either
173 * scan all references, or just those created since the last scan.
174 *
175 * TODO parallelise this code
176 *
177 * @param nursery Scan only the newly created references
178 */
179 @Override
180 @UninterruptibleNoWarn
181 public void scan(TraceLocal trace, boolean nursery) {
182 int toIndex = nursery ? nurseryIndex : 0;
183
184 for (int fromIndex = toIndex; fromIndex < maxIndex; fromIndex++) {
185 ObjectReference ref = table.get(fromIndex).toObjectReference();
186
187 /* Determine liveness (and forward if necessary) */
188 if (trace.isLive(ref)) {
189 table.set(toIndex++, trace.getForwardedFinalizable(ref).toAddress());
190 continue;
191 }
192
193 /* Make ready for finalize */
194 ref = trace.retainForFinalize(ref);
195
196 /* Add to object table */
197 Offset offset = Word.fromIntZeroExtend(lastReadyIndex).lsh(LOG_BYTES_IN_ADDRESS).toOffset();
198 Selected.Plan.get().storeObjectReference(Magic.objectAsAddress(readyForFinalize).plus(offset), ref);
199 lastReadyIndex = (lastReadyIndex + 1) % readyForFinalize.length;
200 }
201 nurseryIndex = maxIndex = toIndex;
202 }
203
204 /**
205 * Get an object to run finalize().
206 *
207 * @return The object to finalize()
208 */
209 @NoInline
210 @Unpreemptible("Non-preemptible but may pause if another thread is growing the table")
211 public Object getReady() {
212 lock.acquire();
213 Object result = null;
214 if (nextReadyIndex != lastReadyIndex) {
215 result = readyForFinalize[nextReadyIndex];
216 Services.setArrayUninterruptible(readyForFinalize, nextReadyIndex, null);
217 nextReadyIndex = (nextReadyIndex + 1) % readyForFinalize.length;
218 }
219 lock.release();
220 return result;
221 }
222
223 /***********************************************************************
224 * Statistics and debugging
225 */
226
227 /**
228 * The number of entries in the table.
229 */
230 public int count() {
231 return maxIndex;
232 }
233
234 /**
235 * The number of entries ready to be finalized.
236 */
237 public int countReady() {
238 return ((lastReadyIndex - nextReadyIndex) + readyForFinalize.length) % readyForFinalize.length;
239 }
240
241 /**
242 * The number of entries ready to be finalized.
243 */
244 public int freeReady() {
245 return readyForFinalize.length - countReady();
246 }
247
248 /***********************************************************************
249 * Static methods.
250 */
251
252 /** Get the singleton */
253 public static FinalizableProcessor getProcessor() {
254 return finalizableProcessor;
255 }
256
257 /**
258 * Add a finalization candidate.
259 * @param object The object with a finalizer.
260 */
261 @Unpreemptible("Non-preemptible but may pause if table needs to be grown")
262 public static void addCandidate(Object object) {
263 finalizableProcessor.add(object);
264 }
265
266 /**
267 * Get an object to call the finalize() method on it.
268 */
269 @Unpreemptible("Non-preemptible but may pause if table is being grown")
270 public static Object getForFinalize() {
271 return finalizableProcessor.getReady();
272 }
273
274 /**
275 * The number of objects waiting for finalize() calls.
276 */
277 public static int countReadyForFinalize() {
278 return finalizableProcessor.countReady();
279 }
280 }