Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6773985

OutOfMemory (PermGen space) under Linux / Firefox when switching bw. applets

    Details

    • Subcomponent:
    • Resolved In Build:
      b48
    • CPU:
      x86
    • OS:
      linux
    • Verification:
      Not verified

      Backports

        Description

        FULL PRODUCT VERSION :
        java version "1.6.0_06"
        Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
        Java Hotspot(TM) Client VM (build 10.0-b22, mixed mode, sharing)

        java version "1.6.0_10"
        Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
        Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode, sharing)


        ADDITIONAL OS VERSION INFORMATION :
        Linux 2.6.13-15.8-smp x86_64

        EXTRA RELEVANT SYSTEM CONFIGURATION :
        Reproduce with Mozilla 1.7.11 and Firefox 1.0.6.
        My colleages reproduced with Firefox 2 and Firefox 3.

        A DESCRIPTION OF THE PROBLEM :
        If I open the web browser (firefox or mozilla) and load Java applets one by one, it eventually runs out of memory (PermGen space). The reason is that it does not release old classloaders of applets that are not anymore active, and hence does not release the classes that were allocated by this classloader.

        If I have more than 5 applets and browse them one by one, and after the last applet I come back to the first applet, I see that its classes are reloaded again by a new classloader, even though the classes of this applet are still in the VM hold by an old classloader.

        The increase of the code memory can easily be observed by using jconsole.

        I analyzed the situation with jhat, and it seems all class loaders are kept in a HashMap in a static field of sun.awt.X11.XToolkit.winToDispatcher. This is of course a memory leak, since when the class loader is hold, all applet classes loaded by the classloader are also hold and never freed by the Garbage collection.

        The exact reference chain (shown by jhat) is this:

        Static reference from sun.awt.X11.XToolkit.winToDispatcher (from class sun.awt.X11.XToolkit) :
        --> java.util.HashMap@0x79d7ce38 (40 bytes) (field table:)
        --> [Ljava.util.HashMap$Entry;@0x79d98670 (72 bytes) (Element 8 of [Ljava.util.HashMap$Entry;@0x79d98670:)
        --> java.util.HashMap$Entry@0x79e75490 (24 bytes) (field value:)
        --> java.util.Vector@0x79e76220 (24 bytes) (field elementData:)
        --> [Ljava.lang.Object;@0x79e77450 (48 bytes) (Element 0 of [Ljava.lang.Object;@0x79e77450:)
        --> sun.awt.X11.XEmbedClientHelper@0x79e77438 (22 bytes) (field embedded:)
        --> sun.awt.X11.XEmbeddedFramePeer@0x79e760b8 (301 bytes) (field target:)
        --> sun.plugin.viewer.frame.XNetscapeEmbeddedFrame@0x79e75618 (361 bytes) (field appContext:)
        --> sun.awt.AppContext@0x79e755e0 (49 bytes) (field contextClassLoader:)
        --> sun.plugin.security.PluginClassLoader@0x79e74ac0 (123 bytes)

        All class loaders are hold by such a chain.

        Indeed the XEmbedClientHelper.install() adds itself to the XToolkit but never removes itself. I think it should remove itself when the applet is destroyed. The XEmbedClientHelper has a link chain to the AppContext, which holds the class loader.




        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        In principle, it can be reproduced by creating a couple of large applets (with lots of classes) and load them one by one into the webbrowser without shutting the webbrowser down. The increase in code can be watched with jconsole. If there are many applets, you will eventually notice the OutOfMemory (PermGen space) no matter how high your PermGenSize setting is.

        For your conveniance, here a csh script that generated 6 large applets,
        each with 1000 classes:

        ---------------------------- cut -------------------------------------------
        #!/bin/csh

        mkdir a
        mkdir b
        mkdir c
        mkdir d
        mkdir e
        mkdir f

        foreach d (a b c d e f)

        pushd .
        cd $d
        touch MyApplet.java
        echo "package "$d";" >> MyApplet.java
        echo "import java.awt.*;" >> MyApplet.java
        echo "import java.awt.event.*;" >> MyApplet.java
        echo "import javax.swing.*;" >> MyApplet.java
        echo "public class MyApplet extends JApplet {" >> MyApplet.java
        echo " public void init() {" >> MyApplet.java
        echo " super.init();" >> MyApplet.java
        echo ' JButton helpButton = new JButton("Test");' >> MyApplet.java
        echo " helpButton.addActionListener(new ActionListener() {" >> MyApplet.java
        echo " public void actionPerformed(ActionEvent evt) {" >> MyApplet.java
        echo ' System.err.println("Help pressed"); ' >> MyApplet.java
        echo " }});" >> MyApplet.java
        echo " JPanel panel = new JPanel(new FlowLayout());" >> MyApplet.java
        echo " panel.add(helpButton);" >> MyApplet.java
        echo " getContentPane().add(panel);" >> MyApplet.java

        foreach i (0 1 2 3 4 5 6 7 8 9)
        foreach j (0 1 2 3 4 5 6 7 8 9)
        foreach k (0 1 2 3 4 5 6 7 8 9)
        echo " new Class"$i$j$k"();" >> MyApplet.java
        touch Class$i$j$k.java
        echo "package "$d";" >> Class$i$j$k.java
        echo "public class Class"$i$j$k >> Class$i$j$k.java
        echo "{" >> Class$i$j$k.java
        foreach m (0 1 2 3 4 5 6 7 8 9)
        foreach n (0 1 2 3 4 5 6 7 8 9)
        echo " public void method"$m$n"() {}" >> Class$i$j$k.java
        end
        end
        echo "}" >> Class$i$j$k.java
        end
        end
        end

        echo " }" >> MyApplet.java
        echo "}" >> MyApplet.java
        popd

        end
        --------------------- cut ----------------------------------------

        Run this script in Linux or Unix creates directories a b c d e f which contains the applet 6 times. The only difference is that package name.
        Compile these one by one and each package into one jar:

        javac -d . src/a/*.java
        javac -d . src/b/*.java
        javac -d . src/c/*.java
        javac -d . src/d/*.java
        javac -d . src/e/*.java
        javac -d . src/f/*.java
        jar cf appletA.jar a/
        jar cf appletB.jar b/
        jar cf appletC.jar c/
        jar cf appletD.jar d/
        jar cf appletE.jar e/
        jar cf appletF.jar f/

        Now you have 6 applets, each just containing one button.
        The applet creates instances of 1000 classes, just to make sure 1000 classes are loaded. The instances are garbage collectable.

        Now create 6 HTML files that load the applets.
        Here is one (index0.html):

        ----------------- cut here -------------------------------------------------
        <HTML>
        <HEAD>
        <META http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <TITLE>Test1</TITLE>
        </HEAD>
        <BODY bgcolor="#FFFFFF">
        <!--"CONVERTED_APPLET"-->
        <!-- HTML CONVERTER -->
        <OBJECT
            classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
            codebase = "http://java.sun.com/update/1.4.2/jinstall-1_4-windows-i586.cab#Version=1,4,0,0"
            WIDTH = "300" HEIGHT = "40" ALIGN = "baseline" >
            <PARAM NAME = CODE VALUE = "a.MyApplet" >
            <PARAM NAME = CODEBASE VALUE = "." >
            <PARAM NAME = ARCHIVE VALUE = "appletA.jar" >
            <PARAM NAME = "type" VALUE = "application/x-java-applet;version=1.4">
            <PARAM NAME = "scriptable" VALUE = "false">

            <COMMENT>
        <EMBED
                    type = "application/x-java-applet;version=1.4" CODE = "a.MyApplet" JAVA_CODEBASE = "." ARCHIVE = "appletA.jar" WIDTH = "300" HEIGHT = "40" ALIGN = "baseline" scriptable = false pluginspage = "http://java.sun.com/products/plugin/index.html#download">
        <NOEMBED>
                    
                    </NOEMBED>
        </EMBED>
            </COMMENT>
        </OBJECT>

        <!--
        <APPLET CODE = "a.MyApplet" JAVA_CODEBASE = "." ARCHIVE = "appletA.jar" WIDTH = "300" HEIGHT = "40" ALIGN = "baseline">


        </APPLET>
        -->


        <!--"END_CONVERTED_APPLET"-->
        <!--@@@-->

        <p>

        <A HREF="index1.html">NEXT</A>

        </BODY>
        </HTML>
        ----------------------------------- cut here -------------------------------

        Create similar index1.html, index2.html ... index5.html.
        Each one should have the NEXT link pointing to the next one,
        and index5.html should have the NEXT link pointing to index0.html.
        Each should load a different applet, i.e.
        index1.html loads b.MyApplet in appletB.jar
        index2.html loads c.MyApplet in appletC.jar
        ...
        index5.html loads f.MyApplet in appletF.jar.


        Now open index0.html, wait until the applet is loaded,
        then repeat to click NEXT, each click loads a new applet,
        until OutOfMemory. If you don't want to wait until OutOfMemory,
        use jconsole to see the code memory increase for each applet
        that is loaded.

        When I tried this with only 3 applets, the classLoaderCache
        of the plugin avoided that the classes of an applet are reloaded.
        In this case, hit the x key in the Java Console before clicking the
        NEXT link to force that an previously loaded applet is reloaded.

        On my machine, with 6 applets, when coming from the last applet
        to the first applet, it is reloaded independent of hitting the x key.



        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        When an applet is destroyed, then all its memory should be
        freed. The applet classloader should not be hold by a reference
        chain from XToolkit.

        Or to say it differently:
        If I have 100 large applets that individually are small enough to
        run in the brower without OutOfMemory, then it should be possible
        to browse through all 100 large applets without shutting down
        the browser and without OutOfMemory caused by accumulated
        memory.

        ACTUAL -
        OutOfMemory (PermGen space)

        ERROR MESSAGES/STACK TRACES THAT OCCUR :
        OutOfMemory (PermGen space)

        REPRODUCIBILITY :
        This bug can be reproduced always.

        ---------- BEGIN SOURCE ----------
        See Description. It contains a csh script to generate 6 large applets.
        ---------- END SOURCE ----------

        CUSTOMER SUBMITTED WORKAROUND :
        Shutting down the browser before loading the next applet avoids the OutOfMemory.

          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  art Artem Ananiev
                  Reporter:
                  ndcosta Nelson Dcosta (Inactive)
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  0 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved:
                    Imported:
                    Indexed: