Opened 7 years ago

Closed 7 years ago

Last modified 6 years ago

#16684 closed defect (fixed)

[patch][ccla]possible gfx memory leaks when many shapes are created

Reported by: Patrick Ruzand Owned by: Patrick Ruzand
Priority: high Milestone: 1.7.5
Component: DojoX GFX Version: 1.8.3
Keywords: Cc: ben hockey, cjolif, Eugene Lazutkin
Blocked By: Blocking:

Description (last modified by Patrick Ruzand)

When a huge number of shapes are created (for ex, a monitoring application that creates/destroy many shapes at regular intervals), the IE allocated memory keeps increasing.

The attached sample reproduces the issue. Run it for a long time (1-2h) and monitor the browser memory using a task mgr.

Happens in IE9 (all doc mode) or IE8. This is NOT reproducible on FF or Safari.

Attachments (5)

gfx_ieleak.html (2.0 KB) - added by Patrick Ruzand 7 years ago.
testcase demonstrating the issue.
gfx_ieleak.2.html (2.0 KB) - added by Patrick Ruzand 7 years ago.
fix images path
16684.patch (11.2 KB) - added by Patrick Ruzand 7 years ago.
patch by pruzand (IBM,CCLA)
test_registryleak.html (1.4 KB) - added by Patrick Ruzand 7 years ago.
"pure" javascript testcase
patch-registry.patch (11.1 KB) - added by Patrick Ruzand 7 years ago.
make the registry API optional and move it in a new dojox/gfx/registry module. patch by pruzand (IBM, CCLA)

Download all attachments as: .zip

Change History (34)

Changed 7 years ago by Patrick Ruzand

Attachment: gfx_ieleak.html added

testcase demonstrating the issue.

comment:1 Changed 7 years ago by Patrick Ruzand

Milestone: tbd1.7.5
Owner: changed from Eugene Lazutkin to Patrick Ruzand
Priority: undecidedhigh
Status: newassigned

comment:2 Changed 7 years ago by Patrick Ruzand

Description: modified (diff)

The problem appears because of the gfx.shape.register function that creates new ids (strings) for each shape.
When the number of shapes that are created is huge (and so the number of id strings), IE seems not to be able to collect all the intern strings, even if there are no references to ids anymore.

comment:3 Changed 7 years ago by Patrick Ruzand

Description: modified (diff)

comment:4 Changed 7 years ago by ben hockey

Cc: ben hockey added

comment:5 Changed 7 years ago by Colin Snover

Summary: possible gfx MLKs when many shapes are createdpossible gfx memory leaks when many shapes are created

Changed 7 years ago by Patrick Ruzand

Attachment: gfx_ieleak.2.html added

fix images path

comment:6 Changed 7 years ago by Patrick Ruzand

Description: modified (diff)

What I have found:

  • I thought it was due to the getUID closure that could not be GCed due to the ref to the id string. It's not: extracting the getUID to be a Shape method with a _uid field that is nullified when the shape is detroyed has no effect. That is:

instead of

constructor: function(){
  ...
  var uid = shape.register(this);
  this.getUID = function(){
    return uid;
  }
}

replaced by:

constructor: function(){
  ...
  this._uid = shape.register(this);
},
getUID: function(){
  return this._uid;
},
destroy: function(){
  shape.dispose(this);
  this._uid = null;
}
  • it's not a leak due to the registry map. Even if I comment the call to registry[uid] = shape, the problem still there.
  • The fact that FF or Safari memory is flat (for Safari, it increases then stabilizes) leads me to think the gfx api implementation is handling the cleanup correctly.

*

Version 0, edited 7 years ago by Patrick Ruzand (next)

comment:7 Changed 7 years ago by Patrick Ruzand

A possible patch would be to recycle the ids of the destroyed shapes. This way, it would limit the number of intern strings the browser has to handle.
But since recycling ids makes an id not "unique" anymore, and therefore may have some possible impacts on existing application, and because this use case where thousands of shapes are created and destroyed during the app lifecycle is quite specific and due to IE bad memory management, the patch would need to be enabled via a dojoConfig flag.

