JDK-4099615 : awt frames leak memory
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.1.3,1.1.7,1.2.2
  • Priority: P3
  • Status: Closed
  • Resolution: Not an Issue
  • OS: solaris_2.5.1,windows_nt
  • CPU: x86,sparc
  • Submitted: 1997-12-16
  • Updated: 2013-11-01
  • Resolved: 1999-05-26
Related Reports
Duplicate :  
Description

Name: diC59631			Date: 12/16/97


We realised that the garbage collector on different virtual machines does not work 
correctly with AWT-classes under Windows NT 4.0.
Memory recources occupied from AWT-classes are not totally recollected.

Following code fragment reproduces this error with the frame class. Running this
code fragment increases the size of the Virtual memory and the size of memory 
address-space of the virtual machine with several MBs!!!
It is easy to lock the whole System with this more complex frames.

for (int i=0;i<1000;i++)
{
    {
        Frame Obj=(new Frame());
        Obj=(new Frame());
        Obj.reshape(50,100,200,200);
        Obj.show();
        Obj.hide();
        Obj.dispose();
     }
     System.gc();
};

We tried several virtual machines from different vendors, but none worked. including:

Java Workshop 1.0
Symantec Visual Cafe Pro Version 1.0 till 1.0e
Symantec Visual Cafe for Java Enterpr. Edition based on JDK 1.1.3
Netscape Navigator 3.0
Internet Explorer 3.02 und 4.0
(Review ID: 19451)
======================================================================

