mozilla / rhino

Rhino is an open-source implementation of JavaScript written entirely in Java
https://rhino.github.io
Other
4.19k stars 851 forks source link

Deleted members are not dereferenced #274

Open omervk opened 8 years ago

omervk commented 8 years ago

This piece of code, when run via Rhino, allocates more and more memory and does not release it (note the line delete data.value; which dereferences the only reference to the 2MB string):

(function() {
    var getLotsOfData = function() {
        var s = "a";

        for (var i = 0; i < 20; i++) {
            s += s;
        }

        return new java.lang.String(s);
    };

    var recurse = function(timesLeft, data) {
        delete data.value;

        if (timesLeft === 0)
            return;

        recurse(timesLeft - 1, { value: getLotsOfData() });
    };

    recurse(5000, { value: 0 });
})();

Alternatively, this piece of code does not cause the issue:

(function() {
    var getLotsOfData = function() {
        var s = "a";

        for (var i = 0; i < 20; i++) {
            s += s;
        }

        return new java.lang.String(s);
    };

    var recurse = function(timesLeft, data) {
        delete data.value;

        if (timesLeft === 0)
            return;

        var newData = { value: getLotsOfData() };
        recurse(timesLeft - 1, newData);
    };

    recurse(5000, { value: 0 });
})();

The only change is the creation of a local variable named newData.

This issue is also not caused when replacing the Java strings with JavaScript strings.

The code runs on an Android device, Rhino 1.7.7 with optimization level -1.

gbrail commented 8 years ago

I was able to reproduce this by running Rhino with optimization level -1 (i.e. interpreted mode). Is that what you did?

With any other optimization mode I get a StackOverflowError long before running out of memory.

Looking at the heap dump from running this with opt level -1 I see that the String objects are being referenced by something in the interpreter's call chain. I am by no means familiar with the way that the interpreter works but both of these things tell me that the problem is in the recursion and not anywhere else. I'm not sure what we can do if that's the case but I can certainly post more diagnostics if others have an idea.

Did you try to reproduce this problem without recursion? That would concern me more.

omervk commented 8 years ago

You're right, I'm sorry. Yes, the optimization level was indeed set to -1. I've updated the OP to reflect that.

I've increased the stack size for threads running code inside Rhino, so this is probably why I wasn't affected by the stack overflow.

This issue does not occur when there's no recursion. The repro above is the most simplified version I've found the issue to occur. In my code in the meantime I've changed from working recursively to iteratively. It's not as good, but at least it doesn't crash. :smile: