001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.objectserver.core.impl;
006:
007: import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArrayList;
008:
009: import com.tc.logging.TCLogger;
010: import com.tc.logging.TCLogging;
011: import com.tc.object.ObjectID;
012: import com.tc.objectserver.api.GCStats;
013: import com.tc.objectserver.api.ObjectManager;
014: import com.tc.objectserver.api.ObjectManagerEventListener;
015: import com.tc.objectserver.core.api.Filter;
016: import com.tc.objectserver.core.api.GarbageCollector;
017: import com.tc.objectserver.core.api.ManagedObject;
018: import com.tc.objectserver.impl.GCLogger;
019: import com.tc.objectserver.impl.GCStatsImpl;
020: import com.tc.objectserver.l1.api.ClientStateManager;
021: import com.tc.objectserver.managedobject.ManagedObjectChangeListener;
022: import com.tc.text.PrettyPrintable;
023: import com.tc.text.PrettyPrinter;
024: import com.tc.util.ObjectIDSet2;
025: import com.tc.util.State;
026: import com.tc.util.concurrent.LifeCycleState;
027: import com.tc.util.concurrent.NullLifeCycleState;
028: import com.tc.util.concurrent.StoppableThread;
029:
030: import java.util.ArrayList;
031: import java.util.Collection;
032: import java.util.Collections;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Set;
036:
037: /**
038: */
039: public class MarkAndSweepGarbageCollector implements GarbageCollector {
040:
041: private static final TCLogger logger = TCLogging
042: .getLogger(MarkAndSweepGarbageCollector.class);
043: private final GCLogger gcLogger;
044:
045: private final List eventListeners = new CopyOnWriteArrayList();
046: private static final ChangeCollector NULL_CHANGE_COLLECTOR = new ChangeCollector() {
047: public void changed(ObjectID changedObject,
048: ObjectID oldReference, ObjectID newReference) {
049: return;
050: }
051:
052: public void addNewReferencesTo(Set set) {
053: return;
054: }
055:
056: public PrettyPrinter prettyPrint(PrettyPrinter out) {
057: return out.println("NULL CHANGE COLLECTOR");
058: }
059: };
060: private static final Filter NULL_FILTER = new Filter() {
061: public boolean shouldVisit(ObjectID referencedObject) {
062: return true;
063: }
064: };
065: private final static LifeCycleState NULL_LIFECYCLE_STATE = new NullLifeCycleState();
066:
067: private static final State GC_DISABLED = new State("GC_DISABLED");
068: private static final State GC_RUNNING = new State("GC_RUNNING");
069: private static final State GC_SLEEP = new State("GC_SLEEP");
070: private static final State GC_PAUSING = new State("GC_PAUSING");
071: private static final State GC_PAUSED = new State("GC_PAUSED");
072: private static final State GC_DELETE = new State("GC_DELETE");
073:
074: private State state = GC_SLEEP;
075: private LifeCycleState lifeCycleState;
076: private int gcIteration = 0;
077: private volatile ChangeCollector referenceCollector = NULL_CHANGE_COLLECTOR;
078: private final ObjectManager objectManager;
079: private final ClientStateManager stateManager;
080: private LifeCycleState gcState = new NullLifeCycleState();
081: private volatile boolean started = false;
082:
083: public MarkAndSweepGarbageCollector(ObjectManager objectManager,
084: ClientStateManager stateManager, boolean verboseGC) {
085: this .gcLogger = new GCLogger(logger, verboseGC);
086: this .objectManager = objectManager;
087: this .stateManager = stateManager;
088: }
089:
090: private Set rescue(final Set gcResults, final List rescueTimes) {
091: long start = System.currentTimeMillis();
092: Set rescueIds = new ObjectIDSet2();
093: stateManager.addAllReferencedIdsTo(rescueIds);
094: int stateManagerIds = rescueIds.size();
095:
096: addNewReferencesTo(rescueIds);
097: int referenceCollectorIds = rescueIds.size() - stateManagerIds;
098:
099: logger.debug("rescueIds: " + rescueIds.size()
100: + ", stateManagerIds: " + stateManagerIds
101: + ", additional referenceCollectorIds: "
102: + referenceCollectorIds);
103:
104: rescueIds.retainAll(gcResults);
105:
106: Filter rescueFilter = new Filter() {
107: public boolean shouldVisit(ObjectID referencedObject) {
108: return gcResults.contains(referencedObject);
109: }
110: };
111:
112: Set rv = collect(rescueFilter, rescueIds, gcResults, gcState);
113: rescueTimes.add(new Long(System.currentTimeMillis() - start));
114: return rv;
115: }
116:
117: public void gc() {
118: if (!requestGCStart()) {
119: gcLogger.log_GCDisabled();
120: return;
121: }
122: GCStatsImpl gcStats = new GCStatsImpl(gcIteration);
123:
124: gcLogger.log_GCStart(gcIteration);
125: long startMillis = System.currentTimeMillis();
126: gcStats.setStartTime(startMillis);
127:
128: Set rootIDs = null;
129: ObjectIDSet2 managedIDs = null;
130:
131: this .referenceCollector = new NewReferenceCollector();
132:
133: rootIDs = objectManager.getRootIDs();
134: managedIDs = objectManager.getAllObjectIDs();
135:
136: gcStats.setBeginObjectCount(managedIDs.size());
137:
138: if (gcState.isStopRequested()) {
139: return;
140: }
141:
142: gcLogger.log_markStart(managedIDs);
143: Set gcResults = collect(NULL_FILTER, rootIDs, managedIDs,
144: gcState);
145: gcLogger.log_markResults(gcResults);
146:
147: if (gcState.isStopRequested()) {
148: return;
149: }
150:
151: List rescueTimes = new ArrayList();
152:
153: gcLogger.log_rescue(1, gcResults);
154: gcResults = rescue(gcResults, rescueTimes);
155:
156: requestGCPause();
157:
158: gcLogger.log_quiescing();
159:
160: if (gcState.isStopRequested()) {
161: return;
162: }
163:
164: objectManager.waitUntilReadyToGC();
165:
166: if (gcState.isStopRequested()) {
167: return;
168: }
169:
170: long pauseStartMillis = System.currentTimeMillis();
171: gcLogger.log_paused();
172:
173: // Assert.eval("No pending lookups allowed during GC pause.", pending.size() == 0);
174:
175: gcLogger.log_rescue(2, gcResults);
176:
177: gcStats.setCandidateGarbageCount(gcResults.size());
178: Set toDelete = Collections.unmodifiableSet(rescue(
179: new ObjectIDSet2(gcResults), rescueTimes));
180:
181: if (gcState.isStopRequested()) {
182: return;
183: }
184: gcLogger.log_sweep(toDelete);
185:
186: gcLogger.log_notifyGCComplete();
187:
188: this .referenceCollector = NULL_CHANGE_COLLECTOR;
189:
190: // Delete Garbage
191: objectManager.notifyGCComplete(toDelete);
192:
193: gcStats.setActualGarbageCount(toDelete.size());
194: long endMillis = System.currentTimeMillis();
195: gcStats.setElapsedTime(endMillis - startMillis);
196: gcLogger.log_GCComplete(startMillis, pauseStartMillis,
197: rescueTimes, endMillis, gcIteration);
198:
199: gcLogger.push(gcStats);
200: fireGCCompleteEvent(gcStats, toDelete);
201: gcIteration++;
202: }
203:
204: public void changed(ObjectID changedObject, ObjectID oldReference,
205: ObjectID newReference) {
206: referenceCollector.changed(changedObject, oldReference,
207: newReference);
208: }
209:
210: public Set collect(Filter filter, Collection rootIds,
211: Set managedObjectIds) {
212: return collect(filter, rootIds, managedObjectIds,
213: NULL_LIFECYCLE_STATE);
214: }
215:
216: public Set collect(Filter filter, Collection rootIds,
217: Set managedObjectIds, LifeCycleState aLifeCycleState) {
218: this .lifeCycleState = aLifeCycleState;
219:
220: long start = System.currentTimeMillis();
221: logstart_collect(rootIds, managedObjectIds);
222:
223: for (Iterator i = rootIds.iterator(); i.hasNext();) {
224: ObjectID rootId = (ObjectID) i.next();
225: managedObjectIds.remove(rootId);
226: if (lifeCycleState.isStopRequested())
227: return Collections.EMPTY_SET;
228: collectRoot(filter, rootId, managedObjectIds);
229: }
230:
231: profile_collect(start);
232:
233: return managedObjectIds;
234: }
235:
236: private void collectRoot(Filter filter, ObjectID rootId,
237: Set managedObjectIds) {
238: Set toBeVisited = new ObjectIDSet2();
239: toBeVisited.add(rootId);
240:
241: while (!toBeVisited.isEmpty()) {
242:
243: for (Iterator i = new ObjectIDSet2(toBeVisited).iterator(); i
244: .hasNext();) {
245: ObjectID id = (ObjectID) i.next();
246: if (lifeCycleState.isStopRequested())
247: return;
248: ManagedObject obj = objectManager.getObjectByID(id);
249: toBeVisited.remove(id);
250:
251: for (Iterator r = obj.getObjectReferences().iterator(); r
252: .hasNext();) {
253: ObjectID mid = (ObjectID) r.next();
254: if (mid.isNull() || !managedObjectIds.contains(mid))
255: continue;
256: if (filter.shouldVisit(mid))
257: toBeVisited.add(mid);
258: managedObjectIds.remove(mid);
259: }
260: objectManager.releaseReadOnly(obj);
261: }
262: }
263: }
264:
265: private synchronized boolean requestGCStart() {
266: if (started && state == GC_SLEEP) {
267: state = GC_RUNNING;
268: return true;
269: }
270: // Can't start GC
271: return false;
272: }
273:
274: public synchronized void enableGC() {
275: if (GC_DISABLED == state) {
276: state = GC_SLEEP;
277: } else {
278: logger.warn("GC is already enabled : " + state);
279: }
280: }
281:
282: public synchronized boolean disableGC() {
283: if (GC_SLEEP == state) {
284: state = GC_DISABLED;
285: return true;
286: }
287: // GC is already running, cant be disabled
288: return false;
289: }
290:
291: public synchronized boolean isDisabled() {
292: return GC_DISABLED == state;
293: }
294:
295: public synchronized boolean isPausingOrPaused() {
296: return GC_PAUSED == state || GC_PAUSING == state;
297: }
298:
299: public synchronized boolean isPaused() {
300: return state == GC_PAUSED;
301: }
302:
303: public synchronized void requestGCPause() {
304: state = GC_PAUSING;
305: }
306:
307: public synchronized void notifyReadyToGC() {
308: if (state == GC_PAUSING) {
309: state = GC_PAUSED;
310: }
311: }
312:
313: public synchronized void notifyGCDeleteStarted() {
314: state = GC_DELETE;
315: }
316:
317: public synchronized void notifyGCComplete() {
318: state = GC_SLEEP;
319: }
320:
321: public synchronized PrettyPrinter prettyPrint(PrettyPrinter out) {
322: return out.print(getClass().getName()).print("[").print(state)
323: .print("]");
324: }
325:
326: private void logstart_collect(Collection rootIds,
327: Set managedObjectIds) {
328: if (logger.isDebugEnabled())
329: logger.debug("collect(): rootIds=" + rootIds.size()
330: + ", managedObjectIds=" + managedObjectIds.size());
331: }
332:
333: private void profile_collect(long start) {
334: if (logger.isDebugEnabled())
335: logger.debug("collect: "
336: + (System.currentTimeMillis() - start) + " ms.");
337: }
338:
339: private static class NewReferenceCollector implements
340: ChangeCollector {
341:
342: Set newReferences = new ObjectIDSet2();
343:
344: public void changed(ObjectID changedObject,
345: ObjectID oldReference, ObjectID newReference) {
346: synchronized (newReferences) {
347: newReferences.add(newReference);
348: }
349: }
350:
351: public void addNewReferencesTo(Set set) {
352: long start = System.currentTimeMillis();
353: synchronized (newReferences) {
354: set.addAll(newReferences);
355: }
356: profile_addNewReferencesTo(start);
357: }
358:
359: private void profile_addNewReferencesTo(long start) {
360: if (logger.isDebugEnabled()) {
361: logger
362: .debug("addNewReferencesTo: "
363: + (System.currentTimeMillis() - start)
364: + " ms.");
365: }
366: }
367:
368: public PrettyPrinter prettyPrint(PrettyPrinter out) {
369: synchronized (newReferences) {
370: return out.println("newReferences: ").println(
371: newReferences);
372: }
373: }
374: }
375:
376: private interface ChangeCollector extends
377: ManagedObjectChangeListener, PrettyPrintable {
378: public void addNewReferencesTo(Set set);
379: }
380:
381: public void addNewReferencesTo(Set rescueIds) {
382: referenceCollector.addNewReferencesTo(rescueIds);
383: }
384:
385: public void start() {
386: this .started = true;
387: gcState.start();
388: }
389:
390: public void stop() {
391: this .started = false;
392: int count = 0;
393: while (!this .gcState.stopAndWait(5000) && (count < 6)) {
394: count++;
395: logger.warn("GC Thread did not stop");
396: }
397: }
398:
399: public boolean isStarted() {
400: return this .started;
401: }
402:
403: public void setState(StoppableThread st) {
404: this .gcState = st;
405: }
406:
407: private void fireGCCompleteEvent(GCStats gcStats, Set deleted) {
408: for (Iterator iter = eventListeners.iterator(); iter.hasNext();) {
409: try {
410: ObjectManagerEventListener listener = (ObjectManagerEventListener) iter
411: .next();
412: listener.garbageCollectionComplete(gcStats, deleted);
413: } catch (Exception e) {
414: if (logger.isDebugEnabled()) {
415: logger.debug(e);
416: } else {
417: logger
418: .warn("Exception in GCComplete event callback: "
419: + e.getMessage());
420: }
421: }
422: }
423: }
424:
425: public void addListener(ObjectManagerEventListener listener) {
426: eventListeners.add(listener);
427: }
428:
429: public GCStats[] getGarbageCollectorStats() {
430: return gcLogger.getGarbageCollectorStats();
431: }
432: }
|