JAL-3597 slight tidying of code and Javadoc, passmark now heap <= 38MB
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Mon, 20 Jul 2020 08:17:29 +0000 (09:17 +0100)
committerBen Soares <b.soares@dundee.ac.uk>
Thu, 13 Aug 2020 22:08:02 +0000 (23:08 +0100)
test/jalview/gui/FreeUpMemoryTest.java

index 277c27e..09ee560 100644 (file)
@@ -1,17 +1,8 @@
 package jalview.gui;
 
-import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 
-import jalview.analysis.AlignmentGenerator;
-import jalview.bin.Cache;
-import jalview.bin.Jalview;
-import jalview.datamodel.AlignmentI;
-import jalview.datamodel.SequenceGroup;
-import jalview.io.DataSourceType;
-import jalview.io.FileLoader;
-
 import java.awt.event.MouseEvent;
 import java.io.File;
 import java.io.IOException;
@@ -20,6 +11,13 @@ import java.io.PrintStream;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
+import jalview.analysis.AlignmentGenerator;
+import jalview.bin.Cache;
+import jalview.bin.Jalview;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.SequenceGroup;
+import jalview.io.DataSourceType;
+import jalview.io.FileLoader;
 import junit.extensions.PA;
 
 /**
@@ -48,7 +46,7 @@ import junit.extensions.PA;
  * <li>Tips:</li>
  * <ul>
  * <li>Perform GC from the Monitor view in visualvm before requesting the heap
- * dump</li>
+ * dump - test failure might be simply a delay to GC</li>
  * <li>View 'Objects' and filter classes to {@code jalview}. Sort columns by
  * Count, or Size, and look for anything suspicious. For example, if the object
  * count for {@code Sequence} is non-zero (it shouldn't be), pick any instance,
@@ -89,6 +87,11 @@ public class FreeUpMemoryTest
 {
   private static final int ONE_MB = 1000 * 1000;
 
+  /*
+   * maximum retained heap usage (in MB) for a passing test
+   */
+  private static int MAX_RESIDUAL_HEAP = 38;
+
   /**
    * Configure (read-only) Jalview property settings for test
    */
@@ -109,8 +112,6 @@ public class FreeUpMemoryTest
   @Test(groups = "Memory")
   public void testFreeMemoryOnClose() throws IOException
   {
-    long expectedMin = 37L;
-
     File f = generateAlignment();
     f.deleteOnExit();
 
@@ -118,15 +119,22 @@ public class FreeUpMemoryTest
 
     Desktop.instance.closeAll_actionPerformed(null);
 
-    checkUsedMemory(expectedMin);
+    checkUsedMemory(MAX_RESIDUAL_HEAP);
   }
 
-  private static long getUsedMemory()
+  /**
+   * Returns the current total used memory (available memory - free memory),
+   * rounded down to the nearest MB
+   * 
+   * @return
+   */
+  private static int getUsedMemory()
   {
-    long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB;
-    long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB;
+    long availableMemory = Runtime.getRuntime().totalMemory();
+    long freeMemory = Runtime.getRuntime().freeMemory();
     long usedMemory = availableMemory - freeMemory;
-    return usedMemory;
+
+    return (int) (usedMemory/ ONE_MB);
   }
 
   /**
@@ -135,43 +143,38 @@ public class FreeUpMemoryTest
    * 
    * @param expectedMax
    */
-  protected void checkUsedMemory(long expectedMax)
+  protected void checkUsedMemory(int expectedMax)
   {
     /*
-     * request garbage collection and wait for it to run;
+     * request garbage collection and wait for it to run (up to 3 times);
      * NB there is no guarantee when, or whether, it will do so
-     * wait time depends on JRE/processor, generous allowance here  
-     */
-    System.gc();
-    waitFor(1500);
-
-    /*
-     * a second gc() call should not be necessary - but it is!
-     * the test passes with it, and fails without it
-     */
-    System.gc();
-    waitFor(1500);
-
-    /*
-     * check used memory is 'reasonably low'
      */
-    long usedMemory = getUsedMemory();
-    /*
-     * sanity check - fails if any frame was added after
-     * closeAll_actionPerformed
-     */
-    assertEquals(Desktop.instance.getAllFrames().length, 0);
+    long usedMemory = 0L;
+    int gcCount = 0;
+    while (gcCount < 3)
+    {
+      gcCount++;
+      System.gc();
+      waitFor(1500);
+      usedMemory = getUsedMemory();
+      if (usedMemory < expectedMax)
+      {
+        break;
+      }
+    }
 
     /*
-     * if this assertion fails
-     * - set a breakpoint here
-     * - run jvisualvm to inspect a heap dump of Jalview
-     * - identify large objects in the heap and their referers
+     * if this assertion fails (reproducibly!)
+     * - set a breakpoint here, conditional on (usedMemory > expectedMax)
+     * - run VisualVM to inspect the heap usage, and run GC from VisualVM to check 
+     *   it is not simply delayed garbage collection causing the test failure 
+     * - take a heap dump and identify large objects in the heap and their referers
      * - fix code as necessary to null the references on close
      */
-    System.out.println("Used memory after gc = " + usedMemory
-            + "MB (should be <" + expectedMax + ")");
-    assertTrue(usedMemory < expectedMax, String.format(
+    System.out
+            .println("Used memory after " + gcCount + " call(s) to gc() = "
+                    + usedMemory + "MB (should be <=" + expectedMax + ")");
+    assertTrue(usedMemory <= expectedMax, String.format(
             "Used memory %d should be less than %d (Recommend running test manually to verify)",
             usedMemory, expectedMax));
   }