Bug Tracker

Ticket #11570 (closed feature: migrated)

Opened 3 years ago

Last modified 2 months ago

Move element cache to the element[expando] to avoid cleanup and reduce code.

Reported by: john.david.dalton@… Owned by: rwaldron
Priority: low Milestone: 1.12/2.2
Component: data Version: git
Keywords: Cc: rwaldron
Blocking: Blocked by:

Description

Soo way back in 2005 Dean Edwards proposed an event system that slapped the listeners on the actual element. This allowed cache to be destroyed once the element was GC'ed.  http://dean.edwards.name/weblog/2005/10/add-event2/

Over the weekend Peter Michaux proposed a similar solution:  https://twitter.com/#!/petermichaux/status/189012245483757568  https://github.com/petermichaux/evento/blob/bd065a9b254a8d14b956ec438dc06c29622c3ef6/src/eventTarget.js#L165

I noticed that jQuery supports events on vanilla objects too so this change would align elements and objects.

The gist is that since jQuery is already adding an expando for a UID they can just use that property as the actual storage for the element.

Then all the code to recursively clean .removed'ed elements event/custom data could be removed as when the element is GC'ed that data is destroyed.

Change History

comment:1 Changed 3 years ago by rwaldron

  • Owner set to rwaldron
  • Status changed from new to assigned

I think this is worth investigating and experimenting. I'm going to grab it and work with @jdalton on implementing

comment:2 Changed 3 years ago by jdalton

We may need to confirm the IE6 memory leak issue (or maybe not as that's really IE6 pre SP2) is handled (though if I remember correctly Dean Edwards 05 solution did *not* leak in IE).

comment:3 Changed 2 years ago by dmethvin

Once we exit the two-GC environment of oldIE we can implement this. However, we will still need to do the recursive crawl in at least some cases, to call any special event teardown hooks. We can probably optimize that significantly though, for example an expando flag that indicates whether there are any special events in use for each element.

comment:4 Changed 2 years ago by dmethvin

  • Priority changed from undecided to low
  • Component changed from unfiled to event

comment:5 Changed 2 years ago by dmethvin

  • Milestone changed from None to 2.0

comment:6 Changed 21 months ago by dmethvin

  • Cc rwaldron added

@rwaldron, what should we do with this ticket?

comment:7 Changed 21 months ago by rwaldron

  • Status changed from assigned to closed
  • Resolution set to fixed

Fixed in 2.0 Data rewrite, which can be read here:  https://github.com/jquery/jquery/commits/master/src/data.js

Read backwards from 332a490573bbbd9e7df1381bde8f590240cb8679

comment:8 Changed 21 months ago by jdalton

It looks like it's still relying on data objects internally, that will leak if not discarded via an explicit call when the element is no longer needed. Using the element itself as the object which holds the data object would avoid this (the gist of this issue).

comment:9 Changed 21 months ago by rwaldron

The current design in 2.x is designed to support a smooth transition to a WeakMap as soon as they are available in at least 3 browsers (I picked that number arbitrarily).

Putting the data directly on the element would mean exposing jQuery's own interally used data (there are actually two sets of data for every object or element: user and private-interal), which we can't do. As long as user code does all of its DOM manipulation via jQuery, then data will be correctly cleaned up (removed).

Last edited 21 months ago by rwaldron (previous) (diff)

comment:10 follow-up: ↓ 11 Changed 21 months ago by jdalton

(there are actually two sets of data for every object or element: user and private-interal)

I know, the gist of this ticket was to suggest putting all element data (internal/user) on the element. That way there would be no need for the user to manually invoke a cleaning method. This would reduce the code needed for storing data and avoid memory leaks without burdening the dev with cleanup (which when overlooked, which is easy, can cause leaks that are not so easy to track down).

comment:11 in reply to: ↑ 10 Changed 21 months ago by rwaldron

Replying to jdalton:

(there are actually two sets of data for every object or element: user and private-interal)

I know, the gist of this ticket was to suggest putting all element data (internal/user) on the element. That way there would be no need for the user to manually invoke a cleaning method. This would reduce the code needed for storing data and avoid memory leaks without burdening the dev with cleanup (which when overlooked, which is easy, can cause leaks that are not so easy to track down).

If user code can get at all of the jQuery-specific internal data, then we can't make any guarantees stability or reliability (http://bugs.jquery.com/ticket/11945,  http://forum.jquery.com/topic/what-s-the-rationale-of-not-exposing-object-s-events-handlers-collections). Trust me, I wish it was that easy :(

I always appreciate your input and feedback, but in this case we can't expose the jQuery-specific internal data on "owner" object.

Related, but not directly... I'm not sure what you mean by "need for the user to manually invoke a cleaning method"? This happens automatically with jQuery dom manip methods (where appropriate). The only time it won't happen is if user code uses DOM APIs directly, which is out of scope for jQuery.

comment:12 Changed 21 months ago by dmethvin

@jdalton, for the general case we still need to call our jQuery.cleanData() method in manipulation.js at least for situations like special events where we guarantee a teardown hook. Also consider our semantics for .remove() which say that when you remove elements from the document it removes both the events and data, whereas .detach() does not.

So we have to go through the loop part of the cleanData code regardless, but perhaps attaching the data to the element would let us skip some steps? If it made a significant performance difference I'd be interested in seeing an implementation, even if it "exposed" data to some extent. In doing perf testing on web apps and sites it's not uncommon to see cleanData high on the list, especially with MV* frameworks that update big DOM chunks.

comment:13 Changed 21 months ago by rwaldron

We could "patchwelcome" this ticket, but the patch would have to pass all of the existing data tests as-is.

comment:14 Changed 21 months ago by jdalton

Related, but not directly... I'm not sure what you mean by "need for the user to manually invoke a cleaning method"?

I was thinking of devs having to use .remove() and friends instead of say simply el.innerHTML=''. You're correct though if they were diligent and used only lib API it is managed.

If user code can get at all of the jQuery-specific internal data, then we can't make any guarantees stability or reliability

As for exposing data, pre 2.0, jQuery.cache existed so I'm not sure how that fits into the guarantees of stability or reliability.

As an aside, jQuery hasn't been a big user of hasOwnProperty checks so couldn't someone manipulate the Object.prototype and populate data values? Does that fit with stability/reliability guarantees?

Also I wanna +1 @dmethvin perf concern.

comment:15 Changed 21 months ago by rwaldron

As for exposing data, pre 2.0, jQuery.cache existed so I'm not sure how that fits into the guarantees of stability or reliability.

In jQuery 2.x there is no jQuery.cache for this very reason :)

