ractive: Memory leak when using components

Description:

Took me hours to pin this issue down in my SPA, but finally managed to reproduce it in a few lines of code 😪 Looks like Ractive 0.8 keeps pointing to unused DOM elements (and possibly something else? whole components?) after their component is teardown.

This may result in big memory leaks, depending on the size of your components. The more the elements, the bigger the memory being leaked.

I started noticing it on components where 4k-20k elements were being removed. Narrowed it down to a minimal example.

Let me know if I can be of any help.

Versions affected:

0.8.11 notably, 0.7.3 is NOT affected

Platforms affected:

Any browser

Reproduction:

http://jsfiddle.net/trc4m077/4/

  1. fire up the inspector (ex. Chrome)
  2. activate inspector’s Memory tab
  3. click the button “TOGGLE ME”, from my fiddle
  4. use the Inspector to take a Heap Snapshot
  5. filter objects by using the Class filter: “detached DOM Tree”

you should see 3 groups of Detached DOM Tree:

  • ignore the first two, which are related to jsFiddle
  • the third one should include 4 entries, all of which appear to be linked to Ractive

Those 4 entries will be a lot more, if you’re building anything more complex than my example.


See also the attached screenshots from my inspector. All screenshots have been taken after ApplicationHeader had been toggled off.

memory leak

component is still kinda alive

node is detached

Code:

Interestingly, if you remove the {{name}}, no memory will be leaked.

From my tests, memory leaks even if ApplicationHeader is marked as isolated: true, and name gets passed via attribute.

const ApplicationHeader =  Ractive.extend({
  template: `
    <nav class="header">
      <section>
        My name is {{name}}
      </section>
    </nav>
  `,
});

const Application = Ractive.extend({
  template: `
  <button on-click="toggle('hideComponent')">TOGGLE ME</button>
  {{#if !hideComponent}}
    <ApplicationHeader/>
  {{/if}}
  `,

  components: {
    ApplicationHeader,
  },

  data: () => ({
    name: 'Laser',
  }),

});


window.r = new Application({ el: '#application' });

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 39 (37 by maintainers)

Most upvoted comments

The great news is that the number of detached trees in my app went down dramatically 😃 Tested on a page that used to have 24k of them, and you can definitely feel the difference!

I’ll still investigate the very few other leaks I’m experiencing, as they might be a big deal for other people.

Things are beginning to work quite nicely 😎 🎉


Isolated the EventDirective leak (biggest offender now): http://jsfiddle.net/7w5sq8ad/3/

google chrome


const Application = Ractive.extend({
  template: `
  {{#each stuff}}
  	<a href="#anything" on-click="@this.fire('myEvent')">Link</a>
  {{/each}}
  `,
  
  data: {
  	stuff: [1,2,3],
  },
  
  onrender() {
  	// could be setting the array to [] rather than [1,2,3,4], the result is the same
  	this.set('stuff', [1,2,3,4]).then(() => {
    	this.set('stuff', [1,2,3]);
    });
  }
});


window.r = new Application({ el: '#application' });

Notably, no leak when using the deprecated bindings:

<a href="#anything" on-click="myEvent">Link</a>

image

Meteor 0.6… those were the days. 😄

Awesome work folks. We @mixmaxhq used to use Meteor i.e. Blaze for our frontend, then switched to Backbone due to performance problems, but are now considering Ractive to simplify some complex UIs, and it’s terrific to see that there’s such a great community actively improving it.

Ah! There was an old deploy script on the 0.8 branch. I’ve updated it, and build-11 is now published.

Thanks for doing the tracking! I’ve pushed out 0.8.12 🎉

Confirmed as fixed. Tested build-11 it quite a bit today, and found nothing new.

As far as I can tell, I think we’re done here 🎉 🎉 🎉 😄 I’ll keep an eye on memory consumption from now on, and open new issues if necessary.

Again, thank you so much @evs-chris for the massive help! 0.8.12 is going to be a pretty awesome minor release! 😎

I took a swing at a potential issue involving multiple rebinds (splices) in one turn of the runloop that may resolve that first issue. Since it’s an issue with unresolveds, it shouldn’t affect edge. The 0.8 build with the change is 0.8.12-build-10.

Offtopic: could you explain shortly how to read such snapshots to find memory leaks?