HHH-16911 Ensure the PhantomReference doesn't get collected before having had a change to trigger
This commit is contained in:
parent
d415b5f0d2
commit
1bc6b11ec5
|
@ -21,7 +21,7 @@ import org.junit.Assert;
|
||||||
*
|
*
|
||||||
* @author Sanne Grinovero (C) 2023 Red Hat Inc.
|
* @author Sanne Grinovero (C) 2023 Red Hat Inc.
|
||||||
*/
|
*/
|
||||||
public class PhantomReferenceLeakDetector {
|
public final class PhantomReferenceLeakDetector {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single second should be more than enough; this might need
|
* A single second should be more than enough; this might need
|
||||||
|
@ -34,6 +34,7 @@ public class PhantomReferenceLeakDetector {
|
||||||
*/
|
*/
|
||||||
private static final int MAX_TOTAL_WAIT_SECONDS = 180;
|
private static final int MAX_TOTAL_WAIT_SECONDS = 180;
|
||||||
private static final int GC_ATTEMPTS = MAX_TOTAL_WAIT_SECONDS * 5;
|
private static final int GC_ATTEMPTS = MAX_TOTAL_WAIT_SECONDS * 5;
|
||||||
|
private static PhantomReference hold;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts that a certain operation won't be leaking
|
* Asserts that a certain operation won't be leaking
|
||||||
|
@ -77,11 +78,35 @@ public class PhantomReferenceLeakDetector {
|
||||||
T criticalReference = action.get();
|
T criticalReference = action.get();
|
||||||
final ReferenceQueue<T> referenceQueue = new ReferenceQueue<>();
|
final ReferenceQueue<T> referenceQueue = new ReferenceQueue<>();
|
||||||
final PhantomReference<T> reference = new PhantomReference<>( criticalReference, referenceQueue );
|
final PhantomReference<T> reference = new PhantomReference<>( criticalReference, referenceQueue );
|
||||||
|
holdOnTo( reference );
|
||||||
//Ignore IDE's suggestion to remove the following line: we really need it!
|
//Ignore IDE's suggestion to remove the following line: we really need it!
|
||||||
// (it could be inlined above, but I prefer this style so that it serves as an example for
|
// (it could be inlined above, but I prefer this style so that it serves as an example for
|
||||||
// future maintenance of how this works)
|
// future maintenance of how this works)
|
||||||
criticalReference = null;
|
criticalReference = null;
|
||||||
return verifyCollection( referenceQueue, gcAttempts, totalWaitSeconds );
|
try {
|
||||||
|
return verifyCollection( referenceQueue, gcAttempts, totalWaitSeconds );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
clearHeldReferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void clearHeldReferences() {
|
||||||
|
PhantomReferenceLeakDetector.hold = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure we keep a reference to the PhantomReference until we're done
|
||||||
|
* with the test, otherwise it might get collected before having had
|
||||||
|
* a change to trigger; this seems to behave differently on different
|
||||||
|
* JVMs but is most likely just a timing issue.
|
||||||
|
* @param reference
|
||||||
|
*/
|
||||||
|
private static synchronized void holdOnTo(PhantomReference reference) {
|
||||||
|
if ( PhantomReferenceLeakDetector.hold != null ) {
|
||||||
|
throw new IllegalStateException( "Detected recursive use of PhantomReferenceLeakDetector, which is not supported, or possibly a leaked previous run" );
|
||||||
|
}
|
||||||
|
PhantomReferenceLeakDetector.hold = reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> boolean verifyCollection(final ReferenceQueue<T> referenceQueue, final int gcAttempts, final int totalWaitSeconds) {
|
private static <T> boolean verifyCollection(final ReferenceQueue<T> referenceQueue, final int gcAttempts, final int totalWaitSeconds) {
|
||||||
|
|
Loading…
Reference in New Issue