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 | } |