Skip to content

Custom elements using store-backed properties can be kept alive by the 'contexts' set of long-lived cache entries #310

Description

@nathawes

If you load the below test case via a local web server in Chrome then manually trigger garbage collection in the memory tab of Chrome's developer tools, clicking the "Log alive counts" button then gives:

withoutStore: 0
withStore: 1000

<!doctype html>
<html>
<head>
	<title>Test case</title>
	<script type="module">
		import { define, html, store } from "https://esm.sh/hybrids@^9";

		const AppState = { count: 0 };

		define({
			tag: 'with-store',
			value: store(AppState),
		});
		define ({
			tag: 'without-store',
			value: 0
		});

		let withoutStoreRefs = [];
		let withStoreRefs = [];

		let count = 0;
		for (let i = 0; i < 1000; ++i) {
			let withStore = document.createElement("with-store");
			let withoutStore = document.createElement("without-store");

			document.body.append(withStore, withoutStore);

			// prime the cache entries
			count += withStore.value + withoutStore.value;

			withStoreRefs.push(new WeakRef(withStore));
			withoutStoreRefs.push(new WeakRef(withoutStore));

			withStore.remove();
			withoutStore.remove();

			withStore = null;
			withoutStore = null;
		}

		function aliveCount(refs) {
			return refs.reduce((count, ref) => count + Boolean(ref.deref()), 0);
		}

		let output = document.createElement('div');
		let button = document.createElement('button');
		button.innerText = "Log alive counts";
		button.addEventListener('click', () => output.innerHTML = `
			withoutStore: ${aliveCount(withoutStoreRefs)}<br>
			withStore: ${aliveCount(withStoreRefs)}
		`);

		document.body.append(button, output);
	</script>
</head>
<body>
</body>
</html>

From looking at cache.js, perhaps you could add a single weak reference (for set identity) on cache entries when they are created:

entry = {
  key,
  target,
  ...
}
entry.ref = new WeakRef(entry);

and then use those in the contexts set instead to avoid holding a strong reference to the originating host?

if (context) {
  ...
  entry.context.add(context.ref)
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    storeApplies to the store feature

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions