1 17 package org.apache.excalibur.store.impl; 18 19 import java.util.ArrayList ; 20 import java.util.Iterator ; 21 22 import org.apache.avalon.framework.activity.Startable; 23 import org.apache.avalon.framework.logger.AbstractLogEnabled; 24 import org.apache.avalon.framework.parameters.ParameterException; 25 import org.apache.avalon.framework.parameters.Parameterizable; 26 import org.apache.avalon.framework.parameters.Parameters; 27 import org.apache.avalon.framework.thread.ThreadSafe; 28 import org.apache.excalibur.store.Store; 29 import org.apache.excalibur.store.StoreJanitor; 30 31 44 public class StoreJanitorImpl 45 extends AbstractLogEnabled 46 implements StoreJanitor, 47 Parameterizable, 48 ThreadSafe, 49 Runnable , 50 Startable 51 { 52 53 private boolean doRun = false; 54 55 private int minFreeMemory = -1; 57 private int maxHeapSize = -1; 58 private int threadInterval = -1; 59 private int minThreadInterval = 500; 60 private boolean adaptiveThreadInterval = false; 61 private int priority = -1; 62 private double fraction; 63 64 private Runtime jvm; 65 private ArrayList storelist; 66 private int index = -1; 67 68 protected boolean invokeGC = false; 69 70 89 public void parameterize(Parameters params) throws ParameterException 90 { 91 if (getLogger().isDebugEnabled()) 92 { 93 getLogger().debug("Configure StoreJanitorImpl"); 94 } 95 setJVM(Runtime.getRuntime()); 96 97 setMinFreeMemory(params.getParameterAsInteger("freememory", 1024 * 1024)); 98 setMaxHeapSize(params.getParameterAsInteger("heapsize", 60 * 1024 * 1024)); 99 setThreadInterval(params.getParameterAsInteger("cleanupthreadinterval", 10) * 1000); 101 setAdaptiveThreadInterval(params.getParameterAsBoolean("adaptivethreadinterval", false)); 102 setPriority(params.getParameterAsInteger("threadpriority", 103 Thread.currentThread().getPriority())); 104 int percent = params.getParameterAsInteger("percent_to_free", 10); 105 this.invokeGC = params.getParameterAsBoolean("invokegc", this.invokeGC); 106 107 if (getMinFreeMemory() < 1) 108 { 109 throw new ParameterException("StoreJanitorImpl freememory parameter has to be greater then 1"); 110 } 111 if (getMaxHeapSize() < 1) 112 { 113 throw new ParameterException("StoreJanitorImpl heapsize parameter has to be greater then 1"); 114 } 115 if (getThreadInterval() < 1) 116 { 117 throw new ParameterException("StoreJanitorImpl cleanupthreadinterval parameter has to be greater then 1"); 118 } 119 if (getPriority() < 1 || getPriority() > 10) 120 { 121 throw new ParameterException("StoreJanitorImpl threadpriority has to be between 1 and 10"); 122 } 123 if (percent > 100 && percent < 1) 124 { 125 throw new ParameterException("StoreJanitorImpl percent_to_free, has to be between 1 and 100"); 126 } 127 128 this.fraction = percent / 100.0D; 129 setStoreList(new ArrayList ()); 130 131 if ( getLogger().isDebugEnabled() ) 132 { 133 getLogger().debug("minimum free memory=" + this.getMinFreeMemory()); 134 getLogger().debug("heapsize=" + this.getMaxHeapSize()); 135 getLogger().debug("thread interval=" + this.getThreadInterval()); 136 getLogger().debug("priority=" + this.getPriority()); 137 getLogger().debug("percent=" + percent); 138 getLogger().debug("invoke gc=" + this.invokeGC); 139 } 140 } 141 142 public void start() 143 { 144 doRun = true; 145 Thread checker = new Thread (this); 146 if (getLogger().isDebugEnabled()) 147 { 148 getLogger().debug("Intializing checker thread"); 149 } 150 checker.setPriority(getPriority()); 151 checker.setDaemon(true); 152 checker.setName("checker"); 153 checker.start(); 154 } 155 156 public void stop() 157 { 158 doRun = false; 159 } 160 161 164 public void run() 165 { 166 boolean firstRun = true; 167 long inUse = memoryInUse(); long interval = Long.MAX_VALUE; long maxRateOfChange = 1; 171 while (doRun) { 172 if (getAdaptiveThreadInterval()) 173 { 174 long change = memoryInUse() - inUse; 176 long rateOfChange = longDiv(change * 1000, interval); if (maxRateOfChange < rateOfChange) 178 { 179 maxRateOfChange = (maxRateOfChange + rateOfChange) / 2; 180 } 181 if (getLogger().isDebugEnabled()) { 182 getLogger().debug("Waking after " + interval + "ms, in use change " 183 + change + "b to " + memoryInUse() + "b, rate " 184 + rateOfChange + "b/sec, max rate " + maxRateOfChange + "b/sec"); 185 } 186 } 187 188 if (memoryLow()) 190 { 191 if ( this.invokeGC ) 192 { 193 this.freePhysicalMemory(); 194 } 195 196 synchronized (this) 197 { 198 if (!this.invokeGC 199 || (memoryLow() && getStoreList().size() > 0)) 200 { 201 202 freeMemory(); 203 setIndex(getIndex() + 1); 204 } 205 } 206 } 207 208 if (getAdaptiveThreadInterval()) 209 { 210 interval = minTimeToFill(maxRateOfChange) * 1000 / 2; 212 if (interval > this.threadInterval) 213 { 214 interval = this.threadInterval; 215 } 216 else if (interval < this.minThreadInterval) 217 { 218 interval = this.minThreadInterval; 219 } 220 inUse = memoryInUse(); 221 } 222 else 223 { 224 interval = this.threadInterval; 225 } 226 if (getLogger().isDebugEnabled()) 227 { 228 getLogger().debug("Sleeping for " + interval + "ms"); 229 } 230 231 try 233 { 234 Thread.sleep(interval); 235 } 236 catch (InterruptedException ignore) {} 237 238 if (firstRun) 240 { 241 firstRun = false; 242 inUse = memoryInUse(); 243 } 244 } 245 } 246 247 252 private boolean memoryLow() 253 { 254 if (getLogger().isDebugEnabled()) 255 { 256 getLogger().debug("JVM Memory total: " + getJVM().totalMemory() 257 + ", free: " + getJVM().freeMemory()); 258 } 259 260 if ((getJVM().totalMemory() >= getMaxHeapSize()) 261 && (getJVM().freeMemory() < getMinFreeMemory())) 262 { 263 if (getLogger().isDebugEnabled()) 264 { 265 getLogger().debug("Memory is low!"); 266 } 267 return true; 268 } 269 else 270 { 271 return false; 272 } 273 } 274 275 280 private long memoryInUse() 281 { 282 return jvm.totalMemory() - jvm.freeMemory(); 283 } 284 285 292 private long minTimeToFill(long rate) 293 { 294 return longDiv(jvm.freeMemory(), rate); 295 } 296 297 private long longDiv(long top, long bottom) 298 { 299 try 300 { 301 return top / bottom; 302 } 303 catch (Exception e) 304 { 305 return top > 0 ? Long.MAX_VALUE : Long.MIN_VALUE; 306 } 307 } 308 309 314 public synchronized void register(Store store) 315 { 316 getStoreList().add(store); 317 if (getLogger().isDebugEnabled()) 318 { 319 getLogger().debug("Registered store instance " + store + ". Stores now: " 320 + getStoreList().size()); 321 } 322 } 323 324 329 public synchronized void unregister(Store store) 330 { 331 getStoreList().remove(store); 332 if (getLogger().isDebugEnabled()) 333 { 334 getLogger().debug("Unregistered store instance " + store + ". Stores now: " 335 + getStoreList().size()); 336 } 337 } 338 339 351 public Iterator iterator() 352 { 353 return getStoreList().iterator(); 354 } 355 356 359 private void freeMemory() 360 { 361 try 363 { 364 if (getIndex() < getStoreList().size()) 366 { 367 if (getIndex() == -1) 368 { 369 setIndex(0); 370 } 371 } 372 else 373 { 374 if (getLogger().isDebugEnabled()) 376 { 377 getLogger().debug("Restarting from the beginning"); 378 } 379 setIndex(0); 380 } 381 382 Store store = (Store)getStoreList().get(getIndex()); 384 int limit = calcToFree(store); 385 if (getLogger().isDebugEnabled()) 386 { 387 getLogger().debug("Freeing " + limit + " items from store N " + getIndex()); 388 } 389 for (int i=0; i < limit; i++) 390 { 391 try 392 { 393 store.free(); 394 } 395 catch (OutOfMemoryError e) 396 { 397 getLogger().error("OutOfMemoryError in freeMemory()"); 398 } 399 } 400 } 401 catch (Exception e) 402 { 403 getLogger().error("Error in freeMemory()", e); 404 } 405 catch (OutOfMemoryError e) 406 { 407 getLogger().error("OutOfMemoryError in freeMemory()"); 408 } 409 } 410 411 418 private int calcToFree(Store store) 419 { 420 int cnt = store.size(); 421 if (cnt < 0) 422 { 423 if ( getLogger().isDebugEnabled() ) 424 { 425 getLogger().debug("Unknown size of the store: " + store); 426 } 427 return 0; 428 } 429 final int res = (int)(cnt * fraction); 430 if ( getLogger().isDebugEnabled() ) 431 { 432 getLogger().debug("Calculating size for store " + store + " with size " + cnt + " : " + res); 433 } 434 return res; 435 } 436 437 440 private void freePhysicalMemory() 441 { 442 if (getLogger().isDebugEnabled()) 443 { 444 getLogger().debug("Invoking garbage collection. Memory total: " 445 + getJVM().totalMemory() + ", free: " 446 + getJVM().freeMemory()); 447 } 448 449 getJVM().runFinalization(); 450 getJVM().gc(); 451 452 if (getLogger().isDebugEnabled()) 453 { 454 getLogger().debug("Garbage collection complete. Memory total: " 455 + getJVM().totalMemory() + ", free: " 456 + getJVM().freeMemory()); 457 } 458 } 459 460 461 private int getMinFreeMemory() 462 { 463 return this.minFreeMemory; 464 } 465 466 private void setMinFreeMemory(int _freememory) 467 { 468 this.minFreeMemory = _freememory; 469 } 470 471 private int getMaxHeapSize() 472 { 473 return this.maxHeapSize; 474 } 475 476 private void setMaxHeapSize(int _heapsize) 477 { 478 this.maxHeapSize = _heapsize; 479 } 480 481 private int getPriority() 482 { 483 return this.priority; 484 } 485 486 private void setPriority(int _priority) 487 { 488 this.priority = _priority; 489 } 490 491 private int getThreadInterval() 492 { 493 return this.threadInterval; 494 } 495 496 private void setThreadInterval(int _threadInterval) 497 { 498 this.threadInterval = _threadInterval; 499 } 500 501 private boolean getAdaptiveThreadInterval() 502 { 503 return this.adaptiveThreadInterval; 504 } 505 506 private void setAdaptiveThreadInterval(boolean _adaptiveThreadInterval) 507 { 508 this.adaptiveThreadInterval = _adaptiveThreadInterval; 509 } 510 511 private Runtime getJVM() 512 { 513 return this.jvm; 514 } 515 516 private void setJVM(Runtime _jvm) 517 { 518 this.jvm = _jvm; 519 } 520 521 private ArrayList getStoreList() 522 { 523 return this.storelist; 524 } 525 526 private void setStoreList(ArrayList _storelist) 527 { 528 this.storelist = _storelist; 529 } 530 531 private void setIndex(int _index) 532 { 533 if (getLogger().isDebugEnabled()) 534 { 535 getLogger().debug("Setting index=" + _index); 536 } 537 this.index = _index; 538 } 539 540 private int getIndex() 541 { 542 return this.index; 543 } 544 } 545 | Popular Tags |