3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertNotNull;
5 import static org.testng.Assert.assertTrue;
7 import jalview.analysis.AlignmentGenerator;
8 import jalview.bin.Cache;
9 import jalview.bin.Jalview;
10 import jalview.datamodel.AlignmentI;
11 import jalview.datamodel.SequenceGroup;
12 import jalview.io.DataSourceType;
13 import jalview.io.FileLoader;
15 import java.awt.event.MouseEvent;
17 import java.io.IOException;
18 import java.io.PrintStream;
20 import org.testng.annotations.BeforeClass;
21 import org.testng.annotations.Test;
23 import junit.extensions.PA;
25 public class FreeUpMemoryTest
27 private static final int ONE_MB = 1000 * 1000;
30 * Configure (read-only) Jalview property settings for test
32 @BeforeClass(alwaysRun = true)
35 Jalview.main(new String[] { "-nonews", "-props",
36 "test/jalview/testProps.jvprops" });
37 String True = Boolean.TRUE.toString();
38 Cache.setPropertyNoSave("SHOW_ANNOTATIONS", True);
39 Cache.setPropertyNoSave("SHOW_QUALITY", True);
40 Cache.setPropertyNoSave("SHOW_CONSERVATION", True);
41 Cache.setPropertyNoSave("SHOW_OCCUPANCY", True);
42 Cache.setPropertyNoSave("SHOW_IDENTITY", True);
46 * A simple test that memory is released when all windows are closed.
48 * <li>generates a reasonably large alignment and loads it</li>
49 * <li>performs various operations on the alignment</li>
50 * <li>closes all windows</li>
51 * <li>requests garbage collection</li>
52 * <li>asserts that the remaining memory footprint (heap usage) is 'not large'
55 * If the test fails, this suggests that a reference to some large object
56 * (perhaps the alignment data, or some annotation / Tree / PCA data) has
57 * failed to be garbage collected. If this is the case, the heap will need to
58 * be inspected manually (suggest using jvisualvm) in order to track down
59 * where large objects are still referenced. The code (for example
60 * AlignmentViewport.dispose()) should then be updated to ensure references to
61 * large objects are set to null when they are no longer required.
65 @Test(groups = "Memory")
66 public void testFreeMemoryOnClose() throws IOException
68 File f = generateAlignment();
71 long expectedMin = 35L;
72 long usedMemoryAtStart=getUsedMemory();
73 if (usedMemoryAtStart>expectedMin)
75 System.err.println("used memory before test is "+usedMemoryAtStart+" > "+expectedMin+"MB .. adjusting minimum.");
76 expectedMin = usedMemoryAtStart;
80 Desktop.getInstance().closeAll_actionPerformed(null);
82 checkUsedMemory(expectedMin);
85 private static long getUsedMemory()
87 long availableMemory = Runtime.getRuntime().totalMemory() / ONE_MB;
88 long freeMemory = Runtime.getRuntime().freeMemory() / ONE_MB;
89 long usedMemory = availableMemory - freeMemory;
93 * Requests garbage collection and then checks whether remaining memory in use
94 * is less than the expected value (in Megabytes)
98 protected void checkUsedMemory(long expectedMax)
101 * request garbage collection and wait for it to run;
102 * NB there is no guarantee when, or whether, it will do so
103 * wait time depends on JRE/processor, generous allowance here
109 * a second gc() call should not be necessary - but it is!
110 * the test passes with it, and fails without it
116 * check used memory is 'reasonably low'
118 long usedMemory = getUsedMemory();
120 * sanity check - fails if any frame was added after
121 * closeAll_actionPerformed
123 assertEquals(Desktop.getInstance().getAllFrames().length, 0);
126 * if this assertion fails
127 * - set a breakpoint here
128 * - run jvisualvm to inspect a heap dump of Jalview
129 * - identify large objects in the heap and their referers
130 * - fix code as necessary to null the references on close
132 System.out.println("Used memory after gc = " + usedMemory + "MB");
133 assertTrue(usedMemory < expectedMax, String.format(
134 "Used memory %d should be less than %d (Recommend running test manually to verify)",
140 * Loads an alignment from file and exercises various operations in Jalview
144 protected void doStuffInJalview(File f)
147 * load alignment, wait for consensus and other threads to complete
149 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(f.getPath(),
150 DataSourceType.FILE);
151 while (af.getViewport().isCalcInProgress())
157 * open an Overview window
159 af.overviewMenuItem_actionPerformed(null);
160 assertNotNull(af.alignPanel.overviewPanel);
163 * exercise the pop-up menu in the Overview Panel (JAL-2864)
165 Object[] args = new Object[] {
166 new MouseEvent(af, 0, 0, 0, 0, 0, 1, true) };
167 PA.invokeMethod(af.alignPanel.overviewPanel,
168 "showPopupMenu(java.awt.event.MouseEvent)", args);
171 * set a selection group - potential memory leak if it retains
172 * a reference to the alignment
174 SequenceGroup sg = new SequenceGroup();
177 AlignmentI al = af.viewport.getAlignment();
178 for (int i = 0; i < al.getHeight(); i++)
180 sg.addSequence(al.getSequenceAt(i), false);
182 af.viewport.setSelectionGroup(sg);
185 * compute Tree and PCA (on all sequences, 100 columns)
187 af.openTreePcaDialog();
188 CalculationChooser dialog = af.alignPanel.getCalculationDialog();
189 dialog.openPcaPanel("BLOSUM62", dialog.getSimilarityParameters(true));
190 dialog.openTreePanel("BLOSUM62", dialog.getSimilarityParameters(false));
193 * wait until Tree and PCA have been computed
195 while (af.viewport.getCurrentTree() == null
196 && dialog.getPcaPanel().isWorking())
202 * give Swing time to add the PCA panel (?!?)
208 * Wait for waitMs miliseconds
212 protected void waitFor(int waitMs)
216 Thread.sleep(waitMs);
217 } catch (InterruptedException e)
223 * Generates an alignment and saves it in a temporary file, to be loaded by
224 * Jalview. We use a peptide alignment (so Conservation and Quality are
225 * calculated), which is wide enough to ensure Consensus, Conservation and
226 * Occupancy have a significant memory footprint (if not removed from the
230 * @throws IOException
232 private File generateAlignment() throws IOException
234 File f = File.createTempFile("MemoryTest", "fa");
235 PrintStream ps = new PrintStream(f);
236 AlignmentGenerator ag = new AlignmentGenerator(false, ps);
239 ag.generate(width, height, 0, 10, 15);