4
$\begingroup$

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?

$\endgroup$
0

1 Answer 1

4
$\begingroup$

The main answer to the question is as follows: don't make circular references. If you think you might accidentally make them, then don't use ExpressionStore.

However, if through some unfortunate coding choices, you think you have circular references, just clear out everything from the store.

You can manually clear an expression store using something like

store["remove"[#]] & /@ store["listTable"[]][[All, 1]];

I like the functionality you get with the expression store, but I very much dislike the syntax for using them. So I made a wrapper called WeakHashTable with a syntax closer to the "HashTable" data structure.

Using that function you would do something like

table = WeakHashTable["example table 1"]
(* define a and b *)
table["Insert", a, "key", b]
table["Insert", b, "key", a]
(* realize you have a leak *)
table["KeyDropAll"]
$\endgroup$
2
  • $\begingroup$ I may have written an unfortunate title. I meant: is there any way to make the garbage collector remove expressions for which there are only cyclic references in the store? $\endgroup$ Commented Mar 28 at 15:14
  • $\begingroup$ I don't know how to remove only the circular references. I posted a method to clear it completely. $\endgroup$
    – Jason B.
    Commented Mar 28 at 16:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.