package jalview.gui; import static org.testng.Assert.assertTrue; import jalview.analysis.AlignmentGenerator; import jalview.bin.Cache; import jalview.bin.Jalview; import jalview.io.DataSourceType; import jalview.io.FileLoader; import java.io.File; import java.io.IOException; import java.io.PrintStream; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; public class FreeUpMemoryTest { private static final int ONE_MB = 1000 * 1000; /** * Configure (read-only) Jalview property settings for test */ @BeforeClass(alwaysRun = true) public void setUp() { Jalview.main(new String[] { "-nonews", "-props", "test/jalview/testProps.jvprops" }); Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", Boolean.TRUE.toString()); Cache.applicationProperties.setProperty("SHOW_QUALITY", Boolean.TRUE.toString()); Cache.applicationProperties.setProperty("SHOW_CONSERVATION", Boolean.TRUE.toString()); Cache.applicationProperties.setProperty("SHOW_OCCUPANCY", Boolean.TRUE.toString()); Cache.applicationProperties.setProperty("SHOW_IDENTITY", Boolean.TRUE.toString()); } /** * A simple test that memory is released when all windows are closed. * * If the test fails, this suggests that a reference to some large object * (perhaps the alignment data, or consensus profile) has failed to be garbage * collected. If this is the case, the heap will need to be inspected manually * (suggest using jvisualvm) in order to track down where large objects are * still referenced. The code (for example AlignmentViewport.dispose()) should * then be updated to ensure references to large objects are set to null when * they are no longer required. * * @throws IOException */ @Test(groups = "Memory") public void testFreeMemoryOnClose() throws IOException { File f = generateAlignment(); f.deleteOnExit(); /* * load alignment, wait for consensus and other threads to complete */ AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(f.getPath(), DataSourceType.FILE); waitForThreads(af.getViewport()); af.closeMenuItem_actionPerformed(true); /* * request garbage collection and wait briefly for it to run; * NB there is no guarantee when, or whether, it will do so */ System.gc(); synchronized (this) { try { wait(10); } catch (InterruptedException e) { } } /* * a second gc() call should not be necessary - but it is! * the test passes with it, and fails without it */ System.gc(); /* * check used memory is 'reasonably low' */ long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB; long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB; long usedMemory = availableMemory - freeMemory; System.out.println("Memory in use after close all windows: " + usedMemory + "MB"); /* * 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 * - fix code as necessary to null the references on close */ long expectedMax = 30L; // typically reports around 25 assertTrue(usedMemory < expectedMax, String.format( "Used memory %d should be less than %d", usedMemory, expectedMax)); } /** * wait for consensus etc thread to complete * * @param av */ protected void waitForThreads(AlignViewport av) { while (av.isCalcInProgress()) { try { Thread.sleep(200); } catch (Exception x) { } } } /** * Generates an alignment and saves it in a temporary file, to be loaded by * Jalview. We use a peptide alignment (so Conservation and Quality are * calculated), which is wide enough to ensure Consensus, Conservation and * Occupancy have a significant memory footprint (if not removed from the * heap). * * @return * @throws IOException */ private File generateAlignment() throws IOException { File f = File.createTempFile("MemoryTest", "fa"); PrintStream ps = new PrintStream(f); AlignmentGenerator ag = new AlignmentGenerator(false, ps); int width = 100000; int height = 100; ag.generate(width, height, 0, 10, 15); return f; } }