Swing's memory model was the protagonist of a
bug that made in fact unusable an application developed with the NetBeans
Platform. The scenario was the same of all memory leaks,
the application that slows down with the Java Heap that grows fast, but we was
unable to come to a solution because a static analisys of our code confirmed
that we have no loss or circular references that can produce such
problems.
At this point we have used a profiler and
discovered that all the memory was "held" by a components of a graphics library
to draw Gantt. In practice, even after this component was removed from our
container (a TopComponent of NetBeans), a variety of data structures of Swing
made it still strongly reachable by the GC and then "Not Eligible" for Garbage
Collection. These include:
In this case make our listeners "Weak
References" was not enough to keep clean our application.
We have thus achieved a minimal test case to try
to restrict the full problem (and also to make sure that the NetBeans Platform
does not enter into play ), consisting of a JFrame containing a DualGanttChart
and a button that re-creates the whole. The interesting part of the code
is:
final JFrame frame = new JFrame(); JButton button = new JButton("Reload"); button.addActionListener(new WeakRefActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { frame.setVisible(false); SwingUtilities.invokeLater(LayerDemo.this); } }));
frame.add(BorderLayout.NORTH, button); frame.setSize(640, 480); frame.setTitle("GANTT Chart Demo");
frame.add(BorderLayout.CENTER, gc);
setFrame(frame); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setVisible(true); This application shows a Gantt, and when you
click the button "Reload" closes the JFrame and creates another (this code is in the
run() method ). This simple application can perfectly reproduce the problem of
the memory leak, eating a few megabytes at every reload.
Unfortunately, the data structures mentioned
above are all internal components for Swing, not accessible to the application
code, and therefore managed by the framework, which is designed to not consider
a memory leak retention of objects that can be overwritten by future
components.
Let me explain better.
Suppose you have a JFrame that contains a panel, at some point for some
reasons we make the JFrame
invisible, removing the panel because it consumes a lot of memory. Swing
in this case, regardless of our decision whether or not to remove content,
maintains a reference to the panel in the"temporaryLostComponent" which will not be
overwritten until there will be a new Component into the JFrame.
And this is just one of the retentions, the list
above shows others, like the worst KeyboardFocusManager.
What solution to adopt if you can not manually
reset the reference? There are those
who wrote the code to use reflection and writing regardless of
visibility, but before arriving at a so drastic solutions we wanted to be
sure there were no other "official" ways..... and fortunately Sun hasn't left us
to admire inert Swing that retains our references, but has written a method in
"KeyBoardFocusManager"
that do exactly what we want, the "clearGlobalFocusOwner".
We don't know how this method can do it (finally
calls a native method of a peer object), but does his work and the retention
disappears completely! How we found it? Thanks to its name, as we wanted to
clear "*focusOwners" into "KeyBoardFocusManager" and the name "clearGlobalFocusOwner" was an
excellent candidate.
So to avoid memory leak we refactored the code
above to include this call:
........... ........... ........... button.addActionListener(new WeakRefActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) { frame.setVisible(false); KeyboardFocusManager. getCurrentKeyboardFocusManager().clearGlobalFocusOwner(); SwingUtilities.invokeLater(LayerDemo.this); } })); ........... ........... ........... Running the profiler again, we verified that in
fact all Swing's internal references was cleared and the problem actually
solved. |
Home page > Java in general... >