KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > exolab > jms > gc > GarbageCollectionService


1 /**
2  * Redistribution and use of this software and associated documentation
3  * ("Software"), with or without modification, are permitted provided
4  * that the following conditions are met:
5  *
6  * 1. Redistributions of source code must retain copyright
7  * statements and notices. Redistributions must also contain a
8  * copy of this document.
9  *
10  * 2. Redistributions in binary form must reproduce the
11  * above copyright notice, this list of conditions and the
12  * following disclaimer in the documentation and/or other
13  * materials provided with the distribution.
14  *
15  * 3. The name "Exolab" must not be used to endorse or promote
16  * products derived from this Software without prior written
17  * permission of Exoffice Technologies. For written permission,
18  * please contact info@exolab.org.
19  *
20  * 4. Products derived from this Software may not be called "Exolab"
21  * nor may "Exolab" appear in their names without prior written
22  * permission of Exoffice Technologies. Exolab is a registered
23  * trademark of Exoffice Technologies.
24  *
25  * 5. Due credit should be given to the Exolab Project
26  * (http://www.exolab.org/).
27  *
28  * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32  * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39  * OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * Copyright 2001-2004 (C) Exoffice Technologies Inc. All Rights Reserved.
42  *
43  * $Id: GarbageCollectionService.java,v 1.1 2004/11/26 01:50:42 tanderson Exp $
44  */

45 package org.exolab.jms.gc;
46
47 import java.util.LinkedList JavaDoc;
48
49 import org.apache.commons.logging.Log;
50 import org.apache.commons.logging.LogFactory;
51
52 import org.exolab.jms.config.Configuration;
53 import org.exolab.jms.config.ConfigurationManager;
54 import org.exolab.jms.config.GarbageCollectionConfiguration;
55 import org.exolab.jms.service.BasicService;
56 import org.exolab.jms.service.ServiceException;
57 import org.exolab.jms.service.ServiceState;
58 import org.exolab.jms.events.BasicEventManager;
59 import org.exolab.jms.events.Event;
60 import org.exolab.jms.events.EventHandler;
61 import org.exolab.jms.events.IllegalEventDefinedException;
62
63
64 /**
65  * The garbage collection service is responsible for managing all transient
66  * garbage collection for OpenJMS, which includes messages, destinations,
67  * endpoints etc. It does not deal with persistent data, which is handled
68  * through the database service. Other services or managers can register
69  * themselves with GarbageCollectionService if they implement the
70  * {@link GarbageCollectable} interface.
71  * <p>
72  * Gargabe collection will be initiated when the amount of free memory falls
73  * below a low water mark, which is calculated as a percentage of total memory.
74  * By default garbage collection will run when free memory falls below 20%
75  * of total memory, this can be changed through the configuration file.
76  * <p>
77  * The service will check the memory usage every 30 seconds by default. but
78  * this can also be modified through the configuration file.
79  * <p>
80  * In addition the garbage collection service can also be configured to
81  * execute at regular intervals regardless the amount of residual free memory.
82  * This option can be employed to ease the burden of performing wholesale
83  * garbage collection when memory falls below the low water mark threshold. The
84  * default value for this is 300 seconds. Setting this value to 0 will disable
85  * this capability.
86  * <p>
87  * This service makes use of the {@link BasicEventManager} to register events
88  * for garbage collection.
89  *
90  * @version $Revision: 1.1 $ $Date: 2004/11/26 01:50:42 $
91  * @author <a HREF="mailto:jima@intalio.com">Jim Alateras</a>
92  */

