--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.iapi.services.memory.LowMemory\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.iapi.services.memory;\r
+\r
+/**\r
+ * Methods to aid classes recover from OutOfMemoryErrors by denying\r
+ * or reducing service rather than a complete shutdown of the JVM.\r
+ * It's intended that classes use to functionality to allow then to\r
+ * deny service when memory is low to allow the JVM to recover,\r
+ * rather than start new operations that are probably doomed to\r
+ * failure due to the low memory.\r
+ * <P>\r
+ * Expected usage is one instance of this class per major logical\r
+ * operation, e.g. creating a connection, preparing a statement,\r
+ * adding an entry to a specific cache etc.\r
+ * <BR>\r
+ * The logical operation would call isLowMemory() before starting\r
+ * the operation, and thrown a static exception if it returns true.\r
+ * <BR>\r
+ * If during the operation an OutOfMemoryException is thrown the\r
+ * operation would call setLowMemory() and throw its static exception\r
+ * representing low memory.\r
+ * <P>\r
+ * Future enhancments could be a callback mechanism for modules\r
+ * where they register they can reduce memory usage on a low\r
+ * memory situation. These callbacks would be triggered by\r
+ * a call to setLowMemory. For example the page cache could\r
+ * reduce its current size by 10% in a low memory situation.\r
+ * \r
+ */\r
+public class LowMemory {\r
+\r
+ /**\r
+ * Free memory seen when caller indicated an out of\r
+ * memory situation. Becomes a low memory watermark\r
+ * for five seconds that causes isLowMemory to return\r
+ * true if free memory is lower than this value.\r
+ * This allows the JVM a chance to recover memory\r
+ * rather than start new operations that are probably\r
+ * doomed to failure due to the low memory.\r
+ * \r
+ */\r
+ private long lowMemory;\r
+ \r
+ /**\r
+ * Time in ms corresponding to System.currentTimeMillis() when\r
+ * lowMemory was set.\r
+ */\r
+ private long whenLowMemorySet;\r
+ \r
+ /**\r
+ * Set a low memory watermark where the owner of this object just hit an\r
+ * OutOfMemoryError. The caller is assumed it has just freed up any\r
+ * references it obtained during the operation, so that the freeMemory call\r
+ * as best as it can reflects the memory before the action that caused the\r
+ * OutOfMemoryError, not part way through the action.\r
+ * \r
+ */\r
+ public void setLowMemory() {\r
+ \r
+ // Can read lowMemory unsynchronized, worst\r
+ // case is that we force extra garbage collection.\r
+ if (lowMemory == 0L) {\r
+ \r
+ // The caller tried to dereference any objects it\r
+ // created during its instantation. Try to garbage\r
+ // collect these so that we can a best-guess effort\r
+ // at the free memory before the overall operation we are\r
+ // failing on occurred. Of course in active multi-threading\r
+ // systems we run the risk that some other thread just freed\r
+ // up some memory that throws off our calcuation. This is\r
+ // avoided by clearing lowMemory some time later on an\r
+ // isLowMemory() call.\r
+ for (int i = 0; i < 5; i++) {\r
+ System.gc();\r
+ System.runFinalization();\r
+ try {\r
+ Thread.sleep(50L);\r
+ } catch (InterruptedException e) {\r
+ }\r
+ }\r
+ }\r
+ synchronized (this) {\r
+ if (lowMemory == 0L) {\r
+ lowMemory = Runtime.getRuntime().freeMemory();\r
+ whenLowMemorySet = System.currentTimeMillis();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return true if a low memory water mark has been set and the current free\r
+ * memory is lower than it. Otherwise return false.\r
+ */\r
+ public boolean isLowMemory() {\r
+ synchronized (this) {\r
+ long lm = lowMemory;\r
+ if (lm == 0)\r
+ return false;\r
+ \r
+ if (Runtime.getRuntime().freeMemory() > lm)\r
+ return false;\r
+ \r
+ // Only allow an low memory watermark to be valid\r
+ // for five seconds after it was set. This stops\r
+ // an incorrect limit being set for ever. This could\r
+ // occur if other threads were freeing memory when\r
+ // we called Runtime.getRuntime().freeMemory()\r
+ \r
+ long now = System.currentTimeMillis();\r
+ if ((now - this.whenLowMemorySet) > 5000L) {\r
+ lowMemory = 0L;\r
+ whenLowMemorySet = 0L;\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+ }\r
+}\r