001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2007-2007 Pentaho and others 008// All Rights Reserved. 009*/ 010package mondrian.util; 011 012/** 013 * API for Mondrian's memory monitors. 014 * 015 * <p>For Java4, the available monitors 016 * do nothing since there is no reliable way of detecting that 017 * memory is running low using such a JVM (you are welcome to 018 * try to create one, but I believe you will fail - some such candidates 019 * only make it more likely that an OutOfMemory condition will occur). 020 * 021 * <p>For Java5 one 022 * can optionally enable a monitor which is based upon the Java5 023 * memory classes locate in java.lang.management. 024 * 025 * <p>A client must implement the <code>MemoryMonitor.Listener</code> interface 026 * and register with the <code>MemoryMonitor</code>. 027 * 028 * <p>The <code>MemoryMonitor</code> supports having multiple 029 * <code>Listener</code> clients. The clients can have the same 030 * threshold percentage or different values. The threshold percentage value 031 * is used by the <code>MemoryMonitor</code> to determine when to 032 * notify a client. It is the percentage of the total memory: 033 * <blockquote> 034 * <code> 035 * 100 * free-memory / total-memory (0 ≤ free-memory ≤ total-memory). 036 * </code> 037 * </blockquote> 038 * 039 * @author Richard M. Emberson 040 * @since Feb 01 2007 041 */ 042public interface MemoryMonitor { 043 044 /** 045 * Adds a <code>Listener</code> to the <code>MemoryMonitor</code> with 046 * a given threshold percentage. 047 * 048 * <p>If the threshold is below the Java5 memory managment system's 049 * threshold, then the Listener is notified from within this 050 * method. 051 * 052 * @param listener the <code>Listener</code> to be added. 053 * @param thresholdPercentage the threshold percentage for this 054 * <code>Listener</code>. 055 * @return <code>true</code> if the <code>Listener</code> was 056 * added and <code>false</code> otherwise. 057 */ 058 boolean addListener(Listener listener, int thresholdPercentage); 059 060 /** 061 * Adds a <code>Listener</code> using the default threshold percentage. 062 * 063 * <p>If the threshold is below the Java5 memory managment system's 064 * threshold, then the Listener is notified from within this 065 * method. 066 * 067 * @param listener the <code>Listener</code> to be added. 068 * @return <code>true</code> if the <code>Listener</code> was 069 * added and <code>false</code> otherwise. 070 */ 071 boolean addListener(final Listener listener); 072 073 /** 074 * Changes the threshold percentage of a given <code>Listener</code>. 075 * 076 * <p>If the new value is below the system's current value, then the 077 * <code>Listener</code> will have its notification callback called 078 * while in this method - so a client should always check if its 079 * notification method was called immediately after calling this 080 * method. 081 * 082 * <p>This method can be used if, for example, an algorithm has 083 * different approaches that result in different memory 084 * usage profiles; one, large memory but fast and 085 * a second which is low-memory but slow. The algorithm starts 086 * with the large memory approach, receives a low memory 087 * notification, switches to the low memory approach and changes 088 * when it should be notified for this new approach. The first 089 * approach need to be notified at a lower percentage because it 090 * uses lots of memory, possibly quickly; while the second 091 * approach, possibly a file based algorithm, has smaller memory 092 * requirements and uses memory less quickly thus one can 093 * live with a higher notification threshold percentage. 094 * 095 * @param listener the <code>Listener</code> being updated. 096 * @param percentage new percentage threshold. 097 */ 098 void updateListenerThreshold(Listener listener, int percentage); 099 100 /** 101 * Removes a <code>Listener</code> from the <code>MemoryMonitor</code>. 102 * Returns <code>true</code> if listener was removed and 103 * <code>false</code> otherwise. 104 * 105 * @param listener the listener to be removed 106 * @return <code>true</code> if listener was removed. 107 */ 108 boolean removeListener(Listener listener); 109 110 /** 111 * Clear out all <code>Listener</code>s and turnoff JVM 112 * memory notification. 113 */ 114 void removeAllListener(); 115 116 /** 117 * Returns the maximum memory usage. 118 * 119 * @return the maximum memory usage. 120 */ 121 long getMaxMemory(); 122 123 /** 124 * Returns the current memory used. 125 * 126 * @return the current memory used. 127 */ 128 long getUsedMemory(); 129 130 131 /** 132 * A <code>MemoryMonitor</code> client implements the <code>Listener</code> 133 * interface and registers with the <code>MemoryMonitor</code>. 134 * When the <code>MemoryMonitor</code> detects that free memory is 135 * low, it notifies the client by calling the client's 136 * <code>memoryUsageNotification</code> method. It is important 137 * that the client quickly return from this call, that the 138 * <code>memoryUsageNotification</code> method does not do a lot of 139 * work. It is best if it simply sets a flag. The flag should be 140 * polled by an application thread and when it detects that the 141 * flag was set, it should take immediate memory relinquishing operations. 142 * In the case of Mondrian, the present query is aborted. 143 */ 144 interface Listener { 145 146 /** 147 * When the <code>MemoryMonitor</code> determines that the 148 * <code>Listener</code>'s threshold is equal to or less than 149 * the current available memory (post garbage collection), 150 * then this method is called with the current memory usage, 151 * <code>usedMemory</code>, and the maximum memory (which 152 * is a constant per JVM invocation). 153 * <p> 154 * This method is called (in the case of Java5) by a system 155 * thread associated with the garbage collection activity. 156 * When this method is called, the client should quickly do what 157 * it needs to to communicate with an application thread and 158 * then return. Generally, quickly de-referencing some big objects 159 * and setting a flag is the most that should be done by 160 * implementations of this method. If the implementor chooses to 161 * de-reference some objects, then the application code must 162 * be written so that if will not throw a NullPointerException 163 * when such de-referenced objects are accessed. If a flag 164 * is set, then the application must be written to check the 165 * flag periodically. 166 * 167 * @param usedMemory the current memory used. 168 * @param maxMemory the maximum available memory. 169 */ 170 void memoryUsageNotification(long usedMemory, long maxMemory); 171 } 172 173 /** 174 * This is an interface that a <code>MemoryMonitor</code> may optionally 175 * implement. These methods give the tester access to some of the 176 * internal, white-box data. 177 * <p> 178 * During testing Mondrian has a default 179 * <code>MemoryMonitor</code> which might be replaced with a test 180 * <code>MemoryMonitor</code>s using the <code>ThreadLocal</code> 181 * mechanism. After the test using the test 182 * <code>MemoryMonitor</code> finishes, a call to the 183 * <code>resetFromTest</code> method allows 184 * the default <code>MemoryMonitor</code> reset itself. 185 * This is hook that should only be called as part of testing. 186 */ 187 interface Test { 188 189 /** 190 * This should only be called when one is switching from a 191 * test <code>MemoryMonitor</code> back to the default system 192 * <code>MemoryMonitor</code>. In particular, look at 193 * the <code>MemoryMonitorFactory</code>'s 194 * <code>clearThreadLocalClassName()</code> method for its 195 * usage. 196 */ 197 void resetFromTest(); 198 } 199} 200 201// End MemoryMonitor.java