MemoryLeakVerifier.java
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2022 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.test;
/*
* ==================================================================== Taken from Apache POI, with
* a big thanks.
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License. ====================================================================
*/
import static org.junit.jupiter.api.Assertions.assertNull;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* A simple utility class that can verify that objects have been successfully garbage collected.
*
* Usage is something like
*
* private final MemoryLeakVerifier verifier = new MemoryLeakVerifier();
*
* {@literal}After void tearDown() { verifier.assertGarbageCollected(); }
*
* {@literal}Test void someTest() { ... verifier.addObject(object); }
*
*
* This will verify at the end of the test if the object is actually removed by the garbage
* collector or if it lingers in memory for some reason.
*
* Idea taken from http://stackoverflow.com/a/7410460/411846
*/
public class MemoryLeakVerifier {
private static final int MAX_GC_ITERATIONS = 50;
private static final int GC_SLEEP_TIME = 100;
private final List<WeakReference<Object>> references = new ArrayList<>();
public void addObject(Object object) {
references.add(new WeakReference<>(object));
}
/**
* Attempts to perform a full garbage collection so that all weak references will be removed.
* Usually only a single GC is required, but there have been situations where some unused memory
* is not cleared up on the first pass. This method performs a full garbage collection and then
* validates that the weak reference now has been cleared. If it hasn't then the thread will
* sleep for 100 milliseconds and then retry up to 50 more times. If after this the object still
* has not been collected then the assertion will fail.
*
* Based upon the method described in:
* http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
*/
public void assertGarbageCollected() {
assertGarbageCollected(MAX_GC_ITERATIONS);
}
/**
* Used only for testing the class itself where we would like to fail faster than 5 seconds
*
* @param maxIterations The number of times a GC will be invoked until a possible memory leak is
* reported
*/
void assertGarbageCollected(int maxIterations) {
try {
for (WeakReference<Object> ref : references) {
assertGarbageCollected(ref, maxIterations);
}
} catch (InterruptedException e) {
// just ensure that we quickly return when the thread is interrupted
}
}
private static void assertGarbageCollected(WeakReference<Object> ref, int maxIterations)
throws InterruptedException {
Runtime runtime = Runtime.getRuntime();
for (int i = 0; i < maxIterations; i++) {
runtime.runFinalization();
runtime.gc();
if (ref == null || ref.get() == null) {
break;
}
// Pause for a while and then go back around the loop to try again...
// EventQueue.invokeAndWait(Procedure.NoOp); // Wait for the AWT event queue to have
// completed processing
Thread.sleep(GC_SLEEP_TIME);
}
assertNull(ref.get(), "Object should not exist after " + MAX_GC_ITERATIONS
+ " collections, but still had: " + ref.get());
}
}