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-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42 *
43 * $Id: GarbageCollectionService.java,v 1.14 2003/08/17 01:32:23 tanderson Exp $
44 *
45 * Date Author Changes
46 * 08/29/2001 jima Created
47 */
48 package org.exolab.jms.gc;
49
50 import java.util.LinkedList;
51
52 import org.apache.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54
55 import org.exolab.core.foundation.HandleIfc;
56 import org.exolab.core.service.BasicService;
57 import org.exolab.core.service.ServiceException;
58 import org.exolab.core.service.ServiceState;
59 import org.exolab.jms.config.Configuration;
60 import org.exolab.jms.config.ConfigurationManager;
61 import org.exolab.jms.config.GarbageCollectionConfiguration;
62 import org.exolab.jms.events.BasicEventManager;
63 import org.exolab.jms.events.Event;
64 import org.exolab.jms.events.EventHandler;
65 import org.exolab.jms.events.IllegalEventDefinedException;
66
67
68 /***
69 * The garbage collection service is responsible for managing all transient
70 * garbage collection for OpenJMS, which includes messages, destinations,
71 * endpoints etc. It does not deal with persistent data, which is handled
72 * through the database service. Other services or managers can register
73 * themselves with GarbageCollectionService if they implement the
74 * {@link GarbageCollectable} interface.
75 * <p>
76 * Gargabe collection will be initiated when the amount of free memory falls
77 * below a low water mark, which is calculated as a percentage of total memory.
78 * By default garbage collection will run when free memory falls below 20%
79 * of total memory, this can be changed through the configuration file.
80 * <p>
81 * The service will check the memory usage every 30 seconds by default. but
82 * this can also be modified through the configuration file.
83 * <p>
84 * In addition the garbage collection service can also be configured to
85 * execute at regular intervals regardless the amount of residual free memory.
86 * This option can be employed to ease the burden of performing wholesale
87 * garbage collection when memory falls below the low water mark threshold. The
88 * default value for this is 300 seconds. Setting this value to 0 will disable
89 * this capability.
90 * <p>
91 * This service makes use of the {@link BasicEventManager} to register events
92 * for garbage collection.
93 *
94 * @version $Revision: 1.14 $ $Date: 2003/08/17 01:32:23 $
95 * @author <a href="mailto:jima@intalio.com">Jim Alateras</a>
96 */
97 public class GarbageCollectionService
98 extends BasicService
99 implements EventHandler {
100
101 /***
102 * The name of the service
103 */
104 private final static String GC_SERVICE_NAME = "GCCollectionService";
105
106 /***
107 * This is the value of the trnasient garbage collection event that
108 * is used to register with the {@link BasicEventManager}. When this event
109 * is received the memory utilization is checked to determine whether
110 * we need to perform some garbage collection.
111 */
112 private final static int CHECK_FREE_MEMORY_EVENT = 1;
113
114 /***
115 * This event is used to unconditionally trigger garbage collection.
116 */
117 private final static int GARBAGE_COLLECT_EVENT = 2;
118
119 /***
120 * Maintains a singleton instance of the gc service
121 */
122 private static GarbageCollectionService _instance = null;
123
124 /***
125 * Used to synchronize the creation of the transaction manager
126 */
127 private static final Object _creator = new Object();
128
129 /***
130 * The default low water threshold value before GC is initiated.
131 * This is specified as a percentage with valid values ranging from
132 * 10-50.
133 */
134 private int _gcLowWaterThreshold = 20;
135
136 /***
137 * The default interval, in seconds, that memory is checked for
138 * the low water threshold. The default is 30 seconds.
139 */
140 private int _memoryCheckInterval = 30 * 1000;
141
142 /***
143 * The default interval, in seconds, between successive executions
144 * of the garbage collector. This will execute regardless the amount
145 * of free memory left in the VM.
146 */
147 private int _gcInterval = 300 * 1000;
148
149 /***
150 * This is the priority of that the GC thread uses to collect garbage.
151 * Changing it effects how aggressive GC is performed. The default value
152 * is 5.
153 */
154 private int _gcThreadPriority = 5;
155
156 /***
157 * This is used to serialize access to the _collectingGarbage flag
158 */
159 private final Object _gcGuard = new Object();
160
161 /***
162 * This flag indicates whether garabage collection is in progress
163 */
164 private boolean _collectingGarbage = false;
165
166 /***
167 * Maintains a list of all GarbageCollectable instances
168 */
169 private LinkedList _gcList = new LinkedList();
170
171 /***
172 * The logger
173 */
174 private static final Log _log =
175 LogFactory.getLog(GarbageCollectionService.class);
176
177
178 /***
179 * Return the singleton instance of the GarbageCollectionService
180 *
181 * @return GarbageCollectionService
182 * @throws GarbageCollectionServiceException
183 */
184 public static GarbageCollectionService instance()
185 throws GarbageCollectionServiceException {
186 if (_instance == null) {
187 synchronized (_creator) {
188 // we need to check again if multiple threads
189 // have blocked on the creation of the singleton
190 if (_instance == null) {
191 _instance = new GarbageCollectionService();
192 }
193 }
194 }
195
196 return _instance;
197 }
198
199 /***
200 * Create an instance of a garbage collection service. It uses the
201 * configuration manager to extract the service parameters.
202 * <p>
203 * It will throw a GarbageCollectionServiceException, if it cannot
204 * construct the service
205 *
206 * @throws GarbageCollectionServiceException
207 */
208 GarbageCollectionService()
209 throws GarbageCollectionServiceException {
210 super(GC_SERVICE_NAME);
211
212 // access the configuration file.
213 Configuration config = ConfigurationManager.getConfig();
214 GarbageCollectionConfiguration gc_config =
215 config.getGarbageCollectionConfiguration();
216
217 // read the value and ensure that it is within
218 // the specified limits
219 int low = gc_config.getLowWaterThreshold();
220 if (low < 10) {
221 low = 10;
222 }
223
224 if (low > 50) {
225 low = 50;
226 }
227 _gcLowWaterThreshold = low;
228
229 // read the memory check interval and fix it if it falls
230 // outside the constraints
231 int mem_interval = gc_config.getMemoryCheckInterval();
232 if ((mem_interval > 0) &&
233 (mem_interval < 5)) {
234 mem_interval = 5;
235 }
236 _memoryCheckInterval = mem_interval * 1000;
237
238 // read the gc interval, which is optional
239 int gc_interval = gc_config.getGarbageCollectionInterval();
240 if (gc_interval <= 0) {
241 gc_interval = 0;
242 }
243 _gcInterval = gc_interval * 1000;
244
245 // read the gc thread priority
246 int gc_priority = gc_config.getGarbageCollectionThreadPriority();
247 if (gc_priority < Thread.MIN_PRIORITY) {
248 gc_priority = Thread.MIN_PRIORITY;
249 }
250
251 if (gc_priority > Thread.MAX_PRIORITY) {
252 gc_priority = Thread.MAX_PRIORITY;
253 }
254 _gcThreadPriority = gc_priority;
255 }
256
257 /***
258 * Check whether the low water threshold has been reached.
259 *
260 * @return boolean - true if it has; false otherwise
261 */
262 public boolean belowLowWaterThreshold() {
263 boolean result = false;
264 long threshold = (long) ((Runtime.getRuntime().totalMemory() / 100) *
265 _gcLowWaterThreshold);
266 long free = Runtime.getRuntime().freeMemory();
267
268 if (_log.isDebugEnabled()) {
269 _log.debug("GC Threshold=" + threshold + " Free=" + free);
270 }
271 if (threshold > free) {
272 result = true;
273 }
274
275 return result;
276 }
277
278 /***
279 * Register an entity that wishes to participate in the garbage collection
280 * process. This entity will be added to the list of other registered
281 * entities and will be called when GC is triggered.
282 *
283 * @param entry - entry to add to list
284 */
285 public void register(GarbageCollectable entry) {
286 if (entry != null) {
287 synchronized (_gcList) {
288 _gcList.add(entry);
289 }
290 }
291 }
292
293 /***
294 * Unregister the specified entry from the list of garbge collectable
295 * entities
296 *
297 * @param entry - entry to remove
298 */
299 public void unregister(GarbageCollectable entry) {
300 if (entry != null) {
301 synchronized (_gcList) {
302 _gcList.remove(entry);
303 }
304 }
305 }
306
307 // override ServiceManager.run
308 public void run() {
309 // do nothing
310 }
311
312 // override ServiceManager.start
313 public void start()
314 throws ServiceException {
315
316 // register an event with the event manager
317 if (_memoryCheckInterval > 0) {
318 _log.info("Registering Garbage Collection every " +
319 _memoryCheckInterval + " for memory.");
320 registerEvent(CHECK_FREE_MEMORY_EVENT, _memoryCheckInterval);
321 }
322
323 // optionally start garbage collection
324 if (_gcInterval > 0) {
325 _log.info("Registering Garbage Collection every " +
326 _gcInterval + " for other resources.");
327 registerEvent(GARBAGE_COLLECT_EVENT, _gcInterval);
328 }
329
330 this.setState(ServiceState.RUNNING);
331 }
332
333 // override ServiceManager.stop
334 public void stop()
335 throws ServiceException {
336
337 this.setState(ServiceState.STOPPED);
338 }
339
340 // implementation of EventHandler.getHandle
341 public HandleIfc getHandle() {
342 return null;
343 }
344
345 // implementation of EventHandler.handleEvent
346 public void handleEvent(int event, Object callback, long time) {
347 boolean valid_event = false;
348
349 try {
350 if (event == CHECK_FREE_MEMORY_EVENT) {
351 valid_event = true;
352 try {
353 // collect garbage only below threshold
354 if (belowLowWaterThreshold()) {
355 _log.info("GC Collecting Garbage Free Heap below "
356 + _gcLowWaterThreshold);
357 collectGarbage(true);
358 }
359 } catch (Exception exception) {
360 _log.error("Error in GC Service [CHECK_FREE_MEMORY_EVENT]",
361 exception);
362 }
363 } else if (event == GARBAGE_COLLECT_EVENT) {
364 valid_event = true;
365 try {
366 // collect garbage now
367 collectGarbage(false);
368 } catch (Exception exception) {
369 _log.error("Error in GC Service [GARBAGE_COLLECT_EVENT]",
370 exception);
371 }
372 }
373 } finally {
374 if (valid_event) {
375 try {
376 registerEvent(event, ((Long) callback).longValue());
377 } catch (Exception exception) {
378 _log.error("Error in GC Service", exception);
379 }
380 }
381 }
382 }
383
384 /***
385 * Iterate through the list of registered {@link GarbageCollectables}
386 * and call collectGarbage on all of them.
387 *
388 * @param aggressive - true ofr aggressive garbage collection
389 */
390 private void collectGarbage(boolean aggressive) {
391 synchronized (_gcGuard) {
392 if (_collectingGarbage) {
393 // if we are in the middle of collecting garbage then
394 // we can ignore this request safely.
395 return;
396 } else {
397 _collectingGarbage = true;
398 }
399 }
400
401 // if we get this far then we are the only thread that will
402 // trigger garbage collection. First we must set the priority
403 // of this thread
404 int oldPriority = Thread.currentThread().getPriority();
405 try {
406 Thread.currentThread().setPriority(_gcThreadPriority);
407 Object[] list = _gcList.toArray();
408 for (int index = 0; index < list.length; index++) {
409 try {
410 GarbageCollectable collectable =
411 (GarbageCollectable) list[index];
412 collectable.collectGarbage(aggressive);
413 } catch (Exception exception) {
414 _log.error("Error while collecting garbage", exception);
415 }
416 }
417 } finally {
418 Thread.currentThread().setPriority(oldPriority);
419 }
420
421 // we have finished collecting garbage
422 synchronized (_gcGuard) {
423 _collectingGarbage = false;
424 }
425 }
426
427 /***
428 * Register the specified event with the corresponding time with the
429 * {@link BasicEventManager}. It will throw an exception if it cannot
430 * contact the event manager or register the event.
431 *
432 * @param event - the event to register
433 * @param time - the associated time
434 * @throws GarbageCollectionServiceException
435 */
436 private void registerEvent(int event, long time)
437 throws GarbageCollectionServiceException {
438 try {
439 BasicEventManager.instance().registerEventRelative(
440 new Event(event, this, new Long(time)), time);
441 } catch (IllegalEventDefinedException exception) {
442 // rethrow as a more relevant exception
443 throw new GarbageCollectionServiceException(
444 "Failed to registerEvent " + exception);
445 }
446 }
447
448 } //-- GarbageCollectionService
This page was automatically generated by Maven