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