Expressions are not garbage collected if there are references to them stored in the expression store. I use the following internal functions:
Language`NewExpressionStore
System`Private`SetNoEntry
System`Private`EntryQ
You can read more about these features here. Next, I create a definition that returns an immutable expression:
$HistoryLength = 0;
store = Language`NewExpressionStore["store"];
construct = Function[obj, System`Private`SetNoEntry[Unevaluated[obj]], HoldFirst];
unconstructedQ = Function[obj, System`Private`EntryQ[Unevaluated[obj]], HoldFirst];
obj[data___]?unconstructedQ := construct[obj[data]];
The store is empty at the initial moment of time
store["listTable"[]]
(* Out[] = {} *)
Now I'm creating two such expression:
{a = obj[], b = obj[]}
I add one key and one value to the storage for each expression The value is the second expression:
{store["put"[a, "x", b]], store["put"[b, "x", a]]}
Checking
{store["get"[b, "x"]], store["get"[a, "x"]]}
(*Out[] = {obj[], obj[]}*)
I'm also checking the contents of the store:
store["listTable"[]]
(*Out[]={{obj[], {{"x", obj[]}}}, {obj[], {{"x", obj[]}}}}*)
Now I'm clearing the values of the variables. It is now impossible to access the expressions that were stored in OwnValues for these variables:
a =.;
b =.;
Checking to see what happened to the store:
store["listTable"[]]
(*Out[] = {{obj[], {{"x", obj[]}}}, {obj[], {{"x", obj[]}}}}*)
Alas, there was no deletion of objects, keys, and values from the expression store
{store["get"[b, "x"]], store["get"[a, "x"]]}
(*Out[] = {Null, Null}*)
Such functionality can easily lead to memory leaks. Is there any way to store other expressions as values and be sure that they will be collected by the garbage collector when the parent expression becomes unavailable?