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