Skip to main content

Search and Top Navigation

#10050 open bug ()

Opened May 20, 2014 10:37PM UTC

Last modified September 21, 2018 06:58PM UTC

Autocomplete: Destroy potential memory/resource leak

Reported by: scott.k.mitch1 Owned by:
Priority: minor Milestone: none
Component: ui.autocomplete Version: 1.10.4
Keywords: Cc:
Blocked by: Blocking:
Description

It seems like Autocomplete's destroy method may not be making all memory available for GC. I have a [test case](http://jsfiddle.net/WX8wd/) setup to try the potential issue out. I am using google chrome dev tool bar to debug and noticing that there are new elements in the Detatched DOM Tree section of heap snap shots before and after the destroy call is made. After waiting a while and forcing a GC collection the timeline indicates the number of nodes is not returning to the original state. The object's retaining tree indicates that the :not(.ui-menu-item) in function cache() object still may have a reference preventing resources from being re-claimed. Can someone confirm my analysis or explain why this is not an issue?

Attachments (0)
Change History (11)

Changed May 21, 2014 06:39PM UTC by scott.k.mitch1 comment:1

Here is some environment information that may be relevant:

Google Chrome Version 35.0.1916.114

Ubuntu 12.04 x86_64 3.2.0-61-generic

Changed May 23, 2014 01:14PM UTC by tj.vantoll comment:2

status: newopen

I'm admittedly no expert in using the heap tools in Chrome's DevTools, but what I'm seeing is two "Detached DOM tree" top level entries. One is datepicker related, which is a known problem that we're addressing in the rewrite.

The second one however contains two elements from autocomplete: the live region and the menu—which correspond to the

liveRegion
and
menu
variables on the autocomplete instance. The autocomplete widget does call
remove()
on each of these elements (see https://github.com/jquery/jquery-ui/blob/203b23a87c68312382aed45c0e67df4461c4ebfc/ui/autocomplete.js#L326-327), but perhaps a reference remains that keeps this from being cleaned up?

In local testing if I add

this.liveRegion = null
to autocomplete's
_destroy()
, the live region no longer appears as a Detached DOM tree, so I'll mark this issue as valid. The real question to me is: what still has a reference to the instance. scott.k.mitch1, do you have any ideas?

Changed May 23, 2014 01:20PM UTC by tj.vantoll comment:3

summary: Autocomplete Destroy potential memory/resource leakAutocomplete: Destroy potential memory/resource leak

Changed May 23, 2014 03:10PM UTC by scott.k.mitch1 comment:4

_comment0: Thanks for looking into this issue. I have been evaluating and contributing to a few of other open source packages that have more sever memory leaks for my use-case. Unfortunately this means I have not had time to dive into Autocomplete. \ \ A few general things to look out for are: \ * Clean up any jquery data cache you may be using. It is good practice to accompany any calls to {{{.data('x',y)}}} with a {{{.removeData('x')}}} \ * In general avoid using references to DOM elements in anonymous functions which are defined outside the scope of that anonymous function. Re-select unless you are sure the anonymous function will be made eligible for GC. For example if the anonymous function is used to register for an {{{.on('click', ...)}}} then you want to make sure you do an {{{.off('click', ...)}}} \ * Avoid storing references to DOM elements in the {{{data}}} cache, or if you must make sure that you clean up after yourself. (inspect line 539 {{{return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );}}}) \ 1400857955258401
_comment1: Thanks for looking into this issue. I have been evaluating and contributing to a few of other open source packages that have more severe memory leaks for my use-case. Unfortunately this means I have not had time to dive into Autocomplete. \ \ A few general things to look out for are: \ * Clean up any jquery data cache you may be using. It is good practice to accompany any calls to {{{.data('x',y)}}} with a {{{.removeData('x')}}} \ * In general avoid using references to DOM elements in anonymous functions which are defined outside the scope of that anonymous function. Re-select unless you are sure the anonymous function will be made eligible for GC. For example if the anonymous function is used to register for an {{{.on('click', ...)}}} then you want to make sure you do an {{{.off('click', ...)}}} \ * Avoid storing references to DOM elements in the {{{data}}} cache, or if you must make sure that you clean up after yourself. (inspect line 539 {{{return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );}}}) \ 1400858223718719

Thanks for looking into this issue. I have been evaluating and contributing to a few of other open source packages that have more severe memory leaks for my use-case. Unfortunately this means I have not had time to dive into Autocomplete.

A few general things to look out for are:

  • Clean up any jquery data cache you may be using. It is good practice to accompany any calls to
    .data('x',y)
    with a
    .removeData('x')
  • In general avoid using references to DOM elements in anonymous functions which are defined outside the scope of that anonymous function. Re-select unless you are sure the anonymous function will be made eligible for GC. For example if the anonymous function is used to register for an
    .on('click', ...)
    then you want to make sure you do an
    .off('click', ...)
    or equivalent call to un-register the event so there is no longer a reference to the anonymous function
  • Avoid storing references to DOM elements in the
    data
    cache, or if you must make sure that you clean up after yourself. (inspect line 539
    return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
    )

