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