I'm not quite clear what I'm seeing here.
Currently I'm trying to solve a memory leak in a rather complex application. Below is a typical path-to-GC trace of one of the most present objects; an instance of EclipseLink's ReadAllQuery... Points 10 through 15 also include, before the dash, the variable name holding the reference (copying a trace from Eclipse's Memory Analyzer Tool does not include that info, so I added it manually where relevant):
1. org.eclipse.persistence.queries.ReadAllQuery
2. org.eclipse.persistence.internal.indirection.QueryBasedValueHolder
3. org.eclipse.persistence.internal.indirection.UnitOfWorkQueryValueHolder
4. org.eclipse.persistence.indirection.IndirectList
5. nl.reinders.bm.Address
5. java.lang.Object[]
7. java.util.Vector
8. org.eclipse.persistence.indirection.IndirectList
9. nl.reinders.bm.Relation
10. *[24]* - java.lang.Object[]
11.
elementData - java.util.ArrayList
12.
iEntities - org.tbee.swing.jpa.JpaEntitySearchResultTableModel
13.
tableModel - org.tbee.swing.table.TableSorter
14.
val$lJpaEntitySearchResultTableModel - org.tbee.swing.jpa.JpaEntitySearchBuilder$11
15. *[3]* - java.lang.Object[]
16. javax.swing.event.EventListenerList
17. javax.swing.JButton
A quick walkthrough of the list above:
1. to 9. are references inside my business model, not relevant
10. to 12. an arraylist in the JpaEntitySearchResultTableModel holding a number of references to multiple Relation-entities a.o. this one
13. a TableSorter like we all know and love
14. ahhhh, this we will be getting back to
15.-17. The event listeners registered to a JButton
Let me show the code for point 13. to 17, where I've filtered out for now what I consider static:
protected List<T> searchDialog(Component component, String title, final int selectMode, final List<T> returnSearchResult)
{
// prep the dialog
final JDialog lJDialog = new JDialog(component, title, Dialog.ModalityType.DOCUMENT_MODAL );
lJDialog.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE );
// return value
final List<T> lList = new ArrayList<T>();
// result table
final JpaEntitySearchResultTableModel<T> lJpaEntitySearchResultTableModel = new JpaEntitySearchResultTableModel<T>( getEntityClass() ); // must be done before the setModel
final TableSorter lTableSorter = new TableSorter(lJpaEntitySearchResultTableModel);
final JpaEntitySearchResultTable lJpaEntitySearchResultTable = new JpaEntitySearchResultTable();
lTableSorter.setTable(lJpaEntitySearchResultTable);
lJpaEntitySearchResultTable.setModel(lTableSorter);
lJpaEntitySearchResultTable.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); // we allow always multiselect so the result can be copied
...
final JButton lOkButton = new JButton( Internationalization.get().translate(this, "ok"), new ImageIcon(JpaObjectNavigatorBar.class.getResource("icon_ok.png")) );
lOkButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e)
{
int[] lSelectedRows = lJpaEntitySearchResultTable.getSelectedRows();
for (int lIdx = 0; lIdx < lSelectedRows.length; lIdx++)
{
T lEntity = lJpaEntitySearchResultTableModel.getEntity( lTableSorter.convertRowIndexToModel( lSelectedRows[lIdx] ) );
lList.add( lEntity );
}
// optionally return the whole SearchResult not only what was selected
if (returnSearchResult != null)
{
returnSearchResult.clear();
for (int lIdx = 0; lIdx < lJpaEntitySearchResultTableModel.getRowCount(); lIdx++)
{
T lEntity = lJpaEntitySearchResultTableModel.getEntity( lTableSorter.convertRowIndexToModel( lIdx ) );
returnSearchResult.add( lEntity );
}
}
// hide the dialog (thus allowing the execution to continue below)
lJDialog.setVisible(false);
}});
...
lJDialog.setVisible(true); // thread is blocked here until the dialog is closed
// now return the selected values
return lList;
}
There are a number of things to notice:
A. a final list parameter: returnSearchResult
B. a final local JDialog
C. a final local variable: lList
D. a final local variable of a special TableModel: lJpaEntitySearchResultTableModel
E. the handling of a button copying data from the: lJpaEntitySearchResultTableModel to the local list (which is returned later on) and also optionally into the final parameter
Now back to the memory trace. I'm experiencing unexpected increases in memory usage. The dialog can be opened 10 times without a problem and then all of a suddent the Nth time the memory increases with like 50MB. At the time the dump was made the dialog was not visible, which means as much as that the method above is not executed. I used jconsole to first do a GC and then write a hprof file.
The trace continues up all the way to the JDialog.
How can it be possible that a reference is being held by a local final variable as indicated by 14 in the path-to-GC? It says that lJpaEntitySearchResultTableModel holds a reference via the TableModel to my business entities. Pausing the VM in eclipse shows there is no thread inside that method!
How can it be the JDialog is still present in the memory trace? The jdialog should have been disposed.