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}