001: /*
002:
003: Derby - Class org.apache.derby.iapi.services.memory.LowMemory
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.services.memory;
023:
024: /**
025: * Methods to aid classes recover from OutOfMemoryErrors by denying
026: * or reducing service rather than a complete shutdown of the JVM.
027: * It's intended that classes use to functionality to allow then to
028: * deny service when memory is low to allow the JVM to recover,
029: * rather than start new operations that are probably doomed to
030: * failure due to the low memory.
031: * <P>
032: * Expected usage is one instance of this class per major logical
033: * operation, e.g. creating a connection, preparing a statement,
034: * adding an entry to a specific cache etc.
035: * <BR>
036: * The logical operation would call isLowMemory() before starting
037: * the operation, and thrown a static exception if it returns true.
038: * <BR>
039: * If during the operation an OutOfMemoryException is thrown the
040: * operation would call setLowMemory() and throw its static exception
041: * representing low memory.
042: * <P>
043: * Future enhancments could be a callback mechanism for modules
044: * where they register they can reduce memory usage on a low
045: * memory situation. These callbacks would be triggered by
046: * a call to setLowMemory. For example the page cache could
047: * reduce its current size by 10% in a low memory situation.
048: *
049: */
050: public class LowMemory {
051:
052: /**
053: * Free memory seen when caller indicated an out of
054: * memory situation. Becomes a low memory watermark
055: * for five seconds that causes isLowMemory to return
056: * true if free memory is lower than this value.
057: * This allows the JVM a chance to recover memory
058: * rather than start new operations that are probably
059: * doomed to failure due to the low memory.
060: *
061: */
062: private long lowMemory;
063:
064: /**
065: * Time in ms corresponding to System.currentTimeMillis() when
066: * lowMemory was set.
067: */
068: private long whenLowMemorySet;
069:
070: /**
071: * Set a low memory watermark where the owner of this object just hit an
072: * OutOfMemoryError. The caller is assumed it has just freed up any
073: * references it obtained during the operation, so that the freeMemory call
074: * as best as it can reflects the memory before the action that caused the
075: * OutOfMemoryError, not part way through the action.
076: *
077: */
078: public void setLowMemory() {
079:
080: // Can read lowMemory unsynchronized, worst
081: // case is that we force extra garbage collection.
082: if (lowMemory == 0L) {
083:
084: // The caller tried to dereference any objects it
085: // created during its instantation. Try to garbage
086: // collect these so that we can a best-guess effort
087: // at the free memory before the overall operation we are
088: // failing on occurred. Of course in active multi-threading
089: // systems we run the risk that some other thread just freed
090: // up some memory that throws off our calcuation. This is
091: // avoided by clearing lowMemory some time later on an
092: // isLowMemory() call.
093: for (int i = 0; i < 5; i++) {
094: System.gc();
095: System.runFinalization();
096: try {
097: Thread.sleep(50L);
098: } catch (InterruptedException e) {
099: }
100: }
101: }
102: synchronized (this ) {
103: if (lowMemory == 0L) {
104: lowMemory = Runtime.getRuntime().freeMemory();
105: whenLowMemorySet = System.currentTimeMillis();
106: }
107: }
108: }
109:
110: /**
111: * Return true if a low memory water mark has been set and the current free
112: * memory is lower than it. Otherwise return false.
113: */
114: public boolean isLowMemory() {
115: synchronized (this ) {
116: long lm = lowMemory;
117: if (lm == 0)
118: return false;
119:
120: if (Runtime.getRuntime().freeMemory() > lm)
121: return false;
122:
123: // Only allow an low memory watermark to be valid
124: // for five seconds after it was set. This stops
125: // an incorrect limit being set for ever. This could
126: // occur if other threads were freeing memory when
127: // we called Runtime.getRuntime().freeMemory()
128:
129: long now = System.currentTimeMillis();
130: if ((now - this .whenLowMemorySet) > 5000L) {
131: lowMemory = 0L;
132: whenLowMemorySet = 0L;
133: return false;
134: }
135: return true;
136: }
137: }
138: }
|