You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MemoryLeakVerifier.java 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.util;
  16. import static org.junit.jupiter.api.Assertions.assertNull;
  17. import java.lang.ref.WeakReference;
  18. import java.util.ArrayList;
  19. import java.util.List;
  20. /**
  21. * A simple utility class that can verify that objects have been successfully garbage collected.
  22. *
  23. * Usage is something like
  24. *
  25. * private final MemoryLeakVerifier verifier = new MemoryLeakVerifier();
  26. {@literal}After
  27. void tearDown() {
  28. verifier.assertGarbageCollected();
  29. }
  30. {@literal}Test
  31. void someTest() {
  32. ...
  33. verifier.addObject(object);
  34. }
  35. *
  36. * This will verify at the end of the test if the object is actually removed by the
  37. * garbage collector or if it lingers in memory for some reason.
  38. */
  39. public class MemoryLeakVerifier {
  40. private static final int MAX_GC_ITERATIONS = 50;
  41. private static final int GC_SLEEP_TIME = 100;
  42. private final List<WeakReference<Object>> references = new ArrayList<>();
  43. public MemoryLeakVerifier() {
  44. }
  45. public void addObject(Object object) {
  46. references.add(new WeakReference<>(object));
  47. }
  48. /**
  49. * Attempts to perform a full garbage collection so that all weak references will be removed. Usually only
  50. * a single GC is required, but there have been situations where some unused memory is not cleared up on the
  51. * first pass. This method performs a full garbage collection and then validates that the weak reference
  52. * now has been cleared. If it hasn't then the thread will sleep for 100 milliseconds and then retry up to
  53. * 50 more times. If after this the object still has not been collected then the assertion will fail.
  54. *
  55. * Based upon the method described in: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
  56. */
  57. public void assertGarbageCollected() {
  58. assertGarbageCollected(MAX_GC_ITERATIONS);
  59. }
  60. /**
  61. * Used only for testing the class itself where we would like to fail faster than 5 seconds
  62. * @param maxIterations The number of times a GC will be invoked until a possible memory leak is reported
  63. */
  64. void assertGarbageCollected(int maxIterations) {
  65. try {
  66. for(WeakReference<Object> ref : references) {
  67. assertGarbageCollected(ref, maxIterations);
  68. }
  69. } catch (InterruptedException e) {
  70. // just ensure that we quickly return when the thread is interrupted
  71. }
  72. }
  73. private static void assertGarbageCollected(WeakReference<Object> ref, int maxIterations) throws InterruptedException {
  74. Runtime runtime = Runtime.getRuntime();
  75. for (int i = 0; i < maxIterations; i++) {
  76. runtime.runFinalization();
  77. runtime.gc();
  78. if (ref.get() == null)
  79. break;
  80. // Pause for a while and then go back around the loop to try again...
  81. //EventQueue.invokeAndWait(Procedure.NoOp); // Wait for the AWT event queue to have completed processing
  82. Thread.sleep(GC_SLEEP_TIME);
  83. }
  84. assertNull(ref.get(), "Object should not exist after " + MAX_GC_ITERATIONS + " collections, but still had: " + ref.get());
  85. }
  86. }