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}