I attach such patch to the ticket. The idea is:

  • it is enabled via the recycleGfxIds dojoConfig property.
  • when disabled, the existing behavior is kept.
  • when enabled, the id of a destroyed shape is stored in a cache, and when a new shape is registered, the cache is checked for available ids.
Last edited 7 years ago by Patrick Ruzand (previous) (diff)

Changed 7 years ago by Patrick Ruzand

Attachment: 16684.patch added

patch by pruzand (IBM,CCLA)

comment:8 Changed 7 years ago by Patrick Ruzand

Summary: possible gfx memory leaks when many shapes are created[patch][ccla]possible gfx memory leaks when many shapes are created

comment:9 Changed 7 years ago by ben hockey

is there some other way to achieve this? it seems that using the type of shape as part of the key into the registry is never leveraged. do you see improvements if you just share a single number as the id for all shapes and only use that number as the index into the registry?

var uid = 0,
    registry = {};
shape.register = function (s) {
    registry[++uid] = s;
    return uid;
};

this has the following advantages:

  • does not rely on declaredClass. you can't rely on declaredClass to exist in any shape subclasses so it would be good not to use it if possible.
  • might help memory consumption by not creating extra unused strings with s.declaredClass.split('.')
  • avoids any string concatenation in order to produce the key into the registry - it's always just the next number in sequence.

if for some reason you really need to rely on declaredClass then maybe you could try using the full declaredClass as the type rather than just the last segment - this may use less memory compared to splitting it to get just the last segment but it will still involve a string concatenation in order to add a unique number for that type.

if you are planning to leverage the type in the future then you'll need a different approach than what you're using currently. you have a single hash where the keys are going to be something like Rect0, Rect1, Line0, Line1, Line2. if you want to leverage the type to retrieve shapes from the registry, you're still going to need to iterate all the keys of the hash and compare them against the type you're looking for. a better approach would be a separate hash for each type so that registry ends up looking something more like:

{
    Rect: {
        0: ...,
        1: ...
    },
    Line: {
        0: ...,
        1: ...,
        2: ...
    }
}

i'd rather try using just a single id and ignoring the type rather than add more complexity such as a recycleGfxIds config property. i'd like to only use something like that as a last resort. i don't think we've explored all the options yet.

comment:10 in reply to:  9 ; Changed 7 years ago by Patrick Ruzand

Replying to neonstalwart:

is there some other way to achieve this? it seems that using the type of shape as part of the key into the registry is never leveraged.

yes, the id could in fact be whatever. The current implementation had the benefit of telling something about the shape, but it could be anything in theory.

do you see improvements if you just share a single number as the id for all shapes and only use that number as the index into the registry?

I tried that for the very same reasons you mentioned. Honnestly, I don't remember exactly as I was trying to isolate the issue (so in the middle of lots of tests), but IIRC, while the increase was much smaller, it was still increasing. But as I said, I don't remember exactly (IE9 or IE8, different doc mode), so I will do another pass to be sure. I will post the results here.

if for some reason you really need to rely on declaredClass then maybe you could try using the full declaredClass as the type rather than just the last segment - this may use less memory compared to splitting it to get just the last segment but it will still involve a string concatenation in order to add a unique number for that type.

There were no particular reasons for the declaredClass other than it was informative about the shape. So in theory, we could change the id to be simple numbers. My only concern is about the existing version (1.7 and 1.8). While the id format has not be documented (so users are not supposed to expect a specific format), changing it in a patch could maybe break an app that was expecting a particular id format ? (But I doubt).

i'd rather try using just a single id and ignoring the type rather than add more complexity such as a recycleGfxIds config property. i'd like to only use something like that as a last resort.

