View Javadoc
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