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