I totally agree, I would really prefer something else than this flag patch, and this should indeed be the last resort. That's why all your feedbacks is more than welcome !

comment:11 in reply to:  10 Changed 7 years ago by Patrick Ruzand

Replying to pruzand:

do you see improvements if you just share a single number as the id for all shapes and only use that number as the index into the registry?

I tried that for the very same reasons you mentioned. Honnestly, I don't remember exactly as I was trying to isolate the issue (so in the middle of lots of tests), but IIRC, while the increase was much smaller, it was still increasing. But as I said, I don't remember exactly (IE9 or IE8, different doc mode), so I will do another pass to be sure. I will post the results here.

So I did some tests, and it confirms indeed that the problem is still there on IE8. Indeed, under IE8, the memory increased (but at much slower pace) without stabilizing.
Under IE9/IE9Standard, the memory first increased then stabilized.

comment:12 Changed 7 years ago by Patrick Ruzand

some numbers for the "simple counter" impl:

IE9 Browser Mode/IE9Standard document mode:

start time Mem.
9:53 46732K
12:57 85294K

IE8 Browser Mode/ IE8Standard document mode:

start time Mem.
6:53 45520K
9:51 124264K

(count = 1,258,788)

comment:13 Changed 7 years ago by Patrick Ruzand

With the alpha build coming tomorrow (Feb, 20th), I would like to have the patch included in the alpha to benefit from the testing period. Any objections ? (or an alternative patch ? )

comment:14 Changed 7 years ago by ben hockey

i'm really cautious about applying a patch without understanding the underlying reason for why the memory is leaking in the first place. it increases the public API by introducing a flag that needs to be kept for backwards compatibility even if we find out that the cause of the issue was not the strings but some other side effect. perhaps there is something in your new patch that makes it possible to do some gc that wasn't possible with just deleting the property from the registry - you are doing more than just recycling ids.

also, i'm seeing this problem in chrome on osx - the memory for the tab started at 36.8Mb and after just a few minutes (maybe 5) it was at 176Mb. even with the patch this continues to happen so we should understand the cause of this problem and fix the real issue.

i would be more inclined to support your patch if it was able to at least fix IE without adding more surface area to the public API - ie if it didn't need to have the flag. that way we could have a fix for IE now but change or back out of it when we properly understand what's causing the leak.

comment:15 Changed 7 years ago by Patrick Ruzand

I understand your concern about this flag/patch, and as I said, I would be the first to drop it for a smarter fix. The thing is, I don't have one.

I have attached below a sample that does what gfx does with the registry but in pure javascript except for dojo/dom,dojo/on and dojo/ready requires. This test case reproduces the issue under IE. I think this is the very same issue we have in gfx.

Changed 7 years ago by Patrick Ruzand

Attachment: test_registryleak.html added

"pure" javascript testcase

comment:16 Changed 7 years ago by ben hockey

fwiw, right now i don't have time to look further at this today and i know you're eager to have it in the 1.9 alpha build. ultimately i can't stop you having this patch committed (this is not my component) so you only answer to the owner of the component (whoever that is - i don't even know any more, maybe it's you).

more generally i'm concerned that the registry has introduced more problems than it's ever fixed and that's why i'd like to see it work properly.

comment:17 in reply to:  16 Changed 7 years ago by Patrick Ruzand

Replying to neonstalwart:

fwiw, right now i don't have time to look further at this today and i know you're eager to have it in the 1.9 alpha build. ultimately i can't stop you having this patch committed (this is not my component) so you only answer to the owner of the component (whoever that is - i don't even know any more, maybe it's you).

I don't want to force a patch that may not be satisfying. I'm just trying to fix an IE bug, and I have not found another way of doing this. Secondly, you asked for more informations in this ticket, so yes, I am asnwering to you, and indeed the module owner,who should be still Eugene.

comment:18 Changed 7 years ago by alexandre

I tested on FF 19.0.2 and the same problem occurs, FF has 2 GB memory in use and stay extremely slow because the memory leak. It's necessary open another bug for this?

