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 8743 2012-10-07 03:06:20Z 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( 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 }