View Javadoc

1   /*
2    *  jDTAUS Core RI Memory Manager
3    *  Copyright (C) 2005 Christian Schulte
4    *  <cs@schulte.it>
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19   *
20   */
21  package org.jdtaus.core.lang.ri;
22  
23  import java.util.Locale;
24  import org.jdtaus.core.container.ContainerFactory;
25  import org.jdtaus.core.lang.spi.MemoryManager;
26  import org.jdtaus.core.logging.spi.Logger;
27  
28  /**
29   * jDTAUS Core SPI {@code MemoryManager} reference implementation.
30   * <p>The reference implementation leaves a configurable amount of memory free
31   * and throws an {@code OutOfMemoryError} although the system has memory
32   * available. This free memory is then available for proper exception handling
33   * or for releasing resources in the system properly. It is configured with the
34   * two configuration properties {@code maximumPercent} and
35   * {@code maximumRetries}. Whenever an allocation would consume more than
36   * {@code maximumPercent} percent of all available memory this implementation
37   * tries to free memory by forcing garbage collection {@code maximumRetries}
38   * times before throwing an {@code OutOfMemoryError} exception. The default for
39   * property {@code maximumPercent} is {@code 98} and the default for property
40   * {@code maximumRetries} is {@code 1}.</p>
41   *
42   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
43   * @version $JDTAUS: DefaultMemoryManager.java 8641 2012-09-27 06:45:17Z schulte $
44   *
45   * @see org.jdtaus.core.container.Container
46   */
47  public class DefaultMemoryManager implements MemoryManager
48  {
49      //--Constructors------------------------------------------------------------
50  
51  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausConstructors
52      // This section is managed by jdtaus-container-mojo.
53  
54      /** Standard implementation constructor <code>org.jdtaus.core.lang.ri.DefaultMemoryManager</code>. */
55      public DefaultMemoryManager()
56      {
57          super();
58      }
59  
60  // </editor-fold>//GEN-END:jdtausConstructors
61  
62      //------------------------------------------------------------Constructors--
63      //--Dependencies------------------------------------------------------------
64  
65  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausDependencies
66      // This section is managed by jdtaus-container-mojo.
67  
68      /**
69       * Gets the configured <code>Logger</code> implementation.
70       *
71       * @return The configured <code>Logger</code> implementation.
72       */
73      private Logger getLogger()
74      {
75          return (Logger) ContainerFactory.getContainer().
76              getDependency( this, "Logger" );
77  
78      }
79  
80      /**
81       * Gets the configured <code>Locale</code> implementation.
82       *
83       * @return The configured <code>Locale</code> implementation.
84       */
85      private Locale getLocale()
86      {
87          return (Locale) ContainerFactory.getContainer().
88              getDependency( this, "Locale" );
89  
90      }
91  
92  // </editor-fold>//GEN-END:jdtausDependencies
93  
94      //------------------------------------------------------------Dependencies--
95      //--Properties--------------------------------------------------------------
96  
97  // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:jdtausProperties
98      // This section is managed by jdtaus-container-mojo.
99  
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 }