| /* ==================================================================== |
| 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. |
| ==================================================================== */ |
| |
| package org.apache.poi.util; |
| |
| 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 MemoryLeakVerifier() { |
| } |
| |
| 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.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()); |
| } |
| } |