As an aside, jQuery hasn't been a big user of hasOwnProperty checks so couldn't someone manipulate the Object.prototype and populate data values? Does that fit with stability/reliability guarantees?

Sure, but that falls under the official Won't Fix :D  http://contribute.jquery.org/wont-fix/

comment:16 Changed 21 months ago by jdalton

Sure, but that falls under the official Won't Fix :D

Yap, lots of things are out of scope, but that makes the "guarantee" more lip service than substance. So in this case I think it's more beneficial to store the data on the element instead.

With that said, I don't have time to investigate at the moment, so if resources are limited the issue can be noted for future reference.

comment:17 Changed 19 months ago by rwaldron

  • Status changed from closed to reopened
  • Resolution fixed deleted
  • Milestone 2.0 deleted

comment:18 Changed 19 months ago by dmethvin

  • Status changed from reopened to open
  • Milestone set to 1.11/2.1

comment:19 Changed 14 months ago by jzaefferer

We discussed this in Amsterdam. jQuery UI currently depends on cleanData to hook its widget removal in: Whenever an element is removed that is a widget, it calls the destroy() method of the widget, for example to unbind event handlers from the document.

Whatever the solution for this looks like needs to provide some alternative hook for jQuery UI. See also #12213

Last edited 14 months ago by jzaefferer (previous) (diff)

comment:20 Changed 14 months ago by m_gol

I've read the comments and it seems there's still an issue with teardown hooks not being invoked if an element is removed via raw DOM methods. Can we overcome this difficulty?

As for the safety guarantees, I mostly agree with jdalton; we're unable to provide complete protection anyway so if (!) it's possible for jQuery-created & handled elements to cooperate with native DOM methods I'm +1 on this one.

comment:21 Changed 12 months ago by dmethvin

  • Milestone changed from 1.11/2.1 to 1.next/2.next

I'm not worried about complete protection, we're mainly trying to avoid walking all the elements in the tree when we don't have to. If someone mingles jQuery with raw DOM methods to manipulate the document they will need to understand the consequences.

comment:22 Changed 10 months ago by dmethvin

  • Component changed from event to data
  • Milestone changed from 1.next/2.next to 1.12/2.2

comment:23 Changed 2 months ago by m_gol

  • Status changed from open to closed
  • Resolution set to migrated
Note: See TracTickets for help on using tickets.