Comments
EVALUATION This doesn't have anything to do with garbage collection. The java heap stays consistently the same size during all this. It appears to be leaking C heap. When running the JavaSoft JDK the leakage on NT4 is less than 1k per frame. On Solaris the leakage is much larger, around 10K per frame. I'm refiling this against classes_awt since the leakage appears to be there. By the way on Solaris you can use dbx to track leaks in programs. Basically start up dbx on your program and then type "check -leaks" at the prompt. Run the program and at any point you can drop back into the debugger and say "showleaks" to get a report about leaks. It gets a little confused about by the Java heap so it sometime erroneously reports some leakage for internal class objects. tom.rodriguez@Eng 1997-12-16 This bug is likely the cause of bug ID# 4208524. It would be nice to get this fixed so we can see if the other bug still exists. steve.wilson@eng 1999-02-07 Name: vuC71690 Date: 05/18/99 ###@###.### 1999-05-18 In the complete test case the loop quoted in Description runs within an event listener (in repsonse to the button press). If the same loop runs outside the event listener Mem Usage reported by Task Manager remains the same during the run. Also the frame have to be shown for the leak to manifest. ====================================================================== Name: vuC71690 Date: 05/25/99 ###@###.### 1999-05-20 I can't see a C heap leak. In fact the debugging allocator on NT shows that AWT consumes typically well under 100K for the test case. On the other hand, the Java heap grows as the test runs, but goes back to normal after the loop. There's no leak, all garbage is properly collected after the loop is done. IMO it's a pilot error, since the test case creates and shows lots of frames while in the ActionListener.actionPerformed called in response to the button press. The test case below demostrates this. ----8<----8<----8<----8<----8<-Test.java-8<----8<----8<----8<----8<---- import java.awt.*; import java.awt.event.*; import java.util.*; public class Test extends Frame implements Runnable { static public void main(String args[]) { new Test("Leak Test"); } static Runtime rt = Runtime.getRuntime(); WeakHashMap frames; boolean forever; int limit; Choice limitChoice; Button runButton; Button spawnButton; Button gcButton; public Test(String title) { super(title); setLayout(new GridLayout(0, 1)); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { Test.this.dispose(); System.exit(0); } }); frames = new WeakHashMap(); Panel choicePanel = new Panel(new FlowLayout(FlowLayout.CENTER)); Panel buttonsPanel = new Panel(new FlowLayout(FlowLayout.CENTER)); Panel gcPanel = new Panel(new FlowLayout(FlowLayout.CENTER)); limitChoice = new Choice(); limitChoice.add("10"); limitChoice.add("20"); limitChoice.add("30"); limitChoice.add("40"); limitChoice.add("50"); limitChoice.add("60"); limitChoice.add("70"); limitChoice.add("80"); limitChoice.add("90"); limitChoice.add("100"); limitChoice.add("forever"); forever = false; limit = 10; limitChoice.select("10"); limitChoice.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { String s = (String) e.getItem(); if (s.equals("forever")) { forever = true; } else { forever = false; try { limit = Integer.parseInt(s); } catch (NumberFormatException excp) { } } } } }); runButton = new Button("Run on this thread"); runButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.err.println("Will run on the SAME thread"); run(); } }); spawnButton = new Button("Spawn a spearate thread"); spawnButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.err.println("Will run on a SEPARATE thread"); (new Thread(Test.this)).start(); } }); gcButton = new Button("GC once"); gcButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.err.println("Before GC: "+rt.totalMemory() +"/"+rt.freeMemory() + " (" + frames.size() + ")"); System.gc(); System.err.println("After GC: "+rt.totalMemory() +"/"+rt.freeMemory() + " (" + frames.size() + ")"); System.runFinalization(); System.err.println("After FIN: "+rt.totalMemory() +"/"+rt.freeMemory() + " (" + frames.size() + ")"); System.err.println(); } }); choicePanel.add(new Label("Limit:", Label.RIGHT)); choicePanel.add(limitChoice); buttonsPanel.add(runButton); buttonsPanel.add(spawnButton); gcPanel.add(gcButton); add(choicePanel); add(buttonsPanel); add(gcPanel); pack(); show(); System.err.println("Hit button to run " + (forever ? "infinite loop" : "an iteration")); } public void run() { Window f; if (forever) { System.err.println("Will run forever"); } else { System.err.println("Will run "+limit+" loops"); } int n = 0; while (forever || (n < limit)) { for (int i = 0; i < 5; ++i) { System.gc(); System.runFinalization(); } long totalBefore = rt.totalMemory(); long freeBefore = rt.freeMemory(); ++n; if (n < 1000) System.err.print(" "); if (n < 100) System.err.print(" "); if (n < 10) System.err.print(" "); if (forever) { System.err.print("" + n + ": "); } else { System.err.print("" + n + " of " + limit + ": "); } System.err.print(totalBefore + "/" + freeBefore + " (" + frames.size() + ") -> "); for (int i=0;i<100;i++) { f = new Frame(); frames.put(f, null); f.setBounds(100,100,200,200); f.setVisible(true); f.setVisible(false); f.dispose(); f = null; } long total = rt.totalMemory(); long free = rt.freeMemory(); System.err.println("" + total + "/" + free + " (" + frames.size() + ")"); } System.err.println(); } } // Test.java ends here ----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<---- The loop puts each Frame into a WeakHashMap and monitor the map size to check how many of those frames were gc'ed. The numbers in the output from the loop are: total/free (frames) Where total and free are the Java heap stats (from Runtime) and the number of frames is the number of frames in the weak hash. If the frame creation loop is ran inside the listener ("Run on this thread" button) the memory consumption goes up as well as the number of frames in the hash. But after the loop is done heap usage goes back. If the frame creation loop is ran on a separate thread ("Spawn a separate thread" button). Heap usage remain the same throughout the loop and the number of frames in the weak hash at the beginning of the inner loop (right after GC) is zero or may be less than 5. ======================================================================
11-06-2004

WORK AROUND Name: diC59631 Date: 12/16/97 Do you know one!!!!!!! ====================================================================== Name: vuC71690 Date: 05/25/99 ###@###.### 1999-05-20 Spawn a separate thread instead of running directly within the listener. ======================================================================
20-05-1999