GcFinalization.java revision 7dd252788645e940eada959bdde927426e2531c9
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
17package com.google.common.testing;
18
19import static java.util.concurrent.TimeUnit.SECONDS;
20
21import com.google.common.annotations.Beta;
22
23import java.lang.ref.WeakReference;
24import java.util.concurrent.CancellationException;
25import java.util.concurrent.CountDownLatch;
26import java.util.concurrent.ExecutionException;
27import java.util.concurrent.Future;
28import 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 stack-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>Even if your non-test code does not use finalization, you can
77 * use this class to test for leaks, by ensuring that objects are no
78 * longer strongly referenced:
79 *
80 * <pre> {@code
81 * // Helper function keeps victim stack-unreachable.
82 * private WeakReference<Foo> fooWeakRef() {
83 *   Foo x = ....;
84 *   WeakReference<Foo> weakRef = new WeakReference<Foo>(x);
85 *   // ... use x ...
86 *   x = null;  // Hint to the JIT that x is stack-unreachable
87 *   return weakRef;
88 * }
89 * public void testFooLeak() {
90 *   GcFinalization.awaitClear(fooWeakRef());
91 * }}</pre>
92 *
93 * <p>This class cannot currently be used to test soft references, since this class does not try to
94 * create the memory pressure required to cause soft references to be cleared.
95 *
96 * <p>This class only provides testing utilities.  It is not designed for direct use in production
97 * or for benchmarking.
98 *
99 * @author mike nonemacher
100 * @author Martin Buchholz
101 * @since 11.0
102 */
103@Beta
104public final class GcFinalization {
105  private GcFinalization() {}
106
107  /**
108   * 10 seconds ought to be long enough for any object to be GC'ed and finalized.  Unless we have a
109   * gigantic heap, in which case we scale by heap size.
110   */
111  private static long timeoutSeconds() {
112    // This class can make no hard guarantees.  The methods in this class are inherently flaky, but
113    // we try hard to make them robust in practice.  We could additionally try to add in a system
114    // load timeout multiplier.  Or we could try to use a CPU time bound instead of wall clock time
115    // bound.  But these ideas are harder to implement.  We do not try to detect or handle a
116    // user-specified -XX:+DisableExplicitGC.
117    //
118    // TODO(user): Consider using
119    // java/lang/management/OperatingSystemMXBean.html#getSystemLoadAverage()
120    //
121    // TODO(user): Consider scaling by number of mutator threads,
122    // e.g. using Thread#activeCount()
123    return Math.max(10L, Runtime.getRuntime().totalMemory() / (32L * 1024L * 1024L));
124  }
125
126  /**
127   * Waits until the given future {@linkplain Future#isDone is done}, invoking the garbage
128   * collector as necessary to try to ensure that this will happen.
129   *
130   * @throws RuntimeException if timed out or interrupted while waiting
131   */
132  public static void awaitDone(Future<?> future) {
133    if (future.isDone()) {
134      return;
135    }
136    final long timeoutSeconds = timeoutSeconds();
137    final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds);
138    do {
139      System.runFinalization();
140      if (future.isDone()) {
141        return;
142      }
143      System.gc();
144      try {
145        future.get(1L, SECONDS);
146        return;
147      } catch (CancellationException ok) {
148        return;
149      } catch (ExecutionException ok) {
150        return;
151      } catch (InterruptedException ie) {
152        throw new RuntimeException("Unexpected interrupt while waiting for future", ie);
153      } catch (TimeoutException tryHarder) {
154        /* OK */
155      }
156    } while (System.nanoTime() - deadline < 0);
157    throw new RuntimeException(
158        String.format("Future not done within %d second timeout", timeoutSeconds));
159  }
160
161  /**
162   * Waits until the given latch has {@linkplain CountDownLatch#countDown counted down} to zero,
163   * invoking the garbage collector as necessary to try to ensure that this will happen.
164   *
165   * @throws RuntimeException if timed out or interrupted while waiting
166   */
167  public static void await(CountDownLatch latch) {
168    if (latch.getCount() == 0) {
169      return;
170    }
171    final long timeoutSeconds = timeoutSeconds();
172    final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds);
173    do {
174      System.runFinalization();
175      if (latch.getCount() == 0) {
176        return;
177      }
178      System.gc();
179      try {
180        if (latch.await(1L, SECONDS)) {
181          return;
182        }
183      } catch (InterruptedException ie) {
184        throw new RuntimeException("Unexpected interrupt while waiting for latch", ie);
185      }
186    } while (System.nanoTime() - deadline < 0);
187    throw new RuntimeException(
188        String.format("Latch failed to count down within %d second timeout", timeoutSeconds));
189  }
190
191  /**
192   * Creates a garbage object that counts down the latch in its finalizer.  Sequestered into a
193   * separate method to make it somewhat more likely to be unreachable.
194   */
195  private static void createUnreachableLatchFinalizer(final CountDownLatch latch) {
196    new Object() { @Override protected void finalize() { latch.countDown(); }};
197  }
198
199  /**
200   * A predicate that is expected to return true subsequent to <em>finalization</em>, that is, one
201   * of the following actions taken by the garbage collector when performing a full collection in
202   * response to {@link System#gc()}:
203   *
204   * <ul>
205   * <li>invoking the {@code finalize} methods of unreachable objects
206   * <li>clearing weak references to unreachable referents
207   * <li>enqueuing weak references to unreachable referents in their reference queue
208   * </ul>
209   */
210  public interface FinalizationPredicate {
211    boolean isDone();
212  }
213
214  /**
215   * Waits until the given predicate returns true, invoking the garbage collector as necessary to
216   * try to ensure that this will happen.
217   *
218   * @throws RuntimeException if timed out or interrupted while waiting
219   */
220  public static void awaitDone(FinalizationPredicate predicate) {
221    if (predicate.isDone()) {
222      return;
223    }
224    final long timeoutSeconds = timeoutSeconds();
225    final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds);
226    do {
227      System.runFinalization();
228      if (predicate.isDone()) {
229        return;
230      }
231      CountDownLatch done = new CountDownLatch(1);
232      createUnreachableLatchFinalizer(done);
233      await(done);
234      if (predicate.isDone()) {
235        return;
236      }
237    } while (System.nanoTime() - deadline < 0);
238    throw new RuntimeException(
239        String.format("Predicate did not become true within %d second timeout", timeoutSeconds));
240  }
241
242  /**
243   * Waits until the given weak reference is cleared, invoking the garbage collector as necessary
244   * to try to ensure that this will happen.
245   *
246   * <p>This is a convenience method, equivalent to:
247   * <pre>   {@code
248   *   awaitDone(new FinalizationPredicate() {
249   *     public boolean isDone() {
250   *       return ref.get() == null;
251   *     }
252   *   });
253   * }</pre>
254   *
255   * @throws RuntimeException if timed out or interrupted while waiting
256   */
257  public static void awaitClear(final WeakReference<?> ref) {
258    awaitDone(new FinalizationPredicate() {
259      public boolean isDone() {
260        return ref.get() == null;
261      }
262    });
263  }
264
265  /**
266   * Tries to perform a "full" garbage collection cycle (including processing of weak references
267   * and invocation of finalize methods) and waits for it to complete.  Ensures that at least one
268   * weak reference has been cleared and one {@code finalize} method has been run before this
269   * method returns.  This method may be useful when testing the garbage collection mechanism
270   * itself, or inhibiting a spontaneous GC initiation in subsequent code.
271   *
272   * <p>In contrast, a plain call to {@link java.lang.System#gc()} does not ensure finalization
273   * processing and may run concurrently, for example, if the JVM flag {@code
274   * -XX:+ExplicitGCInvokesConcurrent} is used.
275   *
276   * <p>Whenever possible, it is preferable to test directly for some observable change resulting
277   * from GC, as with {@link #awaitClear}.  Because there are no guarantees for the order of GC
278   * finalization processing, there may still be some unfinished work for the GC to do after this
279   * method returns.
280   *
281   * <p>This method does not create any memory pressure as would be required to cause soft
282   * references to be processed.
283   *
284   * @throws RuntimeException if timed out or interrupted while waiting
285   * @since 12.0
286   */
287  public static void awaitFullGc() {
288    final CountDownLatch finalizerRan = new CountDownLatch(1);
289    WeakReference<Object> ref = new WeakReference<Object>(
290        new Object() {
291          @Override protected void finalize() { finalizerRan.countDown(); }
292        });
293
294    await(finalizerRan);
295    awaitClear(ref);
296
297    // Hope to catch some stragglers queued up behind our finalizable object
298    System.runFinalization();
299  }
300}
301