1
2
3
4 package org.sourceforge.jemm.database.components;
5
6 import java.util.Iterator;
7 import java.util.Set;
8 import java.util.concurrent.CountDownLatch;
9 import java.util.concurrent.TimeUnit;
10
11 import org.apache.log4j.Logger;
12 import org.sourceforge.jemm.database.ObjectAccessor;
13 import org.sourceforge.jemm.database.components.interfaces.DBGarbageSweeper;
14 import org.sourceforge.jemm.database.components.interfaces.DBObjectHandler;
15 import org.sourceforge.jemm.database.components.se.StorageEngineGCIF;
16 import org.sourceforge.jemm.database.components.types.GCInfo;
17 import org.sourceforge.jemm.types.ID;
18 import org.sourceforge.jemm.util.LockManager;
19
20
21
22
23
24 public class DefaultDBGarbageSweeper implements DBGarbageSweeper {
25
26 private static final Logger LOG = Logger.getLogger(DefaultDBGarbageSweeper.class);
27
28 private final ObjectAccessor objectAccessor;
29 private final StorageEngineGCIF gcIF;
30 private final LockManager<ID> gcLock = new LockManager<ID>();
31
32 private GCMode gcMode;
33 private ObjectStatusListenerImpl osListener = new ObjectStatusListenerImpl();
34
35 private volatile boolean shutdown = false;
36
37 private final CountDownLatch shutdownLatch = new CountDownLatch(1);
38
39
40 private class ObjectStatusListenerImpl implements ObjectStatusListener {
41 @Override
42 public void initialisationComplete(ID id) {
43 try {
44 gcLock.acquire(id);
45 GCInfo info = gcIF.get(id);
46 info.initFinished();
47 gcIF.save(info);
48 }finally {
49 gcLock.release(id);
50 }
51 }
52
53 @Override
54 public void objectCreated(ID id) {
55 try {
56 gcLock.acquire(id);
57 GCInfo info = new GCInfo(id);
58 gcIF.save(info);
59 }finally {
60 gcLock.release(id);
61 }
62 }
63
64 @Override
65 public void rootRefAdded(ID id) {
66 try {
67 gcLock.acquire(id);
68 GCInfo info = gcIF.get(id);
69 info.incrementRootCount();
70 gcIF.save(info);
71 }finally {
72 gcLock.release(id);
73 }
74 }
75
76 @Override
77 public void rootRefCleared(ID id) {
78 try {
79 gcLock.acquire(id);
80 GCInfo info = gcIF.get(id);
81 info.decrementRootCount();
82 gcIF.save(info);
83 }finally {
84 gcLock.release(id);
85 }
86 }
87
88 @Override
89 public void clientRefAdded(ID id) {
90 try {
91 gcLock.acquire(id);
92 GCInfo info = gcIF.get(id);
93 info.incrementClientRefCount();
94 gcIF.save(info);
95 }finally {
96 gcLock.release(id);
97 }
98 }
99
100 @Override
101 public void clientRefCleared(ID id) {
102 try {
103 gcLock.acquire(id);
104 GCInfo info = gcIF.get(id);
105 info.decrementClientRefCount();
106 gcIF.save(info);
107 }finally {
108 gcLock.release(id);
109 }
110 }
111 }
112
113 private class Sweeper implements Runnable {
114
115 @Override
116 public void run() {
117 boolean done = false;
118 while(!done) {
119 if(!shutdown)
120 pause(100);
121
122 if(shutdown) {
123 shutdownLatch.countDown();
124 done = true;
125 } else {
126 try {
127 internalRunGCCycle();
128 }catch(Exception e) {
129 LOG.error("Error generated whilst performing GC",e);
130 }
131 }
132 }
133 }
134
135 private void pause(long time) {
136 try {
137 Thread.sleep(time);
138 } catch (InterruptedException e) {
139 e.printStackTrace();
140 }
141 }
142 }
143
144 public DefaultDBGarbageSweeper(StorageEngine storageEngine,DBObjectHandler objectHandler,GCMode gcMode) {
145 this.gcIF = storageEngine.getGCIF();
146 this.objectAccessor = objectHandler;
147 this.gcMode = gcMode;
148 if(gcMode == GCMode.AUTO) {
149 Thread thread = new Thread(new Sweeper());
150 thread.setDaemon(true);
151 thread.start();
152 }
153 }
154
155 private int internalRunGCCycle() {
156 gcInitialise();
157 boolean modified;
158 do {
159 modified = gcVisitPass();
160 }while(modified);
161
162 int cleaned = gcCleanup();
163 return cleaned;
164 }
165
166 private int gcCleanup() {
167 int count = 0;
168 for (Iterator<ID> itr = objectAccessor.idIterator(); itr.hasNext();) {
169 ID id = itr.next();
170 try {
171 gcLock.acquire(id);
172 GCInfo info = gcIF.get(id);
173 if(!info.isLive()) {
174 objectAccessor.removeObject(id);
175 gcIF.remove(id);
176 count++;
177 }
178 }finally {
179 gcLock.release(id);
180 }
181 }
182
183 return count;
184 }
185
186 private void gcInitialise() {
187 for (Iterator<ID> itr = objectAccessor.idIterator(); itr.hasNext();) {
188 ID id = itr.next();
189
190 try {
191 gcLock.acquire(id);
192 GCInfo info = gcIF.get(id);
193 if(info.gcInitialise())
194 gcIF.save(info);
195 }finally {
196 gcLock.release(id);
197 }
198 }
199 }
200
201 private boolean gcVisitPass() {
202 boolean changed = false;
203 for (Iterator<ID> itr = objectAccessor.idIterator(); itr.hasNext();) {
204 ID id = itr.next();
205 boolean objChanged = visitObject(id);
206
207 if(objChanged) {
208 touchChildren(id);
209 changed = true;
210 }
211 }
212 return changed;
213 }
214
215 private boolean visitObject(ID id) {
216 boolean objChanged = false;
217 try {
218 gcLock.acquire(id);
219 GCInfo info = gcIF.get(id);
220 if(info.getGCStatus() == GCStatus.GREY) {
221 info.setGCStatus(GCStatus.BLACK);
222 gcIF.save(info);
223 objChanged = true;
224 }
225
226 }finally {
227 gcLock.release(id);
228 }
229 return objChanged;
230 }
231
232 private void touchObect(ID id) {
233 try {
234 gcLock.acquire(id);
235 GCInfo info = gcIF.get(id);
236 boolean changed = info.touch();
237 if(changed)
238 gcIF.save(info);
239 } finally {
240 gcLock.release(id);
241 }
242 }
243
244 private void touchChildren(ID id) {
245 Set<ID> childrenIds = objectAccessor.getObjectChildren(id);
246
247 if(childrenIds.size() > 0)
248 for (ID childId : childrenIds)
249 touchObect(childId);
250 }
251
252 public void runGCCycle() {
253 if(gcMode == GCMode.AUTO)
254 throw new IllegalStateException("Cannot manually trigger MemoryStore gc whilst running in auto mode");
255
256 internalRunGCCycle();
257 }
258
259 public ObjectStatusListener getObjectStatusListener() {
260 return osListener;
261 }
262
263 @Override
264 public void shutdown() {
265 shutdown = true;
266 if(gcMode == GCMode.AUTO)
267 try {
268 if(!shutdownLatch.await(5000,TimeUnit.MILLISECONDS))
269 LOG.error("Timeout whilst waiting for GC shutdown");
270 } catch (InterruptedException e) {
271 LOG.error("InterruptedException thrown whilst waiting for shutdown",e);
272 }
273 }
274 }