1 /* 2 * Copyright (C) 2011 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.testing; 18 19 import static java.util.concurrent.TimeUnit.SECONDS; 20 21 import com.google.common.annotations.Beta; 22 23 import java.lang.ref.WeakReference; 24 import java.util.concurrent.CancellationException; 25 import java.util.concurrent.CountDownLatch; 26 import java.util.concurrent.ExecutionException; 27 import java.util.concurrent.Future; 28 import java.util.concurrent.TimeoutException; 29 30 /** 31 * Testing utilities relating to garbage collection finalization. 32 * 33 * <p>Use this class to test code triggered by <em>finalization</em>, that is, one of the 34 * following actions taken by the java garbage collection system: 35 * 36 * <ul> 37 * <li>invoking the {@code finalize} methods of unreachable objects 38 * <li>clearing weak references to unreachable referents 39 * <li>enqueuing weak references to unreachable referents in their reference queue 40 * </ul> 41 * 42 * <p>This class uses (possibly repeated) invocations of {@link java.lang.System#gc()} to cause 43 * finalization to happen. However, a call to {@code System.gc()} is specified to be no more 44 * than a hint, so this technique may fail at the whim of the JDK implementation, for example if 45 * a user specified the JVM flag {@code -XX:+DisableExplicitGC}. But in practice, it works very 46 * well for ordinary tests. 47 * 48 * <p>Failure of the expected event to occur within an implementation-defined "reasonable" time 49 * period or an interrupt while waiting for the expected event will result in a {@link 50 * RuntimeException}. 51 * 52 * <p>Here's an example that tests a {@code finalize} method: 53 * 54 * <pre> {@code 55 * final CountDownLatch latch = new CountDownLatch(1); 56 * Object x = new MyClass() { 57 * ... 58 * protected void finalize() { latch.countDown(); ... } 59 * }; 60 * x = null; // Hint to the JIT that x is unreachable 61 * GcFinalization.await(latch); 62 * }</pre> 63 * 64 * <p>Here's an example that uses a user-defined finalization predicate: 65 * 66 * <pre> {@code 67 * final WeakHashMap<Object, Object> map = new WeakHashMap<Object, Object>(); 68 * map.put(new Object(), Boolean.TRUE); 69 * GcFinalization.awaitDone(new FinalizationPredicate() { 70 * public boolean isDone() { 71 * return map.isEmpty(); 72 * } 73 * }); 74 * }</pre> 75 * 76 * <p>This class cannot currently be used to test soft references, since this class does not try to 77 * create the memory pressure required to cause soft references to be cleared. 78 * 79 * <p>This class only provides testing utilities. It is not designed for direct use in production 80 * or for benchmarking. 81 * 82 * @author schmoe (at) google.com (mike nonemacher) 83 * @author martinrb (at) google.com (Martin Buchholz) 84 * @since 11.0 85 */ 86 @Beta 87 public final class GcFinalization { 88 private GcFinalization() {} 89 90 /** 91 * 10 seconds ought to be long enough for any object to be GC'ed and finalized. Unless we have a 92 * gigantic heap, in which case we scale by heap size. 93 */ 94 private static long timeoutSeconds() { 95 // This class can make no hard guarantees. The methods in this class are inherently flaky, but 96 // we try hard to make them robust in practice. We could additionally try to add in a system 97 // load timeout multiplier. Or we could try to use a CPU time bound instead of wall clock time 98 // bound. But these ideas are harder to implement. We do not try to detect or handle a 99 // user-specified -XX:+DisableExplicitGC. 100 // 101 // TODO(user): Consider using 102 // java/lang/management/OperatingSystemMXBean.html#getSystemLoadAverage() 103 // 104 // TODO(user): Consider scaling by number of mutator threads, 105 // e.g. using Thread#activeCount() 106 return Math.max(10L, Runtime.getRuntime().totalMemory() / (32L * 1024L * 1024L)); 107 } 108 109 /** 110 * Waits until the given future {@linkplain Future#isDone is done}, invoking the garbage 111 * collector as necessary to try to ensure that this will happen. 112 * 113 * @throws RuntimeException if timed out or interrupted while waiting 114 */ 115 public static void awaitDone(Future<?> future) { 116 if (future.isDone()) { 117 return; 118 } 119 final long timeoutSeconds = timeoutSeconds(); 120 final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds); 121 do { 122 System.runFinalization(); 123 if (future.isDone()) { 124 return; 125 } 126 System.gc(); 127 try { 128 future.get(1L, SECONDS); 129 return; 130 } catch (CancellationException ok) { 131 return; 132 } catch (ExecutionException ok) { 133 return; 134 } catch (InterruptedException ie) { 135 throw new RuntimeException("Unexpected interrupt while waiting for future", ie); 136 } catch (TimeoutException tryHarder) { 137 /* OK */ 138 } 139 } while (System.nanoTime() - deadline < 0); 140 throw new RuntimeException( 141 String.format("Future not done within %d second timeout", timeoutSeconds)); 142 } 143 144 /** 145 * Waits until the given latch has {@linkplain CountDownLatch#countDown counted down} to zero, 146 * invoking the garbage collector as necessary to try to ensure that this will happen. 147 * 148 * @throws RuntimeException if timed out or interrupted while waiting 149 */ 150 public static void await(CountDownLatch latch) { 151 if (latch.getCount() == 0) { 152 return; 153 } 154 final long timeoutSeconds = timeoutSeconds(); 155 final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds); 156 do { 157 System.runFinalization(); 158 if (latch.getCount() == 0) { 159 return; 160 } 161 System.gc(); 162 try { 163 if (latch.await(1L, SECONDS)) { 164 return; 165 } 166 } catch (InterruptedException ie) { 167 throw new RuntimeException("Unexpected interrupt while waiting for latch", ie); 168 } 169 } while (System.nanoTime() - deadline < 0); 170 throw new RuntimeException( 171 String.format("Latch failed to count down within %d second timeout", timeoutSeconds)); 172 } 173 174 /** 175 * Creates a garbage object that counts down the latch in its finalizer. Sequestered into a 176 * separate method to make it somewhat more likely to be unreachable. 177 */ 178 private static void createUnreachableLatchFinalizer(final CountDownLatch latch) { 179 new Object() { @Override protected void finalize() { latch.countDown(); }}; 180 } 181 182 /** 183 * A predicate that is expected to return true subsequent to <em>finalization</em>, that is, one 184 * of the following actions taken by the garbage collector when performing a full collection in 185 * response to {@link System#gc()}: 186 * 187 * <ul> 188 * <li>invoking the {@code finalize} methods of unreachable objects 189 * <li>clearing weak references to unreachable referents 190 * <li>enqueuing weak references to unreachable referents in their reference queue 191 * </ul> 192 */ 193 public interface FinalizationPredicate { 194 boolean isDone(); 195 } 196 197 /** 198 * Waits until the given predicate returns true, invoking the garbage collector as necessary to 199 * try to ensure that this will happen. 200 * 201 * @throws RuntimeException if timed out or interrupted while waiting 202 */ 203 public static void awaitDone(FinalizationPredicate predicate) { 204 if (predicate.isDone()) { 205 return; 206 } 207 final long timeoutSeconds = timeoutSeconds(); 208 final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds); 209 do { 210 System.runFinalization(); 211 if (predicate.isDone()) { 212 return; 213 } 214 CountDownLatch done = new CountDownLatch(1); 215 createUnreachableLatchFinalizer(done); 216 await(done); 217 if (predicate.isDone()) { 218 return; 219 } 220 } while (System.nanoTime() - deadline < 0); 221 throw new RuntimeException( 222 String.format("Predicate did not become true within %d second timeout", timeoutSeconds)); 223 } 224 225 /** 226 * Waits until the given weak reference is cleared, invoking the garbage collector as necessary 227 * to try to ensure that this will happen. 228 * 229 * <p>This is a convenience method, equivalent to: 230 * <pre> {@code 231 * awaitDone(new FinalizationPredicate() { 232 * public boolean isDone() { 233 * return ref.get() == null; 234 * } 235 * }); 236 * }</pre> 237 * 238 * @throws RuntimeException if timed out or interrupted while waiting 239 */ 240 public static void awaitClear(final WeakReference<?> ref) { 241 awaitDone(new FinalizationPredicate() { 242 public boolean isDone() { 243 return ref.get() == null; 244 } 245 }); 246 } 247 } 248