1 4 package com.tc.objectserver.core.impl; 5 6 import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList; 7 8 import com.tc.logging.TCLogger; 9 import com.tc.logging.TCLogging; 10 import com.tc.object.ObjectID; 11 import com.tc.objectserver.api.GCStats; 12 import com.tc.objectserver.api.ObjectManager; 13 import com.tc.objectserver.api.ObjectManagerEventListener; 14 import com.tc.objectserver.core.api.Filter; 15 import com.tc.objectserver.core.api.GarbageCollector; 16 import com.tc.objectserver.core.api.ManagedObject; 17 import com.tc.objectserver.impl.GCLogger; 18 import com.tc.objectserver.impl.GCStatsImpl; 19 import com.tc.objectserver.l1.api.ClientStateManager; 20 import com.tc.objectserver.managedobject.ManagedObjectChangeListener; 21 import com.tc.text.PrettyPrintable; 22 import com.tc.text.PrettyPrinter; 23 import com.tc.util.ObjectIDSet2; 24 import com.tc.util.SyncObjectIdSet; 25 import com.tc.util.concurrent.LifeCycleState; 26 import com.tc.util.concurrent.NullLifeCycleState; 27 import com.tc.util.concurrent.StoppableThread; 28 29 import java.util.ArrayList ; 30 import java.util.Collection ; 31 import java.util.Collections ; 32 import java.util.Iterator ; 33 import java.util.List ; 34 import java.util.Set ; 35 36 38 public class MarkAndSweepGarbageCollector implements GarbageCollector { 39 40 private static final TCLogger logger = TCLogging.getLogger(MarkAndSweepGarbageCollector.class); 41 private final GCLogger gcLogger; 42 43 private final List eventListeners = new CopyOnWriteArrayList(); 44 private static final ChangeCollector NULL_CHANGE_COLLECTOR = new ChangeCollector() { 45 public void changed(ObjectID changedObject, 46 ObjectID oldReference, 47 ObjectID newReference) { 48 return; 49 } 50 51 public void addNewReferencesTo(Set set) { 52 return; 53 } 54 55 public PrettyPrinter prettyPrint(PrettyPrinter out) { 56 return out.println("NULL CHANGE COLLECTOR"); 57 } 58 }; 59 private static final Filter NULL_FILTER = new Filter() { 60 public boolean shouldVisit(ObjectID referencedObject) { 61 return true; 62 } 63 }; 64 private final static LifeCycleState NULL_LIFECYCLE_STATE = new NullLifeCycleState(); 65 private static final int GC_SLEEP = 1; 66 private static final int GC_PAUSING = 2; 67 private static final int GC_PAUSED = 3; 68 69 private int state = GC_SLEEP; 70 private LifeCycleState lifeCycleState; 71 private int gcIteration = 0; 72 private volatile ChangeCollector referenceCollector = NULL_CHANGE_COLLECTOR; 73 private final ObjectManager objectManager; 74 private final ClientStateManager stateManager; 75 private LifeCycleState gcState = new NullLifeCycleState(); 76 77 public MarkAndSweepGarbageCollector(ObjectManager objectManager, ClientStateManager stateManager, boolean verboseGC) { 78 this.gcLogger = new GCLogger(logger, verboseGC); 79 this.objectManager = objectManager; 80 this.stateManager = stateManager; 81 } 82 83 private Set rescue(final Set gcResults, final List rescueTimes) { 84 long start = System.currentTimeMillis(); 85 Set rescueIds = new ObjectIDSet2(); 86 stateManager.addAllReferencedIdsTo(rescueIds); 87 int stateManagerIds = rescueIds.size(); 88 89 addNewReferencesTo(rescueIds); 90 int referenceCollectorIds = rescueIds.size() - stateManagerIds; 91 92 logger.debug("rescueIds: " + rescueIds.size() + ", stateManagerIds: " + stateManagerIds 93 + ", additional referenceCollectorIds: " + referenceCollectorIds); 94 95 rescueIds.retainAll(gcResults); 96 97 Filter rescueFilter = new Filter() { 98 public boolean shouldVisit(ObjectID referencedObject) { 99 return gcResults.contains(referencedObject); 100 } 101 }; 102 103 Set rv = collect(rescueFilter, rescueIds, gcResults, gcState); 104 rescueTimes.add(new Long (System.currentTimeMillis() - start)); 105 return rv; 106 } 107 108 public void gc() { 109 GCStatsImpl gcStats = new GCStatsImpl(gcIteration); 110 111 gcLogger.log_GCStart(gcIteration); 112 long startMillis = System.currentTimeMillis(); 113 gcStats.setStartTime(startMillis); 114 115 Set rootIDs = null; 116 SyncObjectIdSet managedIDs = null; 117 118 this.referenceCollector = new NewReferenceCollector(); 119 120 rootIDs = objectManager.getRootIDs(); 121 managedIDs = objectManager.getAllObjectIDs(); 122 123 gcStats.setBeginObjectCount(managedIDs.size()); 124 125 if (gcState.isStopRequested()) { return; } 126 127 gcLogger.log_markStart(managedIDs); 128 Set gcResults = collect(NULL_FILTER, rootIDs, managedIDs, gcState); 129 gcLogger.log_markResults(gcResults); 130 131 if (gcState.isStopRequested()) { return; } 132 133 List rescueTimes = new ArrayList(); 134 135 gcLogger.log_rescue(1, gcResults); 136 gcResults = rescue(gcResults, rescueTimes); 137 138 requestGCPause(); 139 140 gcLogger.log_quiescing(); 141 142 if (gcState.isStopRequested()) { return; } 143 144 objectManager.waitUntilReadyToGC(); 145 146 if (gcState.isStopRequested()) { return; } 147 148 long pauseStartMillis = System.currentTimeMillis(); 149 gcLogger.log_paused(); 150 151 153 gcLogger.log_rescue(2, gcResults); 154 155 gcStats.setCandidateGarbageCount(gcResults.size()); 156 Set toDelete = rescue(new ObjectIDSet2(gcResults), rescueTimes); 157 158 if (gcState.isStopRequested()) { return; } 159 gcLogger.log_sweep(toDelete); 160 161 gcLogger.log_notifyGCComplete(); 162 163 this.referenceCollector = NULL_CHANGE_COLLECTOR; 164 165 objectManager.notifyGCComplete(toDelete); 167 168 gcStats.setActualGarbageCount(toDelete.size()); 169 long endMillis = System.currentTimeMillis(); 170 gcStats.setElapsedTime(endMillis - startMillis); 171 gcLogger.log_GCComplete(startMillis, pauseStartMillis, rescueTimes, endMillis, gcIteration); 172 173 gcLogger.push(gcStats); 174 fireGCCompleteEvent(gcStats); 175 gcIteration++; 176 } 177 178 public void changed(ObjectID changedObject, ObjectID oldReference, ObjectID newReference) { 179 referenceCollector.changed(changedObject, oldReference, newReference); 180 } 181 182 public Set collect(Filter filter, Collection rootIds, Set managedObjectIds) { 183 return collect(filter, rootIds, managedObjectIds, NULL_LIFECYCLE_STATE); 184 } 185 186 public Set collect(Filter filter, Collection rootIds, Set managedObjectIds, LifeCycleState aLifeCycleState) { 187 this.lifeCycleState = aLifeCycleState; 188 189 long start = System.currentTimeMillis(); 190 logstart_collect(rootIds, managedObjectIds); 191 192 for (Iterator i = rootIds.iterator(); i.hasNext();) { 193 ObjectID rootId = (ObjectID) i.next(); 194 managedObjectIds.remove(rootId); 195 if (lifeCycleState.isStopRequested()) return Collections.EMPTY_SET; 196 collectRoot(filter, rootId, managedObjectIds); 197 } 198 199 profile_collect(start); 200 201 return managedObjectIds; 202 } 203 204 private void collectRoot(Filter filter, ObjectID rootId, Set managedObjectIds) { 205 Set toBeVisited = new ObjectIDSet2(); 206 toBeVisited.add(rootId); 207 208 while (!toBeVisited.isEmpty()) { 209 210 for (Iterator i = new ObjectIDSet2(toBeVisited).iterator(); i.hasNext();) { 211 ObjectID id = (ObjectID) i.next(); 212 if (lifeCycleState.isStopRequested()) return; 213 ManagedObject obj = objectManager.getObjectByID(id); 214 toBeVisited.remove(id); 215 216 for (Iterator r = obj.getObjectReferences().iterator(); r.hasNext();) { 217 ObjectID mid = (ObjectID) r.next(); 218 if (mid.isNull() || !managedObjectIds.contains(mid)) continue; 219 if (filter.shouldVisit(mid)) toBeVisited.add(mid); 220 managedObjectIds.remove(mid); 221 } 222 objectManager.releaseReadOnly(obj); 223 } 224 } 225 } 226 227 public synchronized boolean isPausingOrPaused() { 228 return GC_SLEEP != state; 229 } 230 231 public synchronized boolean isPaused() { 232 return state == GC_PAUSED; 233 } 234 235 public synchronized void requestGCPause() { 236 state = GC_PAUSING; 237 } 238 239 public synchronized void notifyReadyToGC() { 240 if (state == GC_PAUSING) { 241 state = GC_PAUSED; 242 } 243 } 244 245 public synchronized void notifyGCComplete() { 246 state = GC_SLEEP; 247 } 248 249 public synchronized PrettyPrinter prettyPrint(PrettyPrinter out) { 250 return out.print(getClass().getName()).print("[state: ").print(stateToString()).print("]"); 251 } 252 253 private String stateToString() { 254 switch (state) { 255 case GC_SLEEP: 256 return "GC_SLEEP"; 257 case GC_PAUSING: 258 return "GC_PAUSING"; 259 case GC_PAUSED: 260 return "GC_PAUSED"; 261 default: 262 return "UNKNOWN"; 263 } 264 } 265 266 private void logstart_collect(Collection rootIds, Set managedObjectIds) { 267 if (logger.isDebugEnabled()) logger.debug("collect(): rootIds=" + rootIds.size() + ", managedObjectIds=" 268 + managedObjectIds.size()); 269 } 270 271 private void profile_collect(long start) { 272 if (logger.isDebugEnabled()) logger.debug("collect: " + (System.currentTimeMillis() - start) + " ms."); 273 } 274 275 private static class NewReferenceCollector implements ChangeCollector { 276 277 Set newReferences = new ObjectIDSet2(); 278 279 public void changed(ObjectID changedObject, ObjectID oldReference, ObjectID newReference) { 280 synchronized (newReferences) { 281 newReferences.add(newReference); 282 } 283 } 284 285 public void addNewReferencesTo(Set set) { 286 long start = System.currentTimeMillis(); 287 synchronized (newReferences) { 288 set.addAll(newReferences); 289 } 290 profile_addNewReferencesTo(start); 291 } 292 293 private void profile_addNewReferencesTo(long start) { 294 if (logger.isDebugEnabled()) { 295 logger.debug("addNewReferencesTo: " + (System.currentTimeMillis() - start) + " ms."); 296 } 297 } 298 299 public PrettyPrinter prettyPrint(PrettyPrinter out) { 300 synchronized (newReferences) { 301 return out.println("newReferences: ").println(newReferences); 302 } 303 } 304 } 305 306 private interface ChangeCollector extends ManagedObjectChangeListener, PrettyPrintable { 307 public void addNewReferencesTo(Set set); 308 } 309 310 public void addNewReferencesTo(Set rescueIds) { 311 referenceCollector.addNewReferencesTo(rescueIds); 312 } 313 314 public void start() { 315 gcState.start(); 316 } 317 318 public void stop() { 319 int count = 0; 320 while (!this.gcState.stopAndWait(5000) && (count < 6)) { 321 count++; 322 logger.warn("GC Thread did not stop"); 323 } 324 } 325 326 public void setState(StoppableThread st) { 327 this.gcState = st; 328 } 329 330 private void fireGCCompleteEvent(GCStats gcStats) { 331 for (Iterator iter = eventListeners.iterator(); iter.hasNext();) { 332 try { 333 ObjectManagerEventListener listener = (ObjectManagerEventListener) iter.next(); 334 listener.garbageCollectionComplete(gcStats); 335 } catch (Exception e) { 336 if (logger.isDebugEnabled()) { 337 logger.debug(e); 338 } else { 339 logger.warn("Exception in GCComplete event callback: " + e.getMessage()); 340 } 341 } 342 } 343 } 344 345 public void addListener(ObjectManagerEventListener listener) { 346 eventListeners.add(listener); 347 } 348 349 public GCStats[] getGarbageCollectorStats() { 350 return gcLogger.getGarbageCollectorStats(); 351 } 352 } | Popular Tags |