/** *BasictestofWeakReferenceswithlargeamountsofmemorythat'sonlyreachablethrough *finalizers.Alsomakessurethatfinalizer-reachabledataisnotcollected. *CaneasilybemodifiedtotimeReference.get()blocking.
*/ publicclass Main { staticfinalboolean PRINT_TIMES = false; // true will cause benchmark failure. // Data structures repeatedly allocated in background to trigger GC. // Size of finalizer reachable trees. staticfinalint TREE_HEIGHT = 15; // Trees contain 2^TREE_HEIGHT -1 allocated objects. // Number of finalizable tree-owning objects that exist at one point. staticfinalint N_RESURRECTING_OBJECTS = 10; // Number of short-lived, not finalizer-reachable, objects allocated between trees. staticfinalint N_PLAIN_OBJECTS = 20_000; // Number of SoftReferences to CBTs we allocate. staticfinalint N_SOFTREFS = 10;
// Large array of WeakReferences filled and accessed by tests below.
ArrayList<WeakReference<Integer>> weakRefs = new ArrayList<>(NREFS);
/** *Completebinarytreedatastructure.make(n)takesO(2^n)space.
*/ staticclass CBT {
CBT left;
CBT right;
CBT(CBT l, CBT r) {
left = l;
right = r;
} static CBT make(int n) { if (n == 0) { returnnull;
} returnnew CBT(make(n - 1), make(n - 1));
} /** *Checkthatpathdescribedbybit-vectorpathhasthecorrectlength.
*/ void check(int n, int path) {
CBT current = this; for (int i = 0; i < n; i++, path = path >>> 1) { // Unexpectedly short paths result in NPE. if ((path & 1) == 0) {
current = current.left;
} else {
current = current.right;
}
} if (current != null) {
System.out.println("Complete binary tree path too long");
}
}
}
/** *AfinalizableobjectthatreferstoO(2^TREE_HEIGHT)otherwiseunreachablememory. *Whenfinalized,itcreatesanewidenticalobject,makingsurethatonealwaysstays *around.
*/ staticclass ResurrectingObject {
CBT stuff;
ResurrectingObject() {
stuff = CBT.make(TREE_HEIGHT);
} static ResurrectingObject a[] = new ResurrectingObject[2]; staticint i = 0; staticsynchronizedvoid allocOne() {
a[(++i) % 2] = new ResurrectingObject(); // Check the previous one to make it hard to optimize anything out. if (i > 1) {
a[(i + 1) % 2].stuff.check(TREE_HEIGHT, i /* weirdly interpreted as path */);
}
} protectedvoid finalize() {
stuff.check(TREE_HEIGHT, 42/* Some path descriptor */); // Allocate a new one to replace this one.
allocOne();
}
}
void fillWeakRefs() { for (int i = 0; i < NREFS; ++i) {
weakRefs.add(null);
}
}
/* *ReturnmaximumobservedtimeinnanostodereferenceaWeakReferencetoanunreachable *object.weakRefsispresumedtobepre-filledtohavethecorrectsize.
*/ long timeUnreachableInner() { long maxNanos = 0; // Fill weakRefs with WeakReferences to unreachable integers, a batch at a time. // Then time and test .get() calls on carefully sampled array entries, some of which // will have been cleared. for (int i = 0; i < NBATCHES; ++i) { for (int j = 0; j < REFS_PER_BATCH; ++j) {
weakRefs.set(i * REFS_PER_BATCH + j, new WeakReference(new Integer(i * REFS_PER_BATCH + j)));
} try { Thread.sleep(50);
} catch (InterruptedException e) {
System.out.println("Unexpected exception");
} // Iterate over the filled-in section of weakRefs, but look only at a subset of the // elements, making sure the subsets for different top-level iterations are disjoint. // Otherwise the get() calls here will extend the lifetimes of the referents, and we // may never see any cleared WeakReferences. for (int j = (i + 1) * REFS_PER_BATCH - i - 1; j >= 0; j -= NBATCHES) {
WeakReference<Integer> wr = weakRefs.get(j); if (wr != null) { long startNanos = System.nanoTime();
Integer referent = wr.get(); long totalNanos = System.nanoTime() - startNanos; if (referent == null) { // Optimization to reduce max space use and scanning time.
weakRefs.set(j, null);
}
maxNanos = Math.max(maxNanos, totalNanos); if (referent != null && referent.intValue() != j) {
System.out.println("Unexpected referent; expected " + j + " got "
+ referent.intValue());
}
}
}
} return maxNanos;
}
/* *Wrapperfortheabovethatalsochecksthatreferenceswerereclaimed. *Wedothisseparatelytomakesureanystackreferencesfromthecoreofthe *testaregone.Empirically,weotherwisesometimesseethezerothWeakReference *notreclaimed.
*/ long timeUnreachable() { long maxNanos = timeUnreachableInner();
Runtime.getRuntime().gc();
System.runFinalization(); // Presumed to wait for reference clearing. for (int i = 0; i < NREFS; ++i) { if (weakRefs.get(i) != null && weakRefs.get(i).get() != null) {
System.out.println("WeakReference to " + i + " wasn't cleared");
}
} return maxNanos;
}
/** *ReturnmaximumobservedtimeinnanostodereferenceaWeakReferencetoareachable *object.OverwritesweakRefs,whichispresumedtohaveNREFSentriesalready.
*/ long timeReachable() { long maxNanos = 0; // Similar to the above, but we use WeakReferences to otherwise reachable objects, // which should thus not get cleared.
Integer[] strongRefs = new Integer[NREFS]; for (int i = 0; i < NBATCHES; ++i) { for (int j = i * REFS_PER_BATCH; j < (i + 1) * REFS_PER_BATCH; ++j) {
Integer newObj = new Integer(j);
strongRefs[j] = newObj;
weakRefs.set(j, new WeakReference(newObj));
} for (int j = (i + 1) * REFS_PER_BATCH - 1; j >= 0; --j) {
WeakReference<Integer> wr = weakRefs.get(j); long startNanos = System.nanoTime();
Integer referent = wr.get(); long totalNanos = System.nanoTime() - startNanos;
maxNanos = Math.max(maxNanos, totalNanos); if (referent == null) {
System.out.println("Unexpectedly cleared referent at " + j);
} elseif (referent.intValue() != j) {
System.out.println("Unexpected reachable referent; expected " + j + " got "
+ referent.intValue());
}
}
}
Reference.reachabilityFence(strongRefs); return maxNanos;
}
void runTest() {
System.out.println("Starting");
fillWeakRefs(); long unreachableNanos = timeUnreachable(); if (PRINT_TIMES) {
System.out.println("Finished timeUnrechable()");
} long reachableNanos = timeReachable();
String unreachableMillis =
String. format("%,.3f", ((double) unreachableNanos) / 1_000_000);
String reachableMillis =
String. format("%,.3f", ((double) reachableNanos) / 1_000_000); if (PRINT_TIMES) {
System.out.println( "Max time for WeakReference.get (unreachable): " + unreachableMillis);
System.out.println( "Max time for WeakReference.get (reachable): " + reachableMillis);
} // Only report extremely egregious pauses to avoid spurious failures. if (unreachableNanos > 10_000_000_000L) {
System.out.println("WeakReference.get (unreachable) time unreasonably long");
} if (reachableNanos > 10_000_000_000L) {
System.out.println("WeakReference.get (reachable) time unreasonably long");
}
}
/** *AllocateandGCalot,whilekeepingsignificantamountsoffinalizerand *SoftReference-reachablememoryaround.
*/ static Runnable allocFinalizable = new Runnable() { publicvoid run() { // Allocate and drop some finalizable objects that take a long time // to mark. Designed to be hard to optimize away. Each of these objects will // build a new one in its finalizer before really going away.
ArrayList<SoftReference<CBT>> softRefs = new ArrayList<>(N_SOFTREFS); for (int i = 0; i < N_SOFTREFS; ++i) { // These should not normally get reclaimed, since we shouldn't run out of // memory. They do increase tracing time.
softRefs.add(new SoftReference(CBT.make(TREE_HEIGHT)));
} for (int i = 0; i < N_RESURRECTING_OBJECTS; ++i) {
ResurrectingObject.allocOne();
}
BigInteger counter = BigInteger.ZERO; for (int i = 1; !pleaseStop; ++i) { // Allocate a lot of short-lived objects, using BigIntegers to minimize the chance // of the allocation getting optimized out. This makes things slightly more // realistic, since not all objects will be finalizer reachable. for (int j = 0; j < N_PLAIN_OBJECTS / 2; ++j) {
counter = counter.add(BigInteger.TEN);
} // Look at counter to reduce chance of optimizing out the allocation. if (counter.longValue() % 10 != 0) {
System.out.println("Bad allocFinalizable counter value: " + counter);
} // Explicitly collect here, mostly to prevent heap growth. Otherwise we get // ahead of the GC and eventually block on it.
Runtime.getRuntime().gc(); if (PRINT_TIMES && i % 100 == 0) {
System.out.println("Collected " + i + " times");
}
} // To be safe, access softRefs. final CBT sample = softRefs.get(N_SOFTREFS / 2).get(); if (sample != null) {
sample.check(TREE_HEIGHT, 47/* some path descriptor */);
}
}
};
publicstaticvoid main(String[] args) throws Exception {
Main theTest = new Main(); Thread allocThread = null; if (BACKGROUND_GC_THREAD) {
allocThread = newThread(allocFinalizable);
allocThread.setDaemon(true); // Terminate if main thread dies.
allocThread.start();
}
theTest.runTest(); if (BACKGROUND_GC_THREAD) {
pleaseStop = true;
allocThread.join();
}
System.out.println("Finished");
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.