93 public class GarbageCollectionService
94     extends BasicService
95     implements EventHandler {
96
97     /**
98      * The name of the service
99      */

100     private final static String JavaDoc GC_SERVICE_NAME = "GCCollectionService";
101
102     /**
103      * This is the value of the trnasient garbage collection event that
104      * is used to register with the {@link BasicEventManager}. When this event
105      * is received the memory utilization is checked to determine whether
106      * we need to perform some garbage collection.
107      */

108     private final static int CHECK_FREE_MEMORY_EVENT = 1;
109
110     /**
111      * This event is used to unconditionally trigger garbage collection.
112      */

113     private final static int GARBAGE_COLLECT_EVENT = 2;
114
115     /**
116      * Maintains a singleton instance of the gc service
117      */

118     private static GarbageCollectionService _instance = null;
119
120     /**
121      * Used to synchronize the creation of the transaction manager
122      */

123     private static final Object JavaDoc _creator = new Object JavaDoc();
124
125     /**
126      * The default low water threshold value before GC is initiated.
127      * This is specified as a percentage with valid values ranging from
128      * 10-50.
129      */

130     private int _gcLowWaterThreshold = 20;
131
132     /**
133      * The default interval, in seconds, that memory is checked for
134      * the low water threshold. The default is 30 seconds.
135      */

136     private int _memoryCheckInterval = 30 * 1000;
137
138     /**
139      * The default interval, in seconds, between successive executions
140      * of the garbage collector. This will execute regardless the amount
141      * of free memory left in the VM.
142      */

143     private int _gcInterval = 300 * 1000;
144
145     /**
146      * This is the priority of that the GC thread uses to collect garbage.
147      * Changing it effects how aggressive GC is performed. The default value
148      * is 5.
149      */

150     private int _gcThreadPriority = 5;
151
152     /**
153      * This is used to serialize access to the _collectingGarbage flag
154      */

155     private final Object JavaDoc _gcGuard = new Object JavaDoc();
156
157     /**
158      * This flag indicates whether garabage collection is in progress
159      */

160     private boolean _collectingGarbage = false;
161
162     /**
163      * Maintains a list of all GarbageCollectable instances
164      */

165     private LinkedList JavaDoc _gcList = new LinkedList JavaDoc();
166
167     /**
168      * The logger
169      */

170     private static final Log _log =
171         LogFactory.getLog(GarbageCollectionService.class);
172
173
174     /**
175      * Return the singleton instance of the GarbageCollectionService
176      *
177      * @return GarbageCollectionService
178      * @throws GarbageCollectionServiceException
179      */

180     public static GarbageCollectionService instance()
181         throws GarbageCollectionServiceException {
182         if (_instance == null) {
183             synchronized (_creator) {
184                 // we need to check again if multiple threads
185
// have blocked on the creation of the singleton
186
if (_instance == null) {
187                     _instance = new GarbageCollectionService();
188                 }
189             }
190         }
191
192         return _instance;
193     }
194
195     /**
196      * Create an instance of a garbage collection service. It uses the
197      * configuration manager to extract the service parameters.
198      *
199      * @throws GarbageCollectionServiceException
200      */

201     GarbageCollectionService() {
202         super(GC_SERVICE_NAME);
203
204         // access the configuration file.
205
Configuration config = ConfigurationManager.getConfig();
206         GarbageCollectionConfiguration gc_config =
207             config.getGarbageCollectionConfiguration();
208
209         // read the value and ensure that it is within
210
// the specified limits
211
int low = gc_config.getLowWaterThreshold();
212         if (low < 10) {
213             low = 10;
214         }
215
216         if (low > 50) {
217             low = 50;
218         }
219         _gcLowWaterThreshold = low;
220
221         // read the memory check interval and fix it if it falls
222
// outside the constraints
223
int mem_interval = gc_config.getMemoryCheckInterval();
224         if ((mem_interval > 0) &&
225             (mem_interval < 5)) {
226             mem_interval = 5;
227         }
228         _memoryCheckInterval = mem_interval * 1000;
229
230         // read the gc interval, which is optional
231
int gc_interval = gc_config.getGarbageCollectionInterval();
232         if (gc_interval <= 0) {
233             gc_interval = 0;
234         }
235         _gcInterval = gc_interval * 1000;
236
237         // read the gc thread priority
238
int gc_priority = gc_config.getGarbageCollectionThreadPriority();
239         if (gc_priority < Thread.MIN_PRIORITY) {
240             gc_priority = Thread.MIN_PRIORITY;
241         }
242
243         if (gc_priority > Thread.MAX_PRIORITY) {
244             gc_priority = Thread.MAX_PRIORITY;
245         }
246         _gcThreadPriority = gc_priority;
247     }
248
249     /**
250      * Check whether the low water threshold has been reached.
251      *
252      * @return boolean - true if it has; false otherwise
253      */

254     public boolean belowLowWaterThreshold() {
255         boolean result = false;
256         long threshold = (long) ((Runtime.getRuntime().totalMemory() / 100) *
257             _gcLowWaterThreshold);
258         long free = Runtime.getRuntime().freeMemory();
259
260         if (_log.isDebugEnabled()) {
261             _log.debug("GC Threshold=" + threshold + " Free=" + free);
262         }
263         if (threshold > free) {
264             result = true;
265         }
266
267         return result;
268     }
269
270     /**
271      * Register an entity that wishes to participate in the garbage collection
272      * process. This entity will be added to the list of other registered
273      * entities and will be called when GC is triggered.
274      *
275      * @param entry - entry to add to list
276      */

277     public void register(GarbageCollectable entry) {
278         if (entry != null) {
279             synchronized (_gcList) {
280                 _gcList.add(entry);
281             }
282         }
283     }
284
285     /**
286      * Unregister the specified entry from the list of garbge collectable
287      * entities
288      *
289      * @param entry - entry to remove
290      */

291     public void unregister(GarbageCollectable entry) {
292         if (entry != null) {
293             synchronized (_gcList) {
294                 _gcList.remove(entry);
295             }
296         }
297     }
298
299     // override ServiceManager.run
300
public void run() {
301         // do nothing
302
}
303
304     // override ServiceManager.start
305
public void start()
306         throws ServiceException {
307
308         // register an event with the event manager
309
if (_memoryCheckInterval > 0) {
310             _log.info("Registering Garbage Collection every " +
311                 _memoryCheckInterval + " for memory.");
312             registerEvent(CHECK_FREE_MEMORY_EVENT, _memoryCheckInterval);
313         }
314
315         // optionally start garbage collection
316
if (_gcInterval > 0) {
317             _log.info("Registering Garbage Collection every " +
318                 _gcInterval + " for other resources.");
319             registerEvent(GARBAGE_COLLECT_EVENT, _gcInterval);
320         }
321
322         this.setState(ServiceState.RUNNING);
323     }
324
325     // override ServiceManager.stop
326
public void stop()
327         throws ServiceException {
328
329         this.setState(ServiceState.STOPPED);
330     }
331
332     // implementation of EventHandler.handleEvent
333
public void handleEvent(int event, Object JavaDoc callback, long time) {
334         boolean valid_event = false;
335
336         try {
337             if (event == CHECK_FREE_MEMORY_EVENT) {
338                 valid_event = true;
339                 try {
340                     // collect garbage only below threshold
341
if (belowLowWaterThreshold()) {
342                         _log.info("GC Collecting Garbage Free Heap below "
343                             + _gcLowWaterThreshold);
344                         collectGarbage(true);
345                     }
346                 } catch (Exception JavaDoc exception) {
347                     _log.error("Error in GC Service [CHECK_FREE_MEMORY_EVENT]",
348                         exception);
349                 }
350             } else if (event == GARBAGE_COLLECT_EVENT) {
351                 valid_event = true;
352                 try {
353                     // collect garbage now
354
collectGarbage(false);
355                 } catch (Exception JavaDoc exception) {
356                     _log.error("Error in GC Service [GARBAGE_COLLECT_EVENT]",
357                         exception);
358                 }
359             }
360         } finally {
361             if (valid_event) {
362                 try {
363                     registerEvent(event, ((Long JavaDoc) callback).longValue());
364                 } catch (Exception JavaDoc exception) {
365                     _log.error("Error in GC Service", exception);
366                 }
367             }
368         }
369     }
370
371     /**
372      * Iterate through the list of registered {@link GarbageCollectables}
373      * and call collectGarbage on all of them.
374      *
375      * @param aggressive - true ofr aggressive garbage collection
376      */

377     private void collectGarbage(boolean aggressive) {
378         synchronized (_gcGuard) {
379             if (_collectingGarbage) {
380                 // if we are in the middle of collecting garbage then
381
// we can ignore this request safely.
382
return;
383             } else {
384                 _collectingGarbage = true;
385             }
386         }
387
388         // if we get this far then we are the only thread that will
389
// trigger garbage collection. First we must set the priority
390
// of this thread
391
int oldPriority = Thread.currentThread().getPriority();
392         try {
393             Thread.currentThread().setPriority(_gcThreadPriority);
394             Object JavaDoc[] list = _gcList.toArray();
395             for (int index = 0; index < list.length; index++) {
396                 try {
397                     GarbageCollectable collectable =
398                         (GarbageCollectable) list[index];
399                     collectable.collectGarbage(aggressive);
400                 } catch (Exception JavaDoc exception) {
401                     _log.error("Error while collecting garbage", exception);
402                 }
403             }
404         } finally {
405             Thread.currentThread().setPriority(oldPriority);
406         }
407
408         // we have finished collecting garbage
409
synchronized (_gcGuard) {
410             _collectingGarbage = false;
411         }
412     }
413
414     /**
415      * Register the specified event with the corresponding time with the
416      * {@link BasicEventManager}. It will throw an exception if it cannot
417      * contact the event manager or register the event.
418      *
419      * @param event - the event to register
420      * @param time - the associated time
421      * @throws GarbageCollectionServiceException
422      */

423     private void registerEvent(int event, long time)
424         throws GarbageCollectionServiceException {
425         try {
426             BasicEventManager.instance().registerEventRelative(
427                 new Event(event, this, new Long JavaDoc(time)), time);
428         } catch (IllegalEventDefinedException exception) {
429             // rethrow as a more relevant exception
430
throw new GarbageCollectionServiceException(
431                 "Failed to registerEvent " + exception);
432         }
433     }
434
435 }
436
Popular Tags