View Javadoc
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  import com.google.common.annotations.GwtIncompatible;
23  import com.google.j2objc.annotations.J2ObjCIncompatible;
24  import java.lang.ref.WeakReference;
25  import java.util.Locale;
26  import java.util.concurrent.CancellationException;
27  import java.util.concurrent.CountDownLatch;
28  import java.util.concurrent.ExecutionException;
29  import java.util.concurrent.Future;
30  import java.util.concurrent.TimeoutException;
31  
32  /**
33   * Testing utilities relating to garbage collection finalization.
34   *
35   * <p>Use this class to test code triggered by <em>finalization</em>, that is, one of the
36   * following actions taken by the java garbage collection system:
37   *
38   * <ul>
39   * <li>invoking the {@code finalize} methods of unreachable objects
40   * <li>clearing weak references to unreachable referents
41   * <li>enqueuing weak references to unreachable referents in their reference queue
42   * </ul>
43   *
44   * <p>This class uses (possibly repeated) invocations of {@link java.lang.System#gc()} to cause
45   * finalization to happen.  However, a call to {@code System.gc()} is specified to be no more
46   * than a hint, so this technique may fail at the whim of the JDK implementation, for example if
47   * a user specified the JVM flag {@code -XX:+DisableExplicitGC}.  But in practice, it works very
48   * well for ordinary tests.
49   *
50   * <p>Failure of the expected event to occur within an implementation-defined "reasonable" time
51   * period or an interrupt while waiting for the expected event will result in a {@link
52   * RuntimeException}.
53   *
54   * <p>Here's an example that tests a {@code finalize} method:
55   *
56   * <pre>   {@code
57   *   final CountDownLatch latch = new CountDownLatch(1);
58   *   Object x = new MyClass() {
59   *     ...
60   *     protected void finalize() { latch.countDown(); ... }
61   *   };
62   *   x = null;  // Hint to the JIT that x is stack-unreachable
63   *   GcFinalization.await(latch);}</pre>
64   *
65   * <p>Here's an example that uses a user-defined finalization predicate:
66   *
67   * <pre>   {@code
68   *   final WeakHashMap<Object, Object> map = new WeakHashMap<Object, Object>();
69   *   map.put(new Object(), Boolean.TRUE);
70   *   GcFinalization.awaitDone(new FinalizationPredicate() {
71   *     public boolean isDone() {
72   *       return map.isEmpty();
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
104 @GwtIncompatible
105 @J2ObjCIncompatible // gc
106 public final class GcFinalization {
107   private GcFinalization() {}
108 
109   /**
110    * 10 seconds ought to be long enough for any object to be GC'ed and finalized.  Unless we have a
111    * gigantic heap, in which case we scale by heap size.
112    */
113   private static long timeoutSeconds() {
114     // This class can make no hard guarantees.  The methods in this class are inherently flaky, but
115     // we try hard to make them robust in practice.  We could additionally try to add in a system
116     // load timeout multiplier.  Or we could try to use a CPU time bound instead of wall clock time
117     // bound.  But these ideas are harder to implement.  We do not try to detect or handle a
118     // user-specified -XX:+DisableExplicitGC.
119     //
120     // TODO(user): Consider using
121     // java/lang/management/OperatingSystemMXBean.html#getSystemLoadAverage()
122     //
123     // TODO(user): Consider scaling by number of mutator threads,
124     // e.g. using Thread#activeCount()
125     return Math.max(10L, Runtime.getRuntime().totalMemory() / (32L * 1024L * 1024L));
126   }
127 
128   /**
129    * Waits until the given future {@linkplain Future#isDone is done}, invoking the garbage
130    * collector as necessary to try to ensure that this will happen.
131    *
132    * @throws RuntimeException if timed out or interrupted while waiting
133    */
134   public static void awaitDone(Future<?> future) {
135     if (future.isDone()) {
136       return;
137     }
138     final long timeoutSeconds = timeoutSeconds();
139     final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds);
140     do {
141       System.runFinalization();
142       if (future.isDone()) {
143         return;
144       }
145       System.gc();
146       try {
147         future.get(1L, SECONDS);
148         return;
149       } catch (CancellationException | 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 formatRuntimeException("Future not done within %d second timeout", timeoutSeconds);
158   }
159 
160   /**
161    * Waits until the given latch has {@linkplain CountDownLatch#countDown counted down} to zero,
162    * invoking the garbage collector as necessary to try to ensure that this will happen.
163    *
164    * @throws RuntimeException if timed out or interrupted while waiting
165    */
166   public static void await(CountDownLatch latch) {
167     if (latch.getCount() == 0) {
168       return;
169     }
170     final long timeoutSeconds = timeoutSeconds();
171     final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds);
172     do {
173       System.runFinalization();
174       if (latch.getCount() == 0) {
175         return;
176       }
177       System.gc();
178       try {
179         if (latch.await(1L, SECONDS)) {
180           return;
181         }
182       } catch (InterruptedException ie) {
183         throw new RuntimeException("Unexpected interrupt while waiting for latch", ie);
184       }
185     } while (System.nanoTime() - deadline < 0);
186     throw formatRuntimeException(
187         "Latch failed to count down within %d second timeout", timeoutSeconds);
188   }
189 
190   /**
191    * Creates a garbage object that counts down the latch in its finalizer.  Sequestered into a
192    * separate method to make it somewhat more likely to be unreachable.
193    */
194   private static void createUnreachableLatchFinalizer(final CountDownLatch latch) {
195     new Object() { @Override protected void finalize() { latch.countDown(); }};
196   }
197 
198   /**
199    * A predicate that is expected to return true subsequent to <em>finalization</em>, that is, one
200    * of the following actions taken by the garbage collector when performing a full collection in
201    * response to {@link System#gc()}:
202    *
203    * <ul>
204    * <li>invoking the {@code finalize} methods of unreachable objects
205    * <li>clearing weak references to unreachable referents
206    * <li>enqueuing weak references to unreachable referents in their reference queue
207    * </ul>
208    */
209   public interface FinalizationPredicate {
210     boolean isDone();
211   }
212 
213   /**
214    * Waits until the given predicate returns true, invoking the garbage collector as necessary to
215    * try to ensure that this will happen.
216    *
217    * @throws RuntimeException if timed out or interrupted while waiting
218    */
219   public static void awaitDone(FinalizationPredicate predicate) {
220     if (predicate.isDone()) {
221       return;
222     }
223     final long timeoutSeconds = timeoutSeconds();
224     final long deadline = System.nanoTime() + SECONDS.toNanos(timeoutSeconds);
225     do {
226       System.runFinalization();
227       if (predicate.isDone()) {
228         return;
229       }
230       CountDownLatch done = new CountDownLatch(1);
231       createUnreachableLatchFinalizer(done);
232       await(done);
233       if (predicate.isDone()) {
234         return;
235       }
236     } while (System.nanoTime() - deadline < 0);
237     throw formatRuntimeException(
238         "Predicate did not become true within %d second timeout", timeoutSeconds);
239   }
240 
241   /**
242    * Waits until the given weak reference is cleared, invoking the garbage collector as necessary
243    * to try to ensure that this will happen.
244    *
245    * <p>This is a convenience method, equivalent to:
246    * <pre>   {@code
247    *   awaitDone(new FinalizationPredicate() {
248    *     public boolean isDone() {
249    *       return ref.get() == null;
250    *     }
251    *   });}</pre>
252    *
253    * @throws RuntimeException if timed out or interrupted while waiting
254    */
255   public static void awaitClear(final WeakReference<?> ref) {
256     awaitDone(new FinalizationPredicate() {
257       public boolean isDone() {
258         return ref.get() == null;
259       }
260     });
261   }
262 
263   /**
264    * Tries to perform a "full" garbage collection cycle (including processing of weak references
265    * and invocation of finalize methods) and waits for it to complete.  Ensures that at least one
266    * weak reference has been cleared and one {@code finalize} method has been run before this
267    * method returns.  This method may be useful when testing the garbage collection mechanism
268    * itself, or inhibiting a spontaneous GC initiation in subsequent code.
269    *
270    * <p>In contrast, a plain call to {@link java.lang.System#gc()} does not ensure finalization
271    * processing and may run concurrently, for example, if the JVM flag {@code
272    * -XX:+ExplicitGCInvokesConcurrent} is used.
273    *
274    * <p>Whenever possible, it is preferable to test directly for some observable change resulting
275    * from GC, as with {@link #awaitClear}.  Because there are no guarantees for the order of GC
276    * finalization processing, there may still be some unfinished work for the GC to do after this
277    * method returns.
278    *
279    * <p>This method does not create any memory pressure as would be required to cause soft
280    * references to be processed.
281    *
282    * @throws RuntimeException if timed out or interrupted while waiting
283    * @since 12.0
284    */
285   public static void awaitFullGc() {
286     final CountDownLatch finalizerRan = new CountDownLatch(1);
287     WeakReference<Object> ref =
288         new WeakReference<Object>(
289             new Object() {
290               @Override
291               protected void finalize() {
292                 finalizerRan.countDown();
293               }
294             });
295 
296     await(finalizerRan);
297     awaitClear(ref);
298 
299     // Hope to catch some stragglers queued up behind our finalizable object
300     System.runFinalization();
301   }
302 
303   private static RuntimeException formatRuntimeException(String format, Object... args) {
304     return new RuntimeException(String.format(Locale.ROOT, format, args));
305   }
306 }