Vitorelli.

comment:19 in reply to:  18 ; Changed 7 years ago by Patrick Ruzand

Replying to alexandre:

I tested on FF 19.0.2 and the same problem occurs, FF has 2 GB memory in use and stay extremely slow because the memory leak. It's necessary open another bug for this?

Vitorelli.

I have never been able to reproduce with FF (but maybe not with FF 19). Did you use the attach test case or another one ?

(btw, no need to create another ticket).

Last edited 7 years ago by Patrick Ruzand (previous) (diff)

comment:20 Changed 7 years ago by ben hockey

i've seen it leak in chrome and here's a video of FF18 on WinodwsXP - https://saucelabs.com/tests/8af5f13c06d14f6585180087ba909a65

you can see the memory climb as the code is running but most of it seems to be reclaimed when i stop the code.

comment:21 in reply to:  19 Changed 7 years ago by alexandre

Replying to pruzand:

Replying to alexandre:

I tested on FF 19.0.2 and the same problem occurs, FF has 2 GB memory in use and stay extremely slow because the memory leak. It's necessary open another bug for this?

Vitorelli.

I have never been able to reproduce with FF (but maybe not with FF 19). Did you use the attach test case or another one ?

(btw, no need to create another ticket).

Yes, I used the attached test case. I see the same problem with dojo charts, because it use gfx. I'm using FF 19.0.2 with Windows 7 (x64), my FF has Firebug 1.11.2 installed with dojo plugin.

[Edited]

I made more tests in the following versions:

  • 1.7.2: Problem occurs;
  • 1.7.3: Problem occurs;
  • 1.7.4: OK;
  • 1.8.3: OK;

The problem exists in old versions not in the last versions. The problem that I see was in old versions, sorry.

Last edited 7 years ago by alexandre (previous) (diff)

comment:22 Changed 7 years ago by Patrick Ruzand

Cc: cjolif Eugene Lazutkin added

As discussed during the team meeting, we will make the registry optional (which means it breaks backward compat...) since it's impossible to find a fix that works for all browsers (all version of IE leaks differently..). I attach a new patch, which extracts the registry code from dojox/gfx/shape and adds a new dojox/gfx/registry module that needs to be required to plug the registry API. So, an app/lib that needs the registry API must require() this module.

Note that the Silverlight renderer requires the registry, in order to be able to bind the rawNode with the gfx shape (because of the underlying marshalling, only string can be used for this).

Changed 7 years ago by Patrick Ruzand

Attachment: patch-registry.patch added

make the registry API optional and move it in a new dojox/gfx/registry module. patch by pruzand (IBM, CCLA)

comment:23 Changed 7 years ago by Patrick Ruzand

Milestone: 1.7.51.9

comment:24 Changed 7 years ago by Eugene Lazutkin

The patch looks fine, my only concern is a back reference from a node to its shape --- this stuff can leak, especially on IE. I hope you tested the patch for memory leaks thoroughly. If not, get ready for an onslaught of tickets. :-)

comment:25 Changed 7 years ago by cjolif

In [30985]:

refs #16684. Modify charting to take into account removal of gfx registry in 1.9 (is optional now). !strict.

comment:26 Changed 7 years ago by Patrick Ruzand

Resolution: fixed
Status: assignedclosed

In [30988]:

move registry api to its own module, make it optional. fixes #16684 !strict

comment:27 Changed 6 years ago by Patrick Ruzand

In [31289]:

fix IE8 leak, refs #16684, thx elazutkin

(backport to 1.8)

Last edited 6 years ago by Patrick Ruzand (previous) (diff)

comment:28 Changed 6 years ago by Patrick Ruzand

In [31295]:

fix IE8 leak, refs #16684, thx elazutkin (backport to 1.7)

comment:29 Changed 6 years ago by bill

Milestone: 1.91.7.5
Note: See TracTickets for help on using tickets.