2005-09-22 22:01:26 +02:00
// serverMemory.java
// -------------------------------------------
2007-05-16 19:45:39 +02:00
// (C) 2005 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
// first published 22.09.2005 on http://yacy.net
2005-09-22 22:01:26 +02:00
//
2009-01-30 15:48:11 +01:00
// $LastChangedDate: 2008-12-04 13:54:16 +0100 (Do, 04 Dez 2008) $
// $LastChangedRevision: 5379 $
// $LastChangedBy: orbiter $
2005-09-22 22:01:26 +02:00
//
2007-05-16 19:45:39 +02:00
// LICENSE
//
2005-09-22 22:01:26 +02:00
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2009-01-30 23:44:20 +01:00
package de.anomic.kelondro.util ;
2005-09-22 22:01:26 +02:00
2009-02-05 20:42:32 +01:00
import de.anomic.tools.Formatter ;
2007-06-05 13:37:19 +02:00
2008-03-02 16:42:50 +01:00
/ * *
* Use this to get information about memory usage or try to free some memory
* /
2009-01-30 16:33:00 +01:00
public class MemoryControl {
2005-09-22 22:01:26 +02:00
private static final Runtime runtime = Runtime . getRuntime ( ) ;
2009-01-31 00:33:47 +01:00
private static final Log log = new Log ( " MEMORY " ) ;
2005-09-22 22:01:26 +02:00
2007-06-05 13:37:19 +02:00
private static final long [ ] gcs = new long [ 5 ] ;
private static int gcs_pos = 0 ;
2007-11-02 15:55:46 +01:00
2008-05-03 11:06:00 +02:00
private static long lastGC = 0l ;
2007-11-02 15:55:46 +01:00
2008-03-02 16:42:50 +01:00
/ * *
* Runs the garbage collector if last garbage collection is more than last millis ago
* @param last time which must be passed since lased gc
* @param info additional info for log
* /
2008-08-02 14:12:04 +02:00
public final synchronized static void gc ( final int last , final String info ) { // thq
2009-02-16 00:35:59 +01:00
assert last > = 10000 ; // too many forced GCs will cause bad execution performance
2008-08-02 14:12:04 +02:00
final long elapsed = System . currentTimeMillis ( ) - lastGC ;
2007-11-02 15:55:46 +01:00
if ( elapsed > last ) {
2009-02-05 20:42:32 +01:00
final long before = free ( ) ;
2008-09-30 13:09:46 +02:00
//System.out.println("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv");
2009-02-05 20:42:32 +01:00
final long start = System . currentTimeMillis ( ) ;
2007-11-02 15:55:46 +01:00
System . gc ( ) ;
2008-09-30 13:09:46 +02:00
//System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ if you see this many times please report to forum");
2007-11-02 15:55:46 +01:00
lastGC = System . currentTimeMillis ( ) ;
2009-02-05 20:42:32 +01:00
final long after = free ( ) ;
if ( log . isFine ( ) ) log . logInfo ( " [gc] before: " + Formatter . bytesToString ( before ) +
" , after: " + Formatter . bytesToString ( after ) +
" , freed: " + Formatter . bytesToString ( after - before ) +
" , rt: " + ( lastGC - start ) + " ms, call: " + info ) ;
2007-11-02 15:55:46 +01:00
} else if ( log . isFine ( ) ) {
2008-04-08 16:44:39 +02:00
if ( log . isFinest ( ) ) log . logFinest ( " [gc] no execute, last run: " + ( elapsed / 1000 ) + " seconds ago, call: " + info ) ;
2007-11-02 15:55:46 +01:00
}
}
2008-03-02 16:42:50 +01:00
/ * *
* Tries to free count bytes
* @param count bytes
* @return the amount of freed bytes by a forced GC this method performes
* /
2007-06-13 17:03:56 +02:00
private static long runGC ( final boolean count ) {
2008-03-02 16:42:50 +01:00
final long memBefore = available ( ) ;
2009-02-16 00:35:59 +01:00
gc ( 10000 , " serverMemory.runGC(...) " ) ;
2008-03-02 16:42:50 +01:00
final long freed = available ( ) - memBefore ;
2007-06-13 17:03:56 +02:00
if ( count ) {
gcs [ gcs_pos ] = freed ;
gcs_pos = ( gcs_pos + 1 ) % gcs . length ;
}
return freed ;
2007-06-05 13:37:19 +02:00
}
/ * *
* This method calculates the average amount of bytes freed by the last GCs forced by this class
* @return the average amount of freed bytes of the last forced GCs or < code > 0 < / code > if no
* GC has been run yet
* /
public static long getAverageGCFree ( ) {
long x = 0 ;
int y = 0 ;
for ( int i = 0 ; i < gcs . length ; i + + )
if ( gcs [ i ] ! = 0 ) {
x + = gcs [ i ] ;
y + + ;
}
return ( y = = 0 ) ? 0 : x / y ;
}
2008-03-02 16:42:50 +01:00
/ * *
* memory that is free without increasing of total memory taken from os
* @return bytes
* /
public static final long free ( ) {
2005-09-22 22:01:26 +02:00
return runtime . freeMemory ( ) ;
}
2007-06-05 13:37:19 +02:00
/ * *
2008-03-02 16:42:50 +01:00
* memory that is available including increasing total memory up to maximum
* @return bytes
2007-06-05 13:37:19 +02:00
* /
2008-03-01 10:44:33 +01:00
public static final long available ( ) {
2008-03-02 16:42:50 +01:00
return max ( ) - total ( ) + free ( ) ;
2005-09-22 22:01:26 +02:00
}
2007-06-05 13:37:19 +02:00
/ * *
2008-03-02 16:42:50 +01:00
* maximum memory which the vm can use
* @return bytes
* /
public static final long max ( )
{
return runtime . maxMemory ( ) ;
}
/ * *
* currently allocated memory in the Java virtual machine ; may vary over time
* @return bytes
* /
public static final long total ( )
{
return runtime . totalMemory ( ) ;
}
/ * *
2007-06-05 13:37:19 +02:00
* < p > Tries to free a specified amount of bytes . < / p >
* < p >
* If the currently available memory is enough , the method returns < code > true < / code > without
* performing additional steps . If not , the behaviour depends on the parameter < code > force < / code > .
* If < code > false < / code > , a Full GC is only performed if former GCs indicated that a GC should
* provide enough free memory . If former GCs didn ' t but < code > force < / code > is set to < code > true < / code >
* a Full GC is performed nevertheless .
* < / p >
* < p >
* Setting the < code > force < / code > parameter to false doesn ' t necessarily mean , that no GC may be
* performed by this method , if the currently available memory doesn ' t suffice !
* < / p >
* < p > < em > Be careful with this method as GCs should always be the last measure to take < / em > < / p >
*
* @param size the requested amount of free memory in bytes
2007-06-17 21:32:38 +02:00
* @param force specifies whether a GC should be run even in case former GCs didn ' t provide enough memory
2007-06-05 13:37:19 +02:00
* @return whether enough memory could be freed ( or is free ) or not
* /
2007-06-13 17:03:56 +02:00
public static boolean request ( final long size , final boolean force ) {
2008-03-02 16:42:50 +01:00
long avail = available ( ) ;
2007-06-15 19:45:49 +02:00
if ( avail > = size ) return true ;
2007-06-13 17:03:56 +02:00
if ( log . isFine ( ) ) {
2008-08-02 14:12:04 +02:00
final String t = new Throwable ( " Stack trace " ) . getStackTrace ( ) [ 1 ] . toString ( ) ;
2007-06-17 21:32:38 +02:00
log . logFine ( t + " requested " + ( size > > 10 ) + " KB, got " + ( avail > > 10 ) + " KB " ) ;
2007-06-15 19:45:49 +02:00
}
2007-06-13 17:03:56 +02:00
final long avg = getAverageGCFree ( ) ;
2007-06-08 14:45:03 +02:00
if ( force | | avg = = 0 | | avg + avail > = size ) {
2007-06-15 19:45:49 +02:00
// this is only called if we expect that an allocation of <size> bytes would cause the jvm to call the GC anyway
2009-02-16 00:35:59 +01:00
final long freed = runGC ( false ) ;
2008-03-02 16:42:50 +01:00
avail = available ( ) ;
2007-06-17 21:32:38 +02:00
log . logInfo ( " performed " + ( ( force ) ? " explicit " : " necessary " ) + " GC, freed " + ( freed > > 10 )
2008-01-06 01:31:26 +01:00
+ " KB (requested/available/average: "
+ ( size > > 10 ) + " / " + ( avail > > 10 ) + " / " + ( avg > > 10 ) + " KB) " ) ;
2007-06-05 13:37:19 +02:00
return avail > = size ;
} else {
2007-06-13 17:03:56 +02:00
log . logInfo ( " former GCs indicate to not be able to free enough memory (requested/available/average: "
2007-06-17 21:32:38 +02:00
+ ( size > > 10 ) + " / " + ( avail > > 10 ) + " / " + ( avg > > 10 ) + " KB) " ) ;
2007-06-05 13:37:19 +02:00
return false ;
}
}
2008-03-02 16:42:50 +01:00
/ * *
* memory that is currently bound in objects
* @return used bytes
* /
2005-09-22 22:01:26 +02:00
public static long used ( ) {
2008-03-02 16:42:50 +01:00
return total ( ) - free ( ) ;
2005-09-22 22:01:26 +02:00
}
2009-02-05 20:42:32 +01:00
2008-03-02 16:42:50 +01:00
/ * *
* main
* @param args
* /
2008-08-02 14:12:04 +02:00
public static void main ( final String [ ] args ) {
2006-08-18 03:33:54 +02:00
// try this with a jvm 1.4.2 and with a jvm 1.5 and compare results
2008-08-02 14:12:04 +02:00
final int mb = 1024 * 1024 ;
2006-08-18 03:33:54 +02:00
System . out . println ( " vm: " + System . getProperty ( " java.vm.version " ) ) ;
2008-03-02 16:42:50 +01:00
System . out . println ( " computed max = " + ( max ( ) / mb ) + " mb " ) ;
2008-08-02 14:12:04 +02:00
final int alloc = 10000 ;
final byte [ ] [ ] x = new byte [ 100000 ] [ ] ;
2006-08-18 03:33:54 +02:00
for ( int i = 0 ; i < 100000 ; i + + ) {
x [ i ] = new byte [ alloc ] ;
if ( i % 100 = = 0 ) System . out . println ( " used = " + ( i * alloc / mb ) +
2008-03-02 16:42:50 +01:00
" , total = " + ( total ( ) / mb ) +
" , free = " + ( free ( ) / mb ) +
" , max = " + ( max ( ) / mb ) +
" , avail = " + ( available ( ) / mb ) ) ;
2006-08-18 03:33:54 +02:00
}
}
2005-09-22 22:01:26 +02:00
}