I have devised a fairly simple example which exhibits this memory-retention problem (plus my fix):

Instructions (presuming that js.jar is already in your CLASSPATH):

1. Compile the attached java file:
javac ClassCacheMemoryExample.java

2. After ensuring that the current directory is also in your CLASSPATH, run the generated class:
java ClassCacheMemoryExample
This should generate a message along the following lines:
"In order to try to generate the anticipated OutOfMemoryException, please add the following jre param: -Xmx32M"
(note the value for -Xmx parameter might vary according to your JRE).

3. Using the above advice, run the following:
java -Xmx32M ClassCacheMemoryExample
Verify that it causes an OutOfMemoryException.

4. Add the following parameter "-customWrapFactory", in order to use the custom WrapFactory:
java -Xmx32M ClassCacheMemoryExample -customWrapFactory
Verify that that this does NOT cause an OutOfMemoryException.

Consult the attached java for more details of the the test and the "fix".

Kieran

import org.mozilla.javascript.*;

public class ClassCacheMemoryExample {

        public static void main (String [] args)
                throws Exception
        {
                final boolean customWrapFactory = args.length>0 && 
args[0].equals("-customWrapFactory");
                
                Context cx = Context.enter();
                
                // create and seal the shared scope.
                // nb the sealing isn't actually relevant to the example.
                final ScriptableObject sharedScope = cx.initStandardObjects();
                sharedScope.sealObject();
                
                if (customWrapFactory) {
                        // Use our own wrap factory, extending WrapFactory, 
that simply
                        // passes all function calls to the super-class, but 
... replacing
                        // scope with our sharedScope.
                        WrapFactory ourWrapFactory = new WrapFactory () {
                                public Object wrap(Context cx, Scriptable 
scope, Object obj, Class staticType)
                                {
                                        return super.wrap(cx, sharedScope, obj, 
staticType);
                                }
                                public Scriptable wrapAsJavaObject(Context cx, 
Scriptable scope, Object javaObject, Class staticType)
                                {
                                        return super.wrapAsJavaObject(cx, 
sharedScope, javaObject, staticType);
                                }
                                public Scriptable wrapNewObject(Context cx, 
Scriptable scope, Object obj)
                                {
                                        return super.wrapNewObject(cx, 
sharedScope, obj);
                                }
                        };
                        cx.setWrapFactory(ourWrapFactory);
                }

                // The javscript we're going to run. Concocted in order to use 
a lot of memory.
                final String js = "var arr = new Array(); for (var 
i=0;i<1024*1024;i++) { arr[i] = i; }";
                // The scope we'll use for running our script.
                Scriptable newScope;
                
                /* Iteration 1. */
                // Create a new scope, and set its parent/prototype as per:
                //   
https://developer.mozilla.org/En/Rhino_documentation/Scopes_and_Contexts#Sharing_Scopes
                newScope = cx.newObject(sharedScope);
                newScope.setPrototype(sharedScope);
                newScope.setParentScope(null);

                // Define a new class; Any old java class will work for this 
example.
                // For simplicity just define a class that extends Object.
                class C1 {}
                // Wrap the javaclass.
                cx.javaToJS(new C1(), newScope); // HERE is where newScope gets 
added to the ClassCache (associated with the C1 class)
                // Then, run the javascript.
                cx.evaluateString(newScope, js, "<script1>", 1, null); // HERE 
is where newScope gets lots of memory assigned to it.
                
                /* Iteration 2. */
                // Repeat Iteration 1, but with a different wrapped class
                newScope = cx.newObject(sharedScope);
                newScope.setPrototype(sharedScope);
                newScope.setParentScope(null);

                class C2 {}
                cx.javaToJS(new C2(), newScope); // HERE is where newScope gets 
added to the ClassCache (associated with the C2 class)
                cx.evaluateString(newScope, js, "<script2>", 1, null); // HERE 
is where newScope gets lots more memory assigned to it.
                
                System.out.println("Successfully ran script1 & script2");
                
                if (!customWrapFactory) {
                        // We're trying to have caused an OutOfMemoryException 
by the time
                        // we get to here.
                        // Therefore work out how much memory we have in the 
heap, and use
                        // it to suggest a better value for -Xmx.
                        Runtime r = Runtime.getRuntime();
                        r.gc(); r.gc();
                        long approxUsedMem = (r.totalMemory()-r.freeMemory()) / 
(1024*1024);
                        System.out.println("In order to try to generate the 
anticipated OutOfMemoryException, please add the following jre param: -Xmx" + 
(approxUsedMem*3/4)+ "M");
                }
                
                cx.exit();
                
        }
}

_______________________________________________
dev-tech-js-engine-rhino mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino

Reply via email to