001/* 002 * jDTAUS Core RI Memory Manager 003 * Copyright (C) 2005 Christian Schulte 004 * <cs@schulte.it> 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License, or any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 019 * 020 */ 021package org.jdtaus.core.lang.ri; 022 023import java.util.Locale; 024import org.jdtaus.core.container.ContainerFactory; 025import org.jdtaus.core.lang.spi.MemoryManager; 026import org.jdtaus.core.logging.spi.Logger; 027 028/** 029 * jDTAUS Core SPI {@code MemoryManager} reference implementation. 030 * <p>The reference implementation leaves a configurable amount of memory free 031 * and throws an {@code OutOfMemoryError} although the system has memory 032 * available. This free memory is then available for proper exception handling 033 * or for releasing resources in the system properly. It is configured with the 034 * two configuration properties {@code maximumPercent} and 035 * {@code maximumRetries}. Whenever an allocation would consume more than 036 * {@code maximumPercent} percent of all available memory this implementation 037 * tries to free memory by forcing garbage collection {@code maximumRetries} 038 * times before throwing an {@code OutOfMemoryError} exception. The default for 039 * property {@code maximumPercent} is {@code 98} and the default for property 040 * {@code maximumRetries} is {@code 1}.</p> 041 * 042 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 043 * @version $JDTAUS: DefaultMemoryManager.java 8743 2012-10-07 03:06:20Z schulte $ 044 * 045 * @see org.jdtaus.core.container.Container 046 */ 047public class DefaultMemoryManager implements MemoryManager 048{ 049 //--Constructors------------------------------------------------------------ 050 051// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausConstructors 052 // This section is managed by jdtaus-container-mojo. 053 054 /** Standard implementation constructor <code>org.jdtaus.core.lang.ri.DefaultMemoryManager</code>. */ 055 public DefaultMemoryManager() 056 { 057 super(); 058 } 059 060// </editor-fold>//GEN-END:jdtausConstructors 061 062 //------------------------------------------------------------Constructors-- 063 //--Dependencies------------------------------------------------------------ 064 065// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies 066 // This section is managed by jdtaus-container-mojo. 067 068 /** 069 * Gets the configured <code>Logger</code> implementation. 070 * 071 * @return The configured <code>Logger</code> implementation. 072 */ 073 private Logger getLogger() 074 { 075 return (Logger) ContainerFactory.getContainer(). 076 getDependency( this, "Logger" ); 077 078 } 079 080 /** 081 * Gets the configured <code>Locale</code> implementation. 082 * 083 * @return The configured <code>Locale</code> implementation. 084 */ 085 private Locale getLocale() 086 { 087 return (Locale) ContainerFactory.getContainer(). 088 getDependency( this, "Locale" ); 089 090 } 091 092// </editor-fold>//GEN-END:jdtausDependencies 093 094 //------------------------------------------------------------Dependencies-- 095 //--Properties-------------------------------------------------------------- 096 097// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties 098 // This section is managed by jdtaus-container-mojo. 099 100 /** 101 * Gets the value of property <code>defaultMaximumRetries</code>. 102 * 103 * @return Default number of retries when trying to free memory. 104 */ 105 private java.lang.Integer getDefaultMaximumRetries() 106 { 107 return (java.lang.Integer) ContainerFactory.getContainer(). 108 getProperty( this, "defaultMaximumRetries" ); 109 110 } 111 112 /** 113 * Gets the value of property <code>defaultMaximumPercent</code>. 114 * 115 * @return Default maximum percent of memory for use by the implementation. 116 */ 117 private java.lang.Integer getDefaultMaximumPercent() 118 { 119 return (java.lang.Integer) ContainerFactory.getContainer(). 120 getProperty( this, "defaultMaximumPercent" ); 121 122 } 123 124// </editor-fold>//GEN-END:jdtausProperties 125 126 //--------------------------------------------------------------Properties-- 127 //--MemoryManager----------------------------------------------------------- 128 129 public long getAllocatedPercent() 130 { 131 final Runtime rt = Runtime.getRuntime(); 132 final long max = rt.maxMemory(); 133 final long limit = ( max / 100L ) * this.getMaximumPercent(); 134 return rt.totalMemory() * ( 100L / limit ); 135 } 136 137 public long getAvailableBytes() 138 { 139 final Runtime rt = Runtime.getRuntime(); 140 final long all = rt.maxMemory() - rt.totalMemory() + rt.freeMemory(); 141 return ( all / 100L ) * this.getMaximumPercent(); 142 } 143 144 public byte[] allocateBytes( final int requested ) throws OutOfMemoryError 145 { 146 if ( requested < 0 ) 147 { 148 throw new IllegalArgumentException( Integer.toString( requested ) ); 149 } 150 151 byte[] ret = null; 152 int retries = this.getMaximumRetries(); 153 154 do 155 { 156 final long available = this.getAvailableBytes(); 157 158 if ( available < requested ) 159 { 160 this.logOutOfMemoryWarning( new Long( requested ), 161 new Long( available ) ); 162 163 this.forceGarbageCollection( 164 this.getMaximumRetries() - retries ); 165 166 } 167 else 168 { 169 ret = new byte[ requested ]; 170 } 171 172 } 173 while ( ret == null && retries-- > 0 ); 174 175 if ( ret == null ) 176 { 177 throw new OutOfMemoryError(); 178 } 179 180 return ret; 181 } 182 183 public long getAvailableShorts() 184 { 185 return this.getAvailableBytes() / 2; 186 } 187 188 public short[] allocateShorts( final int requested ) throws OutOfMemoryError 189 { 190 if ( requested < 0 ) 191 { 192 throw new IllegalArgumentException( Integer.toString( requested ) ); 193 } 194 195 short[] ret = null; 196 int retries = this.getMaximumRetries(); 197 198 do 199 { 200 final long available = this.getAvailableShorts(); 201 202 if ( available < requested ) 203 { 204 this.logOutOfMemoryWarning( new Long( requested * 2 ), 205 new Long( available * 2 ) ); 206 207 this.forceGarbageCollection( 208 this.getMaximumRetries() - retries ); 209 210 } 211 else 212 { 213 ret = new short[ requested ]; 214 } 215 216 } 217 while ( ret == null && retries-- > 0 ); 218 219 if ( ret == null ) 220 { 221 throw new OutOfMemoryError(); 222 } 223 224 return ret; 225 } 226 227 public long getAvailableIntegers() 228 { 229 return this.getAvailableBytes() / 4; 230 } 231 232 public int[] allocateIntegers( final int requested ) throws OutOfMemoryError 233 { 234 if ( requested < 0 ) 235 { 236 throw new IllegalArgumentException( Integer.toString( requested ) ); 237 } 238 239 int[] ret = null; 240 int retries = this.getMaximumRetries(); 241 242 do 243 { 244 final long available = this.getAvailableIntegers(); 245 if ( available < requested ) 246 { 247 this.logOutOfMemoryWarning( new Long( requested * 4 ), 248 new Long( available * 4 ) ); 249 250 this.forceGarbageCollection( 251 this.getMaximumRetries() - retries ); 252 253 } 254 else 255 { 256 ret = new int[ requested ]; 257 } 258 259 260 } 261 while ( ret == null && retries-- > 0 ); 262 263 if ( ret == null ) 264 { 265 throw new OutOfMemoryError(); 266 } 267 268 return ret; 269 } 270 271 public long getAvailableLongs() 272 { 273 return this.getAvailableBytes() / 8; 274 } 275 276 public long[] allocateLongs( final int requested ) throws OutOfMemoryError 277 { 278 if ( requested < 0 ) 279 { 280 throw new IllegalArgumentException( Integer.toString( requested ) ); 281 } 282 283 long[] ret = null; 284 int retries = this.getMaximumRetries(); 285 286 do 287 { 288 final long available = this.getAvailableLongs(); 289 290 if ( available < requested ) 291 { 292 this.logOutOfMemoryWarning( new Long( requested * 8 ), 293 new Long( available * 8 ) ); 294 295 this.forceGarbageCollection( 296 this.getMaximumRetries() - retries ); 297 298 } 299 else 300 { 301 ret = new long[ requested ]; 302 } 303 304 } 305 while ( ret == null && retries-- > 0 ); 306 307 if ( ret == null ) 308 { 309 throw new OutOfMemoryError(); 310 } 311 312 return ret; 313 } 314 315 public long getAvailableChars() 316 { 317 return this.getAvailableBytes() / 2; 318 } 319 320 public char[] allocateChars( final int requested ) throws OutOfMemoryError 321 { 322 if ( requested < 0 ) 323 { 324 throw new IllegalArgumentException( Integer.toString( requested ) ); 325 } 326 327 char[] ret = null; 328 int retries = this.getMaximumRetries(); 329 330 do 331 { 332 final long available = this.getAvailableChars(); 333 334 if ( available < requested ) 335 { 336 this.logOutOfMemoryWarning( new Long( requested * 2L ), 337 new Long( available * 2L ) ); 338 339 this.forceGarbageCollection( 340 this.getMaximumRetries() - retries ); 341 342 } 343 else 344 { 345 ret = new char[ requested ]; 346 } 347 348 } 349 while ( ret == null && retries-- > 0 ); 350 351 if ( ret == null ) 352 { 353 throw new OutOfMemoryError(); 354 } 355 356 return ret; 357 } 358 359 public long getAvailableFloats() 360 { 361 return this.getAvailableBytes() / 4; 362 } 363 364 public float[] allocateFloats( final int requested ) throws OutOfMemoryError 365 { 366 if ( requested < 0 ) 367 { 368 throw new IllegalArgumentException( Integer.toString( requested ) ); 369 } 370 371 float[] ret = null; 372 int retries = this.getMaximumRetries(); 373 374 do 375 { 376 final long available = this.getAvailableFloats(); 377 378 if ( available < requested ) 379 { 380 this.logOutOfMemoryWarning( new Long( requested * 4L ), 381 new Long( available * 4L ) ); 382 383 this.forceGarbageCollection( 384 this.getMaximumRetries() - retries ); 385 386 } 387 else 388 { 389 ret = new float[ requested ]; 390 } 391 392 } 393 while ( ret == null && retries-- > 0 ); 394 395 if ( ret == null ) 396 { 397 throw new OutOfMemoryError(); 398 } 399 400 return ret; 401 } 402 403 public long getAvailableDoubles() 404 { 405 return this.getAvailableBytes() / 8; 406 } 407 408 public double[] allocateDoubles( final int requested ) 409 throws OutOfMemoryError 410 { 411 if ( requested < 0 ) 412 { 413 throw new IllegalArgumentException( Integer.toString( requested ) ); 414 } 415 416 double[] ret = null; 417 int retries = this.getMaximumRetries(); 418 419 do 420 { 421 final long available = this.getAvailableDoubles(); 422 423 if ( available < requested ) 424 { 425 this.logOutOfMemoryWarning( new Long( requested * 8L ), 426 new Long( available * 8L ) ); 427 428 this.forceGarbageCollection( 429 this.getMaximumRetries() - retries ); 430 431 } 432 else 433 { 434 ret = new double[ requested ]; 435 } 436 437 } 438 while ( ret == null && retries-- > 0 ); 439 440 if ( ret == null ) 441 { 442 throw new OutOfMemoryError(); 443 } 444 445 return ret; 446 } 447 448 public long getAvailableBooleans() 449 { 450 return this.getAvailableBytes(); 451 } 452 453 public boolean[] allocateBoolean( final int requested ) 454 throws OutOfMemoryError 455 { 456 if ( requested < 0 ) 457 { 458 throw new IllegalArgumentException( Integer.toString( requested ) ); 459 } 460 461 boolean[] ret = null; 462 int retries = this.getMaximumRetries(); 463 464 do 465 { 466 final long available = this.getAvailableBooleans(); 467 468 if ( available < requested ) 469 { 470 this.logOutOfMemoryWarning( new Long( requested ), 471 new Long( available ) ); 472 473 this.forceGarbageCollection( 474 this.getMaximumRetries() - retries ); 475 476 } 477 else 478 { 479 ret = new boolean[ requested ]; 480 } 481 482 } 483 while ( ret == null && retries-- > 0 ); 484 485 if ( ret == null ) 486 { 487 throw new OutOfMemoryError(); 488 } 489 490 return ret; 491 } 492 493 //-----------------------------------------------------------MemoryManager-- 494 //--DefaultMemoryManager---------------------------------------------------- 495 496 /** The maximum percent of memory for use by the implementation. */ 497 private Integer maximumPercent; 498 499 /** The number of retries used when trying to free memory. */ 500 private Integer maximumRetries; 501 502 /** 503 * Creates a new {@code DefaultMemoryManager} instance taking the maximum 504 * percent of memory for use by the implementation and the number of retries 505 * used when trying to free memory. 506 * 507 * @param maximumPercent the maximum percent of memory for use by the 508 * implementation. 509 * @param maximumRetries the number of retries used when trying to free 510 * memory. 511 */ 512 public DefaultMemoryManager( final int maximumPercent, 513 final int maximumRetries ) 514 { 515 if ( maximumPercent >= 0 && maximumPercent <= 100 ) 516 { 517 this.maximumPercent = new Integer( maximumPercent ); 518 } 519 if ( maximumRetries > 0 ) 520 { 521 this.maximumRetries = new Integer( maximumRetries ); 522 } 523 } 524 525 /** 526 * Gets the value of property {@code maximumRetries}. 527 * 528 * @return the number of retries when trying to free memory. 529 */ 530 private int getMaximumRetries() 531 { 532 if ( this.maximumRetries == null ) 533 { 534 this.maximumRetries = this.getDefaultMaximumRetries(); 535 } 536 537 return this.maximumRetries.intValue(); 538 } 539 540 /** 541 * Gets the value of property {@code maximumPercent}. 542 * 543 * @return the maximum percent of memory for use by the implementation. 544 */ 545 private int getMaximumPercent() 546 { 547 if ( this.maximumPercent == null ) 548 { 549 this.maximumPercent = this.getDefaultMaximumPercent(); 550 } 551 552 return this.maximumPercent.intValue(); 553 } 554 555 /** 556 * Logs a warning message for out of memory conditions. 557 * 558 * @param requestedByte number of byte requested to be allocated. 559 * @param availableByte number of byte available when {@code requestedByte} 560 * were requested. 561 */ 562 private void logOutOfMemoryWarning( final Long requestedByte, 563 final Long availableByte ) 564 { 565 this.getLogger().warn( this.getOutOfMemoryWarningMessage( 566 this.getLocale(), availableByte, requestedByte ) ); 567 568 } 569 570 /** 571 * Forces garbage collection and logs a warning message. 572 * 573 * @param repetition number of times garbage collection was already forced. 574 * 575 * @see System#gc() 576 */ 577 private void forceGarbageCollection( final int repetition ) 578 { 579 this.getLogger().warn( this.getForcingGarbageCollectionMessage( 580 this.getLocale(), new Integer( repetition ) ) ); 581 582 System.gc(); 583 } 584 585 //----------------------------------------------------DefaultMemoryManager-- 586 //--Messages---------------------------------------------------------------- 587 588// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages 589 // This section is managed by jdtaus-container-mojo. 590 591 /** 592 * Gets the text of message <code>outOfMemoryWarning</code>. 593 * <blockquote><pre>Wenig Hauptspeicher (verfügbar: {1,number}, benötigt {0,number}).</pre></blockquote> 594 * <blockquote><pre>Memory low (needed {0,number}, available {1,number}).</pre></blockquote> 595 * 596 * @param locale The locale of the message instance to return. 597 * @param neededMemory Needed number of bytes. 598 * @param availableMemory Available bytes. 599 * 600 * @return Out of memory warning. 601 */ 602 private String getOutOfMemoryWarningMessage( final Locale locale, 603 final java.lang.Number neededMemory, 604 final java.lang.Number availableMemory ) 605 { 606 return ContainerFactory.getContainer(). 607 getMessage( this, "outOfMemoryWarning", locale, 608 new Object[] 609 { 610 neededMemory, 611 availableMemory 612 }); 613 614 } 615 616 /** 617 * Gets the text of message <code>forcingGarbageCollection</code>. 618 * <blockquote><pre>Speicherbereinigung erzwungen ({0,number}).</pre></blockquote> 619 * <blockquote><pre>Forcing garbage collection ({0,number}).</pre></blockquote> 620 * 621 * @param locale The locale of the message instance to return. 622 * @param retry Number of currently forced garbage collections. 623 * 624 * @return Information about a forced garbage collection. 625 */ 626 private String getForcingGarbageCollectionMessage( final Locale locale, 627 final java.lang.Number retry ) 628 { 629 return ContainerFactory.getContainer(). 630 getMessage( this, "forcingGarbageCollection", locale, 631 new Object[] 632 { 633 retry 634 }); 635 636 } 637 638// </editor-fold>//GEN-END:jdtausMessages 639 640 //----------------------------------------------------------------Messages-- 641}