K. Gadd (2013-11-07T22:42:57.000Z)
I'll try and restate an example I used before.

Let's say you're building a somewhat simple tab-based application, where
each tab represents a different 'mode'. In this case, you need a higher
level coordinating object that manages the tab bar, and it needs to own
references to all of the active modes in order to manage the tabs.
Similarly, each mode may need to have a reference to the coordinator in
order to interact with it - like by setting the title of the tab, or
opening a new tab containing another mode - and it may need to have
references to other modes in order to communicate (like if one tab opens a
tab containing related information).

In a model like that, you can trivially end up with object cycles. All the
references (going in every direction) are strong, so to successfully
collect a mode you need to ensure that every reference to it from live
objects is released. This is problematic because it means that, for example:

If Mode A opens a new tab containing Mode B, and Mode A wants to respond to
things happening in Mode B, Mode A now has to register some sort of event
listener on Mode B. This means that Mode A and Mode B have now formed a
strong cycle: Mode B's event listener list contains a strong reference to
Mode A (to fire the event notification), and Mode A has a strong reference
to Mode B in order to be able to respond to those events.

Now, if you close Mode B, the coordinator gets rid of the tab and drops its
strong references to Mode B - the tab is 'gone' - but there's no trivial
way to ensure that everyone else holding a reference to it is dead. If
multiple tabs interact in this way it's possible for a huge graph of
objects to be hanging off one live tab. Eventually, if you close enough
tabs you might 'free' the graph, but if you aren't careful the coordinator
itself can end up keeping dead tabs alive, with things like event listener
lists.

In the real apps I've worked on like this, the solution was some form of
weak references - making event listeners own weak self-references instead
of strong self-references, so that in normal circumstances event
subscriptions expire along with the subscriber, along with using weak
references directly in cases like 'tab a spawns tab b' in order to ensure
that you aren't keeping an object alive just to monitor it.

IIRC, cycles like this are already common in some DOM scenarios, where
event subscription and such result in big graphs of objects hanging off a
single live object? I've certainly run into it in a few cases, where an
event listener hanging off an <audio> element caused a huge graph of
objects to leak, and I had to manually remove the event listeners at an
appropriate time (and finding an appropriate time can be difficult!)


On Thu, Nov 7, 2013 at 2:33 PM, Mark S. Miller <erights at google.com> wrote:

> Could you give an example, hopefully simple, of such a problem you can
> solve better with GC + weakmaps + weakrefs but without finalization, vs
> just GC + weakmaps ? Thanks.
>
>
> On Thu, Nov 7, 2013 at 1:46 PM, K. Gadd <kg at luminance.org> wrote:
>
>> When I talk about the importance of weak references I am *not* talking
>> about the importance of finalization for managing native/remote resources;
>> that's an important consideration but it's a separate one. When I talk
>> about why weak references are important, I mean for the purposes of
>> building large applications without creating uncollectable cycles in object
>> graphs. Doing this without weak references is *very* difficult in some
>> scenarios, and in other scenarios requires manual management of all object
>> lifetimes (addref/release style, or having a single lifetime owner that
>> 'deletes' an object on demand).
>>
>> That's the sort of obstacle that factors into a developer's choice of
>> language and toolset. I've seen this particular concern with ES crop up in
>> the past on real projects, and I've seen firsthand how difficult it is to
>> avoid uncollectable cycles in a language environment without any sort of
>> weak reference mechanism. Leaking large uncollectable cycles can have
>> catastrophic consequences in multimedia applications and games, where those
>> cycles might be retaining images or sounds or other huge game assets.
>>
>>
>> On Thu, Nov 7, 2013 at 9:19 AM, Terrence Cole <tcole at mozilla.com> wrote:
>>
>>> On 11/06/2013 07:37 PM, K. Gadd wrote:
>>> > Generally speaking, all the discussions on this list about WRs so far
>>> have
>>> > not suggested that there is any way to introduce WeakRefs without
>>> making GC
>>> > behavior observable in some fashion. WeakRefs functionally make GC
>>> > observable because when you try to get at the target of the weakref,
>>> you
>>> > either get it or you don't. Once you introduce the ability to get back
>>> > 'null' when asking for the target of a WR (or any similar failure
>>> case),
>>> > you can use that to determine things about GC behavior.
>>> >
>>> > If you somehow eliminate this weakness, WRs no longer solve the
>>> problems
>>> > they are intended to solve. Or at the least, they solve only a small
>>> > reduced subset of the problems solved in real software via the use of
>>> weak
>>> > references. Being able to access the target of the WR (and have this
>>> > operation fail) is core to their value (even if finalization
>>> notifications
>>> > are also used quite often).
>>> >
>>> > I've already argued in the past about why weak references are
>>> important,
>>> > and why not having solutions for those problems will kneecap the
>>> > development of web applications by either preventing certain types of
>>> > software from running in the browser, or forcing implementers to write
>>> > their own GCs (or entire runtime environments) inside the browser, as
>>> is
>>> > done with environments like emscripten and native client. Once that
>>> becomes
>>> > the solution, the ES spec is irrelevant for those applications because
>>> they
>>> > have had to abandon the language. While the risk of something like this
>>> > happening is still relatively low, the risk increases over time as more
>>> > people begin seriously considering solutions like emscripten and nacl -
>>> > we're starting to see companies ship real products using them already.
>>> If
>>> > this spreads to popular reusable libraries (physics libraries,
>>> rendering
>>> > libraries, etc), there's a risk that those libraries will pull new
>>> > applications out of the ES realm because it's not possible to use those
>>> > libraries without abandoning ES in favor of a custom GC/runtime
>>> environment.
>>> >
>>> > Based on the conversations thus far, a choice just has to be made
>>> between
>>> > the two downsides: exposing some amount of GC internals, or making it
>>> > impossible to write some subset of applications in ES. It's possible
>>> that
>>> > exposing GC semantics has such catastrophic security consequences that
>>> > ruling those applications out is merited. It's also possible that
>>> > workarounds can be applied to reduce the harm of GC visibility (I
>>> think in
>>> > the past someone - maybe Mark? - suggested that disabling cross-realm
>>> WRs
>>> > would mitigate the damage considerably?)
>>>
>>> This is a false dichotomy. At the extreme, we could simply ship a new
>>> builtin resource manager which has it's own GC behaviour that we can
>>> expose at will. Given that the sorts of resources that people want to
>>> use the memory GC to manage generally have very different cost and
>>> volume tradeoffs than memory [1], this is actually much more reasonable
>>> than it sounds.
>>>
>>> The real problem with weak things is that they do have a performance
>>> impact on the GC, even when not used. Missing weak-maps can at least be
>>> worked around; a slow environment cannot.
>>>
>>> -Terrence
>>>
>>> 1 -
>>>
>>> http://www.mail-archive.com/[email protected]/msg00572.html
>>>
>>> > On Wed, Nov 6, 2013 at 3:23 PM, Oliver Hunt <oliver at apple.com> wrote:
>>> >
>>> >> On Nov 6, 2013, at 3:14 PM, Rick Waldron <waldron.rick at gmail.com>
>>> wrote:
>>> >>
>>> >>
>>> >>
>>> >>
>>> >> On Wed, Nov 6, 2013 at 2:15 PM, Domenic Denicola <
>>> >> domenic at domenicdenicola.com> wrote:
>>> >>
>>> >>> Thanks Mark for the education, especially on the pre- vs.
>>> post-morterm
>>> >>> finalization distinction. I don't think I was specifically
>>> advocating for
>>> >>> pre-mortem in my OP, since I didn't really understand the difference
>>> :P.
>>> >>> Post-mortem finalization sounds quite reasonable. What do people
>>> think of
>>> >>> introducing it into ECMAScript?
>>> >>>
>>> >>
>>> >> This may be a naïve question, but how would the handler know which
>>> >> object/weakref had been gc'ed?
>>> >>
>>> >> You wouldn’t :)
>>> >>
>>> >> I’m kind of anti-finalisers in JS for all of the reasons people have
>>> >> raised - they are extremely hazardous, expose non-deterministic
>>> behaviour,
>>> >> etc
>>> >>
>>> >> Given our general desire to avoid exposing internal GC semantics, and
>>> the
>>> >> difficulty in defining observable GC behaviour (I suspect this would
>>> be a
>>> >> non-starter in any case), I can’t see any specification that would
>>> allow
>>> >> useful finalisers or WeakRefs.
>>> >>
>>> >> If MarkM has an idea for WeakRefs that don’t leak observable GC
>>> behaviour
>>> >> i’d love to hear, but in general i’m opposed to both them and
>>> finalisers :-/
>>> >>
>>> >> —Oliver
>>> >>
>>> >> Rick
>>> >>
>>> >> _______________________________________________
>>> >> es-discuss mailing list
>>> >> es-discuss at mozilla.org
>>> >> https://mail.mozilla.org/listinfo/es-discuss
>>> >>
>>> >>
>>> >>
>>> >> _______________________________________________
>>> >> es-discuss mailing list
>>> >> es-discuss at mozilla.org
>>> >> https://mail.mozilla.org/listinfo/es-discuss
>>> >>
>>> >>
>>> >
>>> >
>>> >
>>> > _______________________________________________
>>> > es-discuss mailing list
>>> > es-discuss at mozilla.org
>>> > https://mail.mozilla.org/listinfo/es-discuss
>>> >
>>>
>>>
>>
>
>
> --
>     Cheers,
>     --MarkM
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20131107/12f5e075/attachment.html>
domenic at domenicdenicola.com (2013-11-17T17:50:17.438Z)
I'll try and restate an example I used before.

Let's say you're building a somewhat simple tab-based application, where
each tab represents a different 'mode'. In this case, you need a higher
level coordinating object that manages the tab bar, and it needs to own
references to all of the active modes in order to manage the tabs.
Similarly, each mode may need to have a reference to the coordinator in
order to interact with it - like by setting the title of the tab, or
opening a new tab containing another mode - and it may need to have
references to other modes in order to communicate (like if one tab opens a
tab containing related information).

In a model like that, you can trivially end up with object cycles. All the
references (going in every direction) are strong, so to successfully
collect a mode you need to ensure that every reference to it from live
objects is released. This is problematic because it means that, for example:

If Mode A opens a new tab containing Mode B, and Mode A wants to respond to
things happening in Mode B, Mode A now has to register some sort of event
listener on Mode B. This means that Mode A and Mode B have now formed a
strong cycle: Mode B's event listener list contains a strong reference to
Mode A (to fire the event notification), and Mode A has a strong reference
to Mode B in order to be able to respond to those events.

Now, if you close Mode B, the coordinator gets rid of the tab and drops its
strong references to Mode B - the tab is 'gone' - but there's no trivial
way to ensure that everyone else holding a reference to it is dead. If
multiple tabs interact in this way it's possible for a huge graph of
objects to be hanging off one live tab. Eventually, if you close enough
tabs you might 'free' the graph, but if you aren't careful the coordinator
itself can end up keeping dead tabs alive, with things like event listener
lists.

In the real apps I've worked on like this, the solution was some form of
weak references - making event listeners own weak self-references instead
of strong self-references, so that in normal circumstances event
subscriptions expire along with the subscriber, along with using weak
references directly in cases like 'tab a spawns tab b' in order to ensure
that you aren't keeping an object alive just to monitor it.

IIRC, cycles like this are already common in some DOM scenarios, where
event subscription and such result in big graphs of objects hanging off a
single live object? I've certainly run into it in a few cases, where an
event listener hanging off an <audio> element caused a huge graph of objects to leak, and I had to manually remove the event listeners at an
appropriate time (and finding an appropriate time can be difficult!)