Closed Bug 446379 Opened 14 years ago Closed 7 years ago

memory leak, with this simple array allocation loop testcase

Categories

(Core :: JavaScript Engine, defect)

1.9.0 Branch
x86
Windows XP
defect
Not set
minor

Tracking

()

RESOLVED WORKSFORME

People

(Reporter: bugzilla33, Unassigned)

References

Details

(Keywords: memory-leak)

Attachments

(2 files)

User-Agent:       Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648)
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1


JavaScript memory leak

Reproducible: Always

Steps to Reproduce:

1. Run with IE 7 or Opera 9.5
   Constant browser memory usage

2. Run with FF 3 (windows xp sp3)
   Browser memory usage increase very fast (proportional to time)
Actual Results:  

Browser memory usage increase very fast (proportional to time)

Expected Results:  

Constant browser memory usage


<script>

 x=[]
 z=100000;while(z--)x.push([])

 function run(){
  var a,b,c,d,e,f,g,h,n
  n=5000;while(n--){
   a=[]
   b=[]
   c=[]
   d=[]
   e=[]
   f=[]
   g=[]
   h=[]
  }
  setTimeout(function(){run()},0)
 }

 run()

</script>
Component: General → JavaScript Engine
Product: Firefox → Core
Version: unspecified → 1.9.0 Branch
Attached file source code
Attached image screenshot
screenshot
Can you reproduce with current Firefox v3.1a2pre ?
URL: n/a
Keywords: mlk
Summary: memory leak → memory leak, with this simple array allocation loop testcase
Assignee: nobody → general
QA Contact: general → general
Yes, I can reproduce this with current Firefox v3.1a2pre

Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1a2pre) Gecko/2008072603 Minefield/3.1a2pre
The example shows the famous closure capture problem exaggerated by changes from the bug 417052:

function run(){
  var a,b,c,d,e,f,g,h,n
  n=5000;while(n--){
   a=[]
...
  }
  setTimeout(function(){run()},0)
 }

Here the code passes a closure function(){run()} to the function setTimeout. That closure captures the variables of the current invocation of the function run. Thus on each invocation of run the live set is increased by 8 newly allocated empty array objects stored in run variables plus the size of the runtime structures for the closure itself.

So what example as a whole does is to allocate about 3.5MB when populating the array x. This memory is live and survives the GC. Then on each setTimeout invocation the code creates a lot of garbage and adds about 400 bytes to the live set due to the closure chain.

Now since the bug 417052 the GC runs only when the total allocation size exceeds the previous live size as seen after the last GC by the factor 16. It means that after the first GC that would see the initialized x array the next GC will run only when the allocation hits 3.5*16 = 56MB. Then after that each second would add 400*100 or about 40K of live memory due to the closure leak from about 100 setTimeout invocations. That it turn would farther delay the GC until the JS engine allocates 40K*16 or about 640K.

So each second increases the maximum size of allocations in the JS engine by 640K starting from 56MB before the engine invokes the GC.

But it is surprising that neither Opera nor IE leaks. They may have better GC heuristics but the closure should leak in any case due to the support of indirect eval that prevents doing escape analysis in this example. That is, the value of variables a-h from the example can be exposed if the code does something like run = eval at soome stage.

So would the memory usage remains constant with MSIE or Opera after, say 1 hour of running?
Blocks: 417052
Status: UNCONFIRMED → NEW
Ever confirmed: true
This will be fixed by the forthcoming patch for bug 445262.

/be
Depends on: upvar1
(In reply to comment #5)
> The example shows the famous closure capture problem exaggerated by changes
> from the bug 417052:

I was wrong about the closure leak. Of cause when the closure invoked from setTimeout finishes, all the objects stored in the variables from the function run is also released. So the code per see does not leak. Perhaps it is the iteration with the cycle collector that makes the dead closures to survive few GC cycles?
Actually I do not see the leak on Linux: for a browser with a few windows with examples running the memory quickly jumps by extra 50M but then it stays at that level.
To lunter: do you have any extensions installed with your Firefox?
To Igor Bukanov:
No, I don't. I use FF without any Add-ons.

---
curiosity:
Safari (only build on Windows) running this stript can absorb all memory and crush.
Assignee: general → nobody
No longer reproducible - Resolving as WFM.
Status: NEW → RESOLVED
Closed: 7 years ago
Resolution: --- → WORKSFORME
You need to log in before you can comment on or make changes to this bug.