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