Changed May 23, 2014 03:15PM UTC by scott.k.mitch1 comment:5

_comment0: Replying to [comment:4 scott.k.mitch1]: \ > Thanks for looking into this issue. I have been evaluating and contributing to a few of other open source packages that have more severe memory leaks for my use-case. Unfortunately this means I have not had time to dive into Autocomplete. \ > \ > A few general things to look out for are: \ > * Clean up any jquery data cache you may be using. It is good practice to accompany any calls to {{{.data('x',y)}}} with a {{{.removeData('x')}}} \ > * In general avoid using references to DOM elements in anonymous functions which are defined outside the scope of that anonymous function. Re-select unless you are sure the anonymous function will be made eligible for GC. For example if the anonymous function is used to register for an {{{.on('click', ...)}}} then you want to make sure you do an {{{.off('click', ...)}}} or equivalent call to un-register the event so there is no longer a reference to the anonymous function \ > * Avoid storing references to DOM elements in the {{{data}}} cache, or if you must make sure that you clean up after yourself. (inspect line 539 {{{return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );}}}) \ 1400858342712724

Replying to [comment:4 scott.k.mitch1]:

Hum...I meant to click 'Edit' but instead I clicked 'Reply'. I don't see a way to delete this so just Ignore this reply.

Why the custom issue interface? Doesn't Jira offer free issue hosting to open source projects?

Changed October 18, 2016 01:52PM UTC by jGeek314 comment:6

