Hi, I’m considering opening a bug report about this, so I’d like to get your guys opinion on this behavior.
The following is a memory leak:
x = 1234
while (true)
GC.enable(false)
x = rand()
GC.enable(true)
#GC.collect(GC.GC_AUTO)
end
This slowly fills up the RAM until the OS kills the process. The unused values of rand() are never deallocated.
With GC.collect commented in, this code behaves as expected. With none of the GC. calls, no memory leak occurrs too, of course.
Clearly the GC is still updating the roots, otherwise GC.collect would have no effect.
This same behavior is exhibited if implementing the above using the C-API. Tested in Julia 1.7.2, freshly downloaded. I’m on Ubuntu 20.04.2 LTS, 64-bit.
They closed the issue stating it is intended behavior which doesn’t make any sense to me, if this is intended then GC.enable is useless by itself, it potentially causes a memory leak and should never be used.
Furthermore, the following still leaks
x = 1234
while (true)
GC.enable(false)
x = rand()
GC.enable(true)
GC.safepoint()
end
So the only safe way to use GC.enable is to manually cause a collection afterwards, I don’t see how that is good design, users who do not know this could be leaking memory which is a fatal error that can cause any application to die. It’s one of the most serious bugs possible imo
I think it’s pretty pragmatic? Julia allocates a lot. It takes very serious effort to avoid any allocations whatsoever. Meanwhile, nothing prevents you from putting a GC.gc() call after GC.enable(true). Yes, the behaviour is a bit surprising, but
help?> GC.enable
GC.enable(on::Bool)
Control whether garbage collection is enabled using a boolean argument (true for
enabled, false for disabled). Return previous GC state.
│ Warning
│
│ Disabling garbage collection should be used only with caution, as it can
│ cause memory use to grow without bound.
I think you somewhat misunderstood the answer that was given to you on Github. GC.enable(true) is not useless, it turns on the GC checks for future collection points but it does not make the GC run by itself. The savepoint is for multithreading coordination and not relevant here. Your example leaks because you Instantly turn the GC back off before it even attempts to run. If you try to allocate anything between GC.enable(true) and the next disable, it should run as intended.
Automatic collection occurs when something tries to allocate and the system needs to free up some memory to give you. GC.enable(false) disables that; GC.enable(true) re-enables it. An allocation must occur while GC is enabled in order for collection to be triggered. Your code here only allocates while GC is disabled, so automatic collection never happens. Disabling collection is low-level, advanced functionality that should only be used with caution and some understanding of the system. In particular, it is on you to make sure you don’t construct a program where collection is never triggered. If you want to perform a collection when you re-enable GC, then you can trigger it manually by calling GC.gc().