001: // serverMemory.java
002: // -------------------------------------------
003: // (C) 2005 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
004: // first published 22.09.2005 on http://yacy.net
005: //
006: // $LastChangedDate: 2008-01-06 00:31:26 +0000 (So, 06 Jan 2008) $
007: // $LastChangedRevision: 4301 $
008: // $LastChangedBy: orbiter $
009: //
010: // LICENSE
011: //
012: // This program is free software; you can redistribute it and/or modify
013: // it under the terms of the GNU General Public License as published by
014: // the Free Software Foundation; either version 2 of the License, or
015: // (at your option) any later version.
016: //
017: // This program is distributed in the hope that it will be useful,
018: // but WITHOUT ANY WARRANTY; without even the implied warranty of
019: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: // GNU General Public License for more details.
021: //
022: // You should have received a copy of the GNU General Public License
023: // along with this program; if not, write to the Free Software
024: // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
025:
026: package de.anomic.server;
027:
028: import de.anomic.server.logging.serverLog;
029: import de.anomic.tools.yFormatter;
030:
031: public class serverMemory {
032:
033: private static final Runtime runtime = Runtime.getRuntime();
034: private static final serverLog log = new serverLog("MEMORY");
035:
036: private static final long[] gcs = new long[5];
037: private static int gcs_pos = 0;
038:
039: private static long lastGC;
040:
041: public final synchronized static void gc(int last, String info) { // thq
042: long elapsed = System.currentTimeMillis() - lastGC;
043: if (elapsed > last) {
044: long free = free();
045: System.gc();
046: lastGC = System.currentTimeMillis();
047: if (log.isFine())
048: log.logInfo("[gc] before: " + bytesToString(free)
049: + ", after: " + bytesToString(free())
050: + ", call: " + info);
051: } else if (log.isFine()) {
052: log.logFinest("[gc] no execute, last run: "
053: + (elapsed / 1000) + " seconds ago, call: " + info);
054: }
055: }
056:
057: /** @return the amount of freed bytes by a forced GC this method performes */
058: private static long runGC(final boolean count) {
059: final long memnow = runtime.maxMemory() - runtime.totalMemory()
060: + runtime.freeMemory();
061: gc(1000, "serverMemory.runGC(...)");
062: final long freed = runtime.maxMemory() - runtime.totalMemory()
063: + runtime.freeMemory() - memnow;
064: if (count) {
065: gcs[gcs_pos] = freed;
066: gcs_pos = (gcs_pos + 1) % gcs.length;
067: }
068: return freed;
069: }
070:
071: /**
072: * This method calculates the average amount of bytes freed by the last GCs forced by this class
073: * @return the average amount of freed bytes of the last forced GCs or <code>0</code> if no
074: * GC has been run yet
075: */
076: public static long getAverageGCFree() {
077: long x = 0;
078: int y = 0;
079: for (int i = 0; i < gcs.length; i++)
080: if (gcs[i] != 0) {
081: x += gcs[i];
082: y++;
083: }
084: return (y == 0) ? 0 : x / y;
085: }
086:
087: public static long free() {
088: // memory that is free without increasing of total memory taken from os
089: return runtime.freeMemory();
090: }
091:
092: /**
093: * Tries to free a specified amount of bytes. If the currently available memory is enough, the
094: * method returns <code>true</code> without performing additional steps. Otherwise - if
095: * <code>gciffail</code> is set - a Full GC is run, if <code>gciffail</code> is set to
096: * <code>false</code> and not enough memory is available, this method returns <code>false</code>.
097: *
098: * @see serverMemory#request(long, boolean) for another implementation
099: * @param memory amount of bytes to be assured to be free
100: * @param gciffail if not enough memory is available, this parameter specifies whether to perform
101: * a Full GC to free enough RAM
102: * @return whether enough RAM is available
103: */
104: public static long available() {
105: // memory that is available including increasing total memory up to maximum
106: return runtime.maxMemory() - runtime.totalMemory()
107: + runtime.freeMemory();
108: }
109:
110: /**
111: * <p>Tries to free a specified amount of bytes.</p>
112: * <p>
113: * If the currently available memory is enough, the method returns <code>true</code> without
114: * performing additional steps. If not, the behaviour depends on the parameter <code>force</code>.
115: * If <code>false</code>, a Full GC is only performed if former GCs indicated that a GC should
116: * provide enough free memory. If former GCs didn't but <code>force</code> is set to <code>true</code>
117: * a Full GC is performed nevertheless.
118: * </p>
119: * <p>
120: * Setting the <code>force</code> parameter to false doesn't necessarily mean, that no GC may be
121: * performed by this method, if the currently available memory doesn't suffice!
122: * </p>
123: * <p><em>Be careful with this method as GCs should always be the last measure to take</em></p>
124: *
125: * @param size the requested amount of free memory in bytes
126: * @param force specifies whether a GC should be run even in case former GCs didn't provide enough memory
127: * @return whether enough memory could be freed (or is free) or not
128: */
129: public static boolean request(final long size, final boolean force) {
130: long avail = runtime.maxMemory() - runtime.totalMemory()
131: + runtime.freeMemory();
132: if (avail >= size)
133: return true;
134: if (log.isFine()) {
135: String t = new Throwable("Stack trace").getStackTrace()[1]
136: .toString();
137: log.logFine(t + " requested " + (size >> 10) + " KB, got "
138: + (avail >> 10) + " KB");
139: }
140: final long avg = getAverageGCFree();
141: if (force || avg == 0 || avg + avail >= size) {
142: // this is only called if we expect that an allocation of <size> bytes would cause the jvm to call the GC anyway
143: final long freed = runGC(!force);
144: avail = runtime.maxMemory() - runtime.totalMemory()
145: + runtime.freeMemory();
146: log.logInfo("performed "
147: + ((force) ? "explicit" : "necessary")
148: + " GC, freed " + (freed >> 10)
149: + " KB (requested/available/average: "
150: + (size >> 10) + " / " + (avail >> 10) + " / "
151: + (avg >> 10) + " KB)");
152: return avail >= size;
153: } else {
154: log
155: .logInfo("former GCs indicate to not be able to free enough memory (requested/available/average: "
156: + (size >> 10)
157: + " / "
158: + (avail >> 10)
159: + " / " + (avg >> 10) + " KB)");
160: return false;
161: }
162: }
163:
164: public static long used() {
165: // memory that is currently bound in objects
166: return runtime.totalMemory() - runtime.freeMemory();
167: }
168:
169: public static String bytesToString(long byteCount) {
170: try {
171: final StringBuffer byteString = new StringBuffer();
172:
173: if (byteCount > 1073741824) {
174: byteString.append(
175: yFormatter.number((double) byteCount
176: / (double) 1073741824)).append(" GB");
177: } else if (byteCount > 1048576) {
178: byteString.append(
179: yFormatter.number((double) byteCount
180: / (double) 1048576)).append(" MB");
181: } else if (byteCount > 1024) {
182: byteString.append(
183: yFormatter.number((double) byteCount
184: / (double) 1024)).append(" KB");
185: } else {
186: byteString.append(Long.toString(byteCount)).append(
187: " Bytes");
188: }
189:
190: return byteString.toString();
191: } catch (Exception e) {
192: return "unknown";
193: }
194: }
195:
196: public static void main(String[] args) {
197: // try this with a jvm 1.4.2 and with a jvm 1.5 and compare results
198: int mb = 1024 * 1024;
199: System.out.println("vm: "
200: + System.getProperty("java.vm.version"));
201: System.out.println("computed max = "
202: + (Runtime.getRuntime().maxMemory() / mb) + " mb");
203: int alloc = 10000;
204: Runtime rt = Runtime.getRuntime();
205: byte[][] x = new byte[100000][];
206: for (int i = 0; i < 100000; i++) {
207: x[i] = new byte[alloc];
208: if (i % 100 == 0)
209: System.out.println("used = "
210: + (i * alloc / mb)
211: + ", total = "
212: + (rt.totalMemory() / mb)
213: + ", free = "
214: + (rt.freeMemory() / mb)
215: + ", max = "
216: + (rt.maxMemory() / mb)
217: + ", avail = "
218: + ((rt.maxMemory() - rt.totalMemory() + rt
219: .freeMemory()) / mb));
220: }
221:
222: }
223:
224: }
|