Is mem of compiled/eval'ed functions garbage collected?

If I create a large number of functions and compile them (say by evaluating expressions that define functions) will they then be garabage collected if I let go of the references to these functions? I need to know this for code generation and code search applications such as genetic programming and code specialisation. Code examples with concrete data and questions below. Any pointers/help appreicated. Thanks.

# Does Julia GC compiled functions when no longer referenced?

BinOps = [:+, :-, :*, :/]

function define_many_functions(N::Int)
    ex = :(f(x) = x*2)
    funcs = Any[]
    for i in 1:N
        ei = deepcopy(ex)
        funcname = gensym("f_")

        # Lets create a random new func based on the simple one above.
        ei.args[1].args[1] = funcname                               # 1. Change name of function
        ei.args[2].args[2].args[1] = BinOps[rand(1:length(BinOps))] # 2. replace bin operator

        # 3. Now randomly change the constant to either Int or Float
        order = rand(2:10)
        max = Int(10^order)
        if rand() < 0.5
            ei.args[2].args[2].args[3] = rand((-max):max)
        else
            ei.args[2].args[2].args[3] = max * (2*rand() - 1.0)
        end

        # Eval func and save for later ref/use
        res = eval(ei)
        push!(funcs, res)
    end
    funcs
end

define_many_functions(2);
# Mac's Activity Monitor shows around 165MB in the julia process at this time
@time fs10k = define_many_functions(10000);
# Output: 2.575254 seconds (851.52 k allocations: 52.141 MB, 1.99% gc time)
# Activity monitor: 181.5MB
@time fs100k = define_many_functions(100000);
# Output: 25.302226 seconds (8.51 M allocations: 520.807 MB, 4.43% gc time)
# Activity monitor: 312MB

# Check mem use:
whos()
# list 781KB for fs100k and 78KB for fs10k

# Just check we can call one of the functions:
fs10k[rand(1:length(fs10k))](1.0)

# If I throw the fs100k away will the memory where the compiled functions' code reside also be gc'ed?
fs100k = [1]

# Check mem use:
whos()
# list 8bytes for fs100k and 78KB for fs10k

# But is the mem of the compiled functions previously held in fs100k gc'ed?
2 Likes

Parsing and codegen do not go through the “normal” pathway of creating Julia variables, so the first guess would be no.

Since the developers seem to have missed your question, and experimenting with Julia is often enlightening…
I did some trials with your code and some similar stuff of my own (on Linux, FWIW), and was unable to recover memory from unreferenced methods or specializations (i.e. machine code from actually compiling the methods), on either v0.5 or a 3-week old v0.6-dev. So, no in practice too.

If you need or want this, open an issue (I didn’t see any relevant old ones).

1 Like

Thanks Ralph. I made an issue for this:
https://github.com/JuliaLang/julia/issues/20755

1 Like

The issue I filed has been closed but there are several previously related ones and in this there seems to be some indication it should eventually be addressed:

https://github.com/JuliaLang/julia/issues/14495

Also see this thread for discussion around genetic programming and how, currently at least, the mem taken up by compiled code is not ever reclaimed:

https://github.com/JuliaLang/julia/issues/18446

I also encountered this issue, my long running script just can not work because this memory leaking issue.

Are you generating an unbounded amount of code?