package cern.util.testhelpers; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; import java.lang.ref.Reference; /** * A helper class to check if Garbage Collection works properly. * It allows you to check whether an object is freed by the Garbage * Collector. * * To check whether an object is garbage collected, create an instance of this * class and pass it the reference the object in to be surveilled for GC. * Then, call the method <code>waitForGC()</code> to wait until the object is * garbage collected. * Here is sample code: * <pre><code> * Object obj = new Object(); * GarbageCollectionTestHelper gch = new GarbageCollectionTestHelper(obj); * * obj = null; // enable garbage collection * * if (gch.waitForGC(maxTimeoutMillis)) { * System.out.println("garbage collection ok"); * } else { * System.err.println("garbage collection problem"); * } * </code></pre> * You have to use one instance of this class per reference you want to * surveil. * * This implementation isbased on the java.lang.ref.* classes. * It keeps a PhantomReference to the reference and waits until * the instance has been garbage collected. * @version 0.9.1 * @author Vito Baggiolini */ public class GarbageCollectionTestHelper { private Reference ref; private ReferenceQueue refQ; // to variables needed for cancelling: private volatile boolean cancelFlag = false; private volatile Thread waitingThread; private final static long GC_SLICE_MILLIS = 200; public GarbageCollectionTestHelper(Object refToSurveil) { refQ = new ReferenceQueue(); ref = new PhantomReference(refToSurveil, refQ); } public boolean waitForGC() { waitingThread = Thread.currentThread(); Reference retRef = null; System.gc(); while(true) { try { System.err.println("refQ:waiting to remove()"); //@ retRef = refQ.remove(); System.err.println("refQ:removed()"); //@ break; } catch (InterruptedException ex) { if (cancelFlag) { break; } // ignore and continue continue; } } waitingThread = null; return (retRef != null); } /** * a simple algorithm to wait for GC */ public boolean waitForGC(long maxTimeout) { long startTime = System.currentTimeMillis(); long timeOut = maxTimeout; Reference retRef = null; System.gc(); while(true) { try { retRef = refQ.remove(timeOut); break; } catch (InterruptedException ex) { long delta = System.currentTimeMillis() - startTime; if (delta < maxTimeout) { timeOut = maxTimeout - delta; continue; } } // catch } // while return (retRef != null); } /** * a more sophisticated algorithm to wait for Property Change Events */ public boolean complexWaitForGC(long maxTimeout) { long startTime = System.currentTimeMillis(); long timeOut = maxTimeout; Reference retRef = null; int slices = (int) (maxTimeout/GC_SLICE_MILLIS); System.err.println("waiting for " + slices + " slices"); //@ for (int ix=0; ix< slices; ix++) { System.gc(); System.err.println("sleeping for " + GC_SLICE_MILLIS); //@ try { Thread.currentThread().sleep(GC_SLICE_MILLIS); } catch (InterruptedException ex) { ex.printStackTrace(); } retRef = refQ.poll(); if (retRef != null) { return true; } } // while return (retRef != null); } public void cancel() { cancelFlag = true; waitingThread.interrupt(); } }