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 8641 2012-09-27 06:45:17Z 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( 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( 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( 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( 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( 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 * 2 ), 337 new Long( available * 2 ) ); 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( 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 * 4 ), 381 new Long( available * 4 ) ); 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( int requested ) throws OutOfMemoryError 409 { 410 if ( requested < 0 ) 411 { 412 throw new IllegalArgumentException( Integer.toString( requested ) ); 413 } 414 415 double[] ret = null; 416 int retries = this.getMaximumRetries(); 417 418 do 419 { 420 final long available = this.getAvailableDoubles(); 421 422 if ( available < requested ) 423 { 424 this.logOutOfMemoryWarning( new Long( requested * 8 ), 425 new Long( available * 8 ) ); 426 427 this.forceGarbageCollection( 428 this.getMaximumRetries() - retries ); 429 430 } 431 else 432 { 433 ret = new double[ requested ]; 434 } 435 436 } 437 while ( ret == null && retries-- > 0 ); 438 439 if ( ret == null ) 440 { 441 throw new OutOfMemoryError(); 442 } 443 444 return ret; 445 } 446 447 public long getAvailableBooleans() 448 { 449 return this.getAvailableBytes(); 450 } 451 452 public boolean[] allocateBoolean( int requested ) throws OutOfMemoryError 453 { 454 if ( requested < 0 ) 455 { 456 throw new IllegalArgumentException( Integer.toString( requested ) ); 457 } 458 459 boolean[] ret = null; 460 int retries = this.getMaximumRetries(); 461 462 do 463 { 464 final long available = this.getAvailableBooleans(); 465 466 if ( available < requested ) 467 { 468 this.logOutOfMemoryWarning( new Long( requested ), 469 new Long( available ) ); 470 471 this.forceGarbageCollection( 472 this.getMaximumRetries() - retries ); 473 474 } 475 else 476 { 477 ret = new boolean[ requested ]; 478 } 479 480 } 481 while ( ret == null && retries-- > 0 ); 482 483 if ( ret == null ) 484 { 485 throw new OutOfMemoryError(); 486 } 487 488 return ret; 489 } 490 491 //-----------------------------------------------------------MemoryManager-- 492 //--DefaultMemoryManager---------------------------------------------------- 493 494 /** The maximum percent of memory for use by the implementation. */ 495 private Integer maximumPercent; 496 497 /** The number of retries used when trying to free memory. */ 498 private Integer maximumRetries; 499 500 /** 501 * Creates a new {@code DefaultMemoryManager} instance taking the maximum 502 * percent of memory for use by the implementation and the number of retries 503 * used when trying to free memory. 504 * 505 * @param maximumPercent the maximum percent of memory for use by the 506 * implementation. 507 * @param maximumRetries the number of retries used when trying to free 508 * memory. 509 */ 510 public DefaultMemoryManager( final int maximumPercent, 511 final int maximumRetries ) 512 { 513 if ( maximumPercent >= 0 && maximumPercent <= 100 ) 514 { 515 this.maximumPercent = new Integer( maximumPercent ); 516 } 517 if ( maximumRetries > 0 ) 518 { 519 this.maximumRetries = new Integer( maximumRetries ); 520 } 521 } 522 523 /** 524 * Gets the value of property {@code maximumRetries}. 525 * 526 * @return the number of retries when trying to free memory. 527 */ 528 private int getMaximumRetries() 529 { 530 if ( this.maximumRetries == null ) 531 { 532 this.maximumRetries = this.getDefaultMaximumRetries(); 533 } 534 535 return this.maximumRetries.intValue(); 536 } 537 538 /** 539 * Gets the value of property {@code maximumPercent}. 540 * 541 * @return the maximum percent of memory for use by the implementation. 542 */ 543 private int getMaximumPercent() 544 { 545 if ( this.maximumPercent == null ) 546 { 547 this.maximumPercent = this.getDefaultMaximumPercent(); 548 } 549 550 return this.maximumPercent.intValue(); 551 } 552 553 /** 554 * Logs a warning message for out of memory conditions. 555 * 556 * @param requestedByte number of byte requested to be allocated. 557 * @param availableByte number of byte available when {@code requestedByte} 558 * were requested. 559 */ 560 private void logOutOfMemoryWarning( final Long requestedByte, 561 final Long availableByte ) 562 { 563 this.getLogger().warn( this.getOutOfMemoryWarningMessage( 564 this.getLocale(), availableByte, requestedByte ) ); 565 566 } 567 568 /** 569 * Forces garbage collection and logs a warning message. 570 * 571 * @param repetition number of times garbage collection was already forced. 572 * 573 * @see System#gc() 574 */ 575 private void forceGarbageCollection( final int repetition ) 576 { 577 this.getLogger().warn( this.getForcingGarbageCollectionMessage( 578 this.getLocale(), new Integer( repetition ) ) ); 579 580 System.gc(); 581 } 582 583 //----------------------------------------------------DefaultMemoryManager-- 584 //--Messages---------------------------------------------------------------- 585 586// <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausMessages 587 // This section is managed by jdtaus-container-mojo. 588 589 /** 590 * Gets the text of message <code>outOfMemoryWarning</code>. 591 * <blockquote><pre>Wenig Hauptspeicher (verfügbar: {1,number}, benötigt {0,number}).</pre></blockquote> 592 * <blockquote><pre>Memory low (needed {0,number}, available {1,number}).</pre></blockquote> 593 * 594 * @param locale The locale of the message instance to return. 595 * @param neededMemory Needed number of bytes. 596 * @param availableMemory Available bytes. 597 * 598 * @return Out of memory warning. 599 */ 600 private String getOutOfMemoryWarningMessage( final Locale locale, 601 final java.lang.Number neededMemory, 602 final java.lang.Number availableMemory ) 603 { 604 return ContainerFactory.getContainer(). 605 getMessage( this, "outOfMemoryWarning", locale, 606 new Object[] 607 { 608 neededMemory, 609 availableMemory 610 }); 611 612 } 613 614 /** 615 * Gets the text of message <code>forcingGarbageCollection</code>. 616 * <blockquote><pre>Speicherbereinigung erzwungen ({0,number}).</pre></blockquote> 617 * <blockquote><pre>Forcing garbage collection ({0,number}).</pre></blockquote> 618 * 619 * @param locale The locale of the message instance to return. 620 * @param retry Number of currently forced garbage collections. 621 * 622 * @return Information about a forced garbage collection. 623 */ 624 private String getForcingGarbageCollectionMessage( final Locale locale, 625 final java.lang.Number retry ) 626 { 627 return ContainerFactory.getContainer(). 628 getMessage( this, "forcingGarbageCollection", locale, 629 new Object[] 630 { 631 retry 632 }); 633 634 } 635 636// </editor-fold>//GEN-END:jdtausMessages 637 638 //----------------------------------------------------------------Messages-- 639}