_comment0: While researching a memory leak in my page that uses autocomplete I found similar issues with a growing detached DOM tree. The more I typed in the autocomplete input, the larger the Detached DOM tree would grow. After a lot of trial and error, I found my issue to be that in the menu.bindings was not being cleared and were referencing the detached DOM objects. By manually clearing them in the search function of the autocomplete: \ \ \ {{{ \ search: function(e,ui){ \ //fix bug in jQuery.ui somewhere where menu.bindings just grows and grows \ autoComplete.data("ui-autocomplete").menu.bindings = $(); \ }, \ }}} \ \ The detached DOM tree disappeared (presumably because I removed the references to the detached objects). \ \ The issue of the menu.bindings growing can be seen in even the basic autocomplete example: \ \ \ {{{ \ <div class="ui-widget"> \ <label for="tags">Tags: </label> \ <input id="tags"> \ </div> \ \ <script> \ \ var availableTags = [ \ "ActionScript", \ "AppleScript", \ "Asp" \ ]; \ var autoTest = $( "#tags" ).autocomplete({ \ source: availableTags, \ search: function(e,ui){ \ console.log(autoTest.data("ui-autocomplete").menu.bindings.length); \ } \ }); \ \ \ </script> \ }}} \ \ Not sure if the issue I found is exactly the same as what is reported in the ticket, but it seemed close enough to be worth noting here.1476799045358104

While researching a memory leak in my page that uses autocomplete I found similar issues with a growing detached DOM tree. The more I typed in the autocomplete input, the larger the Detached DOM tree would grow. After a lot of trial and error, I found my issue to be that in the menu.bindings was not being cleared and were referencing the detached DOM objects. By manually clearing them in the search function of the autocomplete:

search: function(e,ui){
     //fix bug in jQuery.ui somewhere where menu.bindings just grows and grows
     autoComplete.data("ui-autocomplete").menu.bindings = $();
},

The detached DOM tree disappeared (presumably because I removed the references to the detached objects).

The issue of the menu.bindings growing can be seen in even the basic autocomplete example:

https://jsfiddle.net/jGeek314/L6wspvnb/

Not sure if the issue I found is exactly the same as what is reported in the ticket, but it seemed close enough to be worth noting here.

Changed March 25, 2017 06:34AM UTC by scottgonzalez comment:7

#15095 is a duplicate of this ticket.

Changed April 05, 2017 10:36AM UTC by eodb comment:8

We encountered the same issue in our application. After using the autocomplete widget on large lists (> 500 items) a couple of times, the widgets quickly becomes unusable. The menu bindings are always added (via menu.refresh) but never removed.

The workaround mentioned above seems to work without noticeable side effects.

Used: Autocomplete v1.12.1, jQuery v3.2.1

Changed August 11, 2017 04:13PM UTC by sklarej comment:9

_comment0: I can confirm that there is an issue with some kind of leak that has performance implications. You can easily duplicate this using the jQuery Combobox demo by doing the following: \ \ 1. Goto the jQuery UI Autocomplete Combobox demo at [https://jqueryui.com/autocomplete/#combobox] \ 2. Repeatedly press the dropdown menu arrow on the right of the combobox to show all items \ 3. After about 20 times (this is dependent on your computer speed) you will start to see a slowdown in the time it takes to display the dropdown list. \ 4. Every time you click the dropdown, it will take longer to display \ 5. Autocomplete speed is also compromised \ \ While this is not very noticeable with a smaller list of items, it can severely affect the user experience with a larger list. \ \ We have some comboboxes with a large number of items (up to ~500 items). The autocomplete works quickly enough at first, but the more you type in the slower it goes. Even if you just open a jquery combobox dropdown list repeatedly, it gets slower and slower each time. We've had reports of typing in the contents of an autocomplete field taking over a minute. \ \ '''Workaround''': The workaround described in jGeek314's comment about clearing out the menu bindings had allowed us to continue to use the autocomplete control: \ \ {{{ \ autoComplete.data("ui-autocomplete").menu.bindings = $(); \ }}} \ \ 1502481927050082

I can confirm that there is an issue with some kind of leak that has performance implications as of jQuery UI 1.12.1 / jQuery 3.2.1.

You can easily duplicate this using the jQuery Combobox demo by doing the following:

1. Goto the jQuery UI Autocomplete Combobox demo at https://jqueryui.com/autocomplete/#combobox

2. Repeatedly press the dropdown menu arrow on the right of the combobox to show all items

3. After about 20 times (this is dependent on your computer speed) you will start to see a slowdown in the time it takes to display the dropdown list.

4. Every time you click the dropdown, it will take longer to display

5. Autocomplete speed is also compromised

While this is not very noticeable with a smaller list of items, it can severely affect the user experience with a larger list.

We have some comboboxes with a large number of items (up to ~500 items). The autocomplete works quickly enough at first, but the more you type in the slower it goes. Even if you just open a jquery combobox dropdown list repeatedly, it gets slower and slower each time. We've had reports of typing in the contents of an autocomplete field taking over a minute.

Workaround: The workaround described in jGeek314's comment about clearing out the menu bindings had allowed us to continue to use the autocomplete control:

autoComplete.data("ui-autocomplete").menu.bindings = $();

Changed December 29, 2017 10:44AM UTC by rjollos comment:10

_comment0: Thanks indeed, the workaround works a treat. Would this not be suitable to be included in the jquery ui source? \ \ Replying to [comment:9 sklarej]: \ > I can confirm that there is an issue with some kind of leak that has performance implications as of jQuery UI 1.12.1 / jQuery 3.2.1. \ > \ > You can easily duplicate this using the jQuery Combobox demo by doing the following: \ > \ > 1. Goto the jQuery UI Autocomplete Combobox demo at [https://jqueryui.com/autocomplete/#combobox] \ > 2. Repeatedly press the dropdown menu arrow on the right of the combobox to show all items \ > 3. After about 20 times (this is dependent on your computer speed) you will start to see a slowdown in the time it takes to display the dropdown list. \ > 4. Every time you click the dropdown, it will take longer to display \ > 5. Autocomplete speed is also compromised \ > \ > While this is not very noticeable with a smaller list of items, it can severely affect the user experience with a larger list. \ > \ > We have some comboboxes with a large number of items (up to ~500 items). The autocomplete works quickly enough at first, but the more you type in the slower it goes. Even if you just open a jquery combobox dropdown list repeatedly, it gets slower and slower each time. We've had reports of typing in the contents of an autocomplete field taking over a minute. \ > \ > '''Workaround''': The workaround described in jGeek314's comment about clearing out the menu bindings had allowed us to continue to use the autocomplete control: \ > \ > {{{ \ > autoComplete.data("ui-autocomplete").menu.bindings = $(); \ > }}} \ > \ 1514899043913059
_comment1: Replying to [comment:9 sklarej]: \ > I can confirm that there is an issue with some kind of leak that has performance implications as of jQuery UI 1.12.1 / jQuery 3.2.1. \ > ... \ \ \ Thanks indeed, the workaround works a treat. Would this not be suitable to be included in the jquery ui source?1528202526526676

Replying to [comment:9 sklarej]:

I can confirm that there is an issue with some kind of leak that has performance implications as of jQuery UI 1.12.1 / jQuery 3.2.1. ...

Thanks indeed, the workaround works a treat. Would this not be suitable to be included in the jquery ui source?

To be clear, I did this each time the list changed. In a function that loaded the new filtered list I called this

$(inputId).data("ui-autocomplete").menu.bindings = $();

Changed September 21, 2018 06:58PM UTC by duanehutchins comment:11

Replying to [comment:10 Colum Clissmann]:

Replying to [comment:9 sklarej]: > I can confirm that there is an issue with some kind of leak that has performance implications as of jQuery UI 1.12.1 / jQuery 3.2.1. > ... Thanks indeed, the workaround works a treat. Would this not be suitable to be included in the jquery ui source? To be clear, I did this each time the list changed. In a function that loaded the new filtered list I called this $(inputId).data("ui-autocomplete").menu.bindings = $();

If it helps anyone, adding this to the open event works great:

$( ".selector" ).autocomplete({
  open: function() { $( this ).data("ui-autocomplete").menu.bindings = $(); }
});