Skip to content

Commit b95fd7c

Browse files
Update MEMORY-MANAGEMENT.md (#129)
Andreu and I talked with some colleagues whose day-to-day job is to work on garbage collectors, and they gave us recommendations on how it would be best to implement the weakness of async context maps balancing memory usage and GC performance. Co-authored-by: Chengzhong Wu <[email protected]>
1 parent c71272a commit b95fd7c

File tree

1 file changed

+39
-22
lines changed

1 file changed

+39
-22
lines changed

MEMORY-MANAGEMENT.md

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -84,36 +84,53 @@ references) because you can do `asyncVar.get()` inside that context and get the
8484
associated value, even if there are no other references to it.
8585

8686
However, the AsyncContext proposal purposefully gives JS code no way to get a
87-
list of the entries, or the `AsyncContext.Variable` keys, in a context. This is
87+
list of the entries stored in an async context map. This is
8888
done to maintain encapsulation, but it also has the side effect that it allows
8989
implementing the context as a weak map.
9090

91-
If an `AsyncContext.Variable` key in the context could be GC'd other than
92-
because it's a key in the context, then there is no way for any JS code to be
93-
able to access that key at any future time. At that point, that whole entry in
94-
the context, including its value, could be deleted (or all references could be
95-
made weak). This would be implementing the context as a weak map (see the JS
96-
[`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap)
91+
If an `AsyncContext.Variable` used as a key in the context is otherwise unreachable,
92+
then there is no way for any JS code to read the corresponding async context entry
93+
at any future time. At that point, that whole entry in the context map (both the key
94+
and the value), could be deleted. This would be implementing the context as a weak
95+
map (see the JS [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap)
9796
built-in).
9897

9998
In most uses of AsyncContext, we don't expect that `AsyncContext.Variable`s
100-
could become unreachable (i.e. GC-able) while the realm in which it was created
101-
remains alive. This is because most uses would store it in a (JavaScript)
102-
variable at the top level of a script or module, so any exported functions in
103-
the script/module will have it in its scope, and will keep it alive.
99+
could become unreachable (i.e. GC-able) while the async context maps that capture
100+
stay reachable. This is because most uses would store the `AsyncContext.Variable`
101+
object in a (JavaScript) variable at the top level of a script or module, so any
102+
exported functions in the script/module will have it in its scope, and will keep
103+
it alive.
104104

105105
However, we do expect a weak map implementation to be useful in cases where a
106-
cross-realm interaction results in `AsyncContext.Variable` keys and object
107-
values of different realms in the same context, since otherwise that could
108-
result in leaking the realm. After all, we expect that in the general case
109-
`AsyncContext.Variable` keys from a realm would map to values that only contain
110-
objects from the same realm. So if the only remaining references to a realm are
111-
from entries in the context which have keys in the realm, the keys will be
112-
unreachable, and so the entries will be deleted.
106+
cross-realm interaction results in capturing `AsyncContext.Variable` keys from a different realm. This capturing happens implicitly through cross-realm function
107+
calls, and we wouldn't want to accidentally keep a whole realm alive.
113108

114109
The proposed JS spec for AsyncContext does not explicitly mandate that the
115110
context must be implemented as a weak map, but that is a possible
116-
implementation. However, garbage collecting weak maps takes a performance hit,
117-
and some folks have previously argued against it for that reason. If you think
118-
it's important that the context is a weak map, please let us know so we can
119-
discuss it with the various JS engine implementers.
111+
implementation. However, we are aware that garbage collecting weak maps comes at a
112+
performance cost.
113+
114+
We expect that there will be two "kinds" of async context maps:
115+
- a lot of very short-lived ones, used by frameworks during the rendering process.
116+
- a few long-lived ones, used to trace long-running tasks
117+
118+
We also expect that, while there can be a lot of async context maps, they will all contain a very limited number of entries (a single-digit amount in most cases).
119+
120+
For this reason, after consulting with experts in the field, the champions'
121+
recommendation is to implement a hybrid approach. Async context maps should initially
122+
be considered to held their entries strongly, and then transition to be weak after a
123+
while. Deciding exactly what "after a while" means will need in-the-field
124+
experimentation, but some potential approaches are:
125+
- async context maps that survive one major CG cycle get marked as weak
126+
- or, async context maps that survive X major GC cycles
127+
- async context maps are marked as weak after a set amount of time
128+
- for platform that have "young objects" and "old objects" memory spaces, async context maps could become weak once they get moved to the old space.
129+
130+
The goal of these approaches is that when creating async context maps that live
131+
shorter than a framework's rendering cycle, they would never transition to be
132+
weak before that the whole map is garbage collected.
133+
134+
Given that weak context maps are not directly exposed to JavaScript, switching between
135+
weak and strong only requires flipping a bit somewhere that the garbage collector
136+
references graph traversal logic can check.

0 commit comments

Comments
 (0)