GcFinalization.java revision 1d580d0f6ee4f21eb309ba7b509d2c6d671c4044
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 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@google.com (mike nonemacher)
83 * @author martinrb@google.com (Martin Buchholz)
84 * @since 11.0
85 */
86@Beta
87public 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