// serverMemory.java // ------------------------------------------- // (C) 2005 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany // first published 22.09.2005 on http://yacy.net // // $LastChangedDate: 2008-12-04 13:54:16 +0100 (Do, 04 Dez 2008) $ // $LastChangedRevision: 5379 $ // $LastChangedBy: orbiter $ // // LICENSE // // 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 package de.anomic.kelondro.util; import de.anomic.tools.Formatter; /** * Use this to get information about memory usage or try to free some memory */ public class MemoryControl { private static final Runtime runtime = Runtime.getRuntime(); public static long maxMemory = runtime.maxMemory(); // this value does never change during runtime private static final Log log = new Log("MEMORY"); private static final long[] gcs = new long[5]; private static int gcs_pos = 0; private static long lastGC = 0l; /** * 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 */ public final synchronized static boolean gc(final int last, final String info) { // thq assert last >= 10000; // too many forced GCs will cause bad execution performance final long elapsed = System.currentTimeMillis() - lastGC; if (elapsed > last) { final long before = free(); //System.out.println("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"); final long start = System.currentTimeMillis(); System.gc(); //System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ if you see this many times please report to forum"); lastGC = System.currentTimeMillis(); final long after = free(); gcs[gcs_pos++] = after - before; if (gcs_pos >= gcs.length) gcs_pos = 0; 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); return true; } if (log.isFinest()) log.logFinest("[gc] no execute, last run: " + (elapsed / 1000) + " seconds ago, call: " + info); return false; } /** * 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 0 if no * GC has been run yet */ public static long getAverageGCFree() { long x = 0; int y = 0; for (int i=0; iTries to free a specified amount of bytes.

*

* If the currently available memory is enough, the method returns true without * performing additional steps. If not, the behaviour depends on the parameter force. * If false, a Full GC is only performed if former GCs indicated that a GC should * provide enough free memory. If former GCs didn't but force is set to true * a Full GC is performed nevertheless. *

*

* Setting the force parameter to false doesn't necessarily mean, that no GC may be * performed by this method, if the currently available memory doesn't suffice! *

*

Be careful with this method as GCs should always be the last measure to take

* * @param size the requested amount of free memory in bytes * @param force specifies whether a GC should be run even in case former GCs didn't provide enough memory * @return whether enough memory could be freed (or is free) or not */ public static boolean request(final long size, final boolean force) { final long avg = getAverageGCFree(); if (avg >= size) return true; long avail = available(); if (avail >= size) return true; if (log.isFine()) { final String t = new Throwable("Stack trace").getStackTrace()[1].toString(); log.logFine(t + " requested " + (size >> 10) + " KB, got " + (avail >> 10) + " KB"); } if (force || avg == 0 || avg + avail >= size) { // this is only called if we expect that an allocation of bytes would cause the jvm to call the GC anyway final long memBefore = avail; boolean performedGC = gc(10000, "serverMemory.runGC(...)"); avail = available(); if (performedGC) { final long freed = avail - memBefore; log.logInfo("performed " + ((force) ? "explicit" : "necessary") + " GC, freed " + (freed >> 10) + " KB (requested/available/average: " + (size >> 10) + " / " + (avail >> 10) + " / " + (avg >> 10) + " KB)"); } return avail >= size; } else { if (log.isFine()) log.logFine("former GCs indicate to not be able to free enough memory (requested/available/average: " + (size >> 10) + " / " + (avail >> 10) + " / " + (avg >> 10) + " KB)"); return false; } } /** * memory that is currently bound in objects * @return used bytes */ public static long used() { return total() - free(); } /** * main * @param args */ public static void main(final String[] args) { // try this with a jvm 1.4.2 and with a jvm 1.5 and compare results final int mb = 1024 * 1024; System.out.println("vm: " + System.getProperty("java.vm.version")); System.out.println("computed max = " + (maxMemory / mb) + " mb"); final int alloc = 10000; final byte[][] x = new byte[100000][]; for (int i = 0; i < 100000; i++) { x[i] = new byte[alloc]; if (i % 100 == 0) System.out.println("used = " + (i * alloc / mb) + ", total = " + (total() / mb) + ", free = " + (free() / mb) + ", max = " + (maxMemory / mb) + ", avail = " + (available() / mb)); } } }