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