A fast implementation is possible if the browser puts a cross-origin isolated web page into a separate process. Such an implementation can return the sizes of the relevant heaps with empty attributions. Inter-process communication may be required if cross-origin iframes are hosted in different processes.
If the browser puts multiple web pages in the same process and the same heap, then it needs a way to distinguish objects belonging to different pages. An object that is allocated by JavaScript code can be attributed to the JS realm of the running execution context. Segregating objects on the heap by realm during allocation provides a way to tell objects of different pages apart and additionally enables fine-grained per-frame attribution. This comes at the cost of a more complex allocator and heap organisation with a separate space/partition for each realm. Note that attribution is not precise for objects shared between multiple realms. Another subtlety is that the realm that keeps an object alive (i.e. retains the object) is not necessarily the same realm that allocated the object. Moreover, both realms may differ from the realm of the object's constructor. This is because realms of the same JavaScript agent can synchronously script with each other and can pass objects to each other. The implementation can sidestep this problem by making attribution more coarse-grained and merging the memory usage of all realms of the same JavaScript agent.
An alternative to heap segregation is dynamic attribution of object during garbage collection. The following algorithm shows how to carry out per-realm memory measurement in the marking phase of a Mark-Sweep garbage collector.
Setup:
- Assume that there is a partial function
InferRealm(object)
that uses implementation dependent heuristics to quickly compute the realm of the object or fails if that is not possible. For example, the function could return the realm of the object's constructor. - Let
realms
be the set of realms present on the heap at the start of garbage collection. - For each
realm
inrealms
create a marking worklistworklist[realm]
. - Create a special marking worklist
worklist[unknown]
for shared/unattributed objects. - Iterate roots and push the discovered objects onto
worklist[unknown]
.
Marking worklist draining:
- Pop an
object
from one of the non-empty worklistsworklist[realm]
, whererealm
can also beunknown
. - If
InferRealm(object)
succeeds, then changerealm
to its result. - If
realm
is notrealms
, then it was created after the start of garbage collection and it does not have a worklist. In that case changerealm
tounknown
. - Account the size of
object
torealm
. - Iterate the reference in the object and push newly discovered to
worklist[realm]
.
The algorithm precisely attributes objects with known realms. Additionally, objects that are not shared between multiple realms are accounted for precisely. However, attribution of shared objects that do not have known realms is non-deterministic. Shared strings and code objects are likely to be affected, so it might be worthwhile to add step 3a for such objects:
3.a. if object
can be shared between multiple realms, then change realm
to unknown
.
The algorithm was implemented in Chrome/V8 and it adds 10%-20% overhead to garbage collection. The overhead applies only if there is a pending memroy measurement request.