-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
task-related memory leaks #6597
Comments
I don't know if it is related, but the following code also causes a leak.
When the task exits, the memory is freed, but as long as the task is around, no amount of calling |
Just
does not release the unreferenced temporary array. |
In the second two examples, the async function has not returned yet. This is an example of a place where an early free of the gc slot would be desirable, but not (fully) implemented yet. |
@JeffBezanson in finish_task, should we NULL the task->start reference (Or perhaps from start_task)? |
This seems to leak with no task involved: function f()
a = zeros(2^28)
g() = a
(()->g())()
nothing
end |
Yes, and only the first invocation results in a leak. Repeat invocations are garbage collected. |
Redefining |
Bump. https://groups.google.com/d/msg/julia-users/q39vyGQF4Fs/pmbBdsiP6kQJ I think this might be related to much of the parallel memory leak issues opened here. I have no idea on how to proceed to fix this. Any pointers? |
Bump again. Can we target this for 0.4 instead of 0.4.1 ? Memory leaks in parallel code keep getting reported (latest one - JuliaParallel/DistributedArrays.jl#11) and I suspect it is due to this. |
Changed to 0.4.0. |
So if I'm reading things correctly, this suffices to reproduce :
The allocated |
@carnaval At least in my tests, it seems you have to call |
Yes sorry, that's what I get from typing the example from memory. You do have to call g() before returning from f. |
the problem comes from the following lines of gf.c: if (newmeth->linfo != NULL && newmeth->linfo->sparams == jl_null) {
// when there are no static parameters, one unspecialized version
// of a function can be shared among all cached specializations.
if (method->linfo->unspecialized == NULL) {
method->linfo->unspecialized =
jl_instantiate_method(method, jl_null);
gc_wb(method->linfo, method->linfo->unspecialized);
}
newmeth->linfo->unspecialized = method->linfo->unspecialized;
gc_wb(newmeth->linfo, newmeth->linfo->unspecialized);
} in particular, this is resulting in i believe the comment is incomplete and that an unspecialized version of the function should also not be shared if |
an unspecialized function should not share it's env variable with other closures. this sets env to NULL in those cases to prevent invalid access, and creates a new function with the proper env when attempting to call the unspecialized code.
Really nice, @vtjnash! I wonder if this will also plug the frequently-reported parallel leaks. |
an unspecialized function should not share it's env variable with other closures. this sets env to NULL in those cases to prevent invalid access, and creates a new function with the proper env when attempting to call the unspecialized code.
an unspecialized function should not share it's env variable with other closures. this sets env to NULL in those cases to prevent invalid access, and creates a new function with the proper env when attempting to call the unspecialized code.
This issue is still present. If you paste the snippet originally posted into a julia REPL, there is no leak. But if you place the snippet in a file and execute the file, there is a leak. To reproduce, put the following in
And then run Also, the @simonster Could you confirm on your end, and reopen this issue?
|
Seems to be kept alive by a reference on the stack of another unrelated task (looks like the gc message one). |
Not exactly the same issue as OP, but seems similar enough. We can repro at the repl with the following code: julia> function f()
t = @schedule nothing
finalizer(t, t->Core.println("finalized t"))
wait(t)
nothing
end
f (generic function with 1 method)
julia> begin
t2 = @schedule wait()
f()
gc() # this should run the finalizer for t, but doesn't
end it'll run the finalizer if we do any of the following:
Edit: ah, I see. This task was started from the now-blocked task, so it lives in the local variable frame there. We need to instead provide a way to transfer ownership of this gc reference from the local frame to jl_yieldto. |
Using an extra indirection through a Ref allows jl_switch_task to ensure that a task-switch in wait() won't hold onto the Task in a gc-root after switching to it, since popping a Task from the Workqueue shouldn't cause it to get a gc-root in the current Task. fix #6597
Using an extra indirection through a Ref allows jl_switch_task to ensure that a task-switch in wait() won't hold onto the Task in a gc-root after switching to it, since popping a Task from the Workqueue shouldn't cause it to get a gc-root in the current Task. fix JuliaLang#6597
since save_stack can allocate, need to delay clearing the gc-root for the new task until between the allocation and the stack copy (bug introduced by the fix for #6597)
This is split off from #6567, since the memory leak seems to be a separate issue from the crash. That leak happened because
getnext_tasklet
got leaked. In fact, any closure that gets used in async
/async
block seems to get leaked. That is pretty easy to reproduce:results in Julia holding on to 2GB. The leak happens in part because
current_task().last
is still around, and that holds a reference to the task, which holds a reference tog
.I tried setting
current_task().last = current_task()
, but Julia still seems to hold on to the memory, I think becauseg
is referenced from Main'sconstant_table
. This does not leak:The text was updated successfully, but these errors were encountered: