JuliaLang / JuliaLang/Distributed.jl
RemoteRef memory leak when serialized to a different worker
Nobody has claimed this yet.
- Dominant language
- Julia
- Stars
- 55
- Forks
- 19
- PR merge metrics
- No merged PRs in 30d
Description
Scenario:
- Master creates a RemoteRef on worker 2
- RemoteRef is sent to Worker 3 as part of message, but not used/assigned/stored on 3
- Reference to RemoteRef is removed from 1 and gc() called everywhere
- Reference continues to exist on 2 since it has not received a "del msg" from 3.
julia> addprocs(2)
2-element Array{Int64,1}:
2
3
julia> # create a reference on pid 2
rr = RemoteRef(2)
RemoteRef(2,1,4)
julia> # See if anything has actually been created on worker 2
Base.remote_do(2, ()->println(keys(Base.PGRP.refs)))
From worker 2: Any[]
julia> # Nope, nothing
put!(rr, :OK)
RemoteRef(2,1,4)
julia> # Now, see again
Base.remote_do(2, ()->println(keys(Base.PGRP.refs)))
From worker 2: Any[(1,4)]
julia> # It exists.
# Let us send this reference to a 3rd worker.
Base.remote_do(3, x->nothing, rr)
julia> # Check which workers that supposed to hold references to this RemoteRef
Base.remote_do(2, ()->println(Base.PGRP.refs[(1,4)].clientset))
From worker 2: IntSet([1, 3])
julia> # 2 believes that 1 and 3 hold a reference
julia> # Clear locally and run gc()
rr=nothing
julia> @everywhere gc()
julia> @everywhere gc()
julia> @everywhere gc()
julia> # 1 is cleared, but worker 2 believes that 3 continues to hold a reference
Base.remote_do(2, ()->println(Base.PGRP.refs[(1,4)].clientset))
From worker 2: IntSet([3])
julia>
I have tracked it down to finalizers not being called on the RemoteRef. The finalizer sends a del_msg to the processes actually holding the value.
Finalizers are not being called for regular objects too, when they are serialized to a remote worker.
julia> addprocs(2)
2-element Array{Int64,1}:
2
3
julia> # creates workers with pids 2 and 3
@everywhere begin
function finalize_foo(f)
v = f.foo
@schedule println("FOO finalized $v")
end
type Foo
foo
Foo(x) = (f=new(x); finalizer(f, finalize_foo); f)
end
function Base.serialize(s::SerializationState, f::Foo)
invoke(serialize, Tuple{SerializationState, Any}, s, f)
end
function Base.deserialize(s::SerializationState, t::Type{Foo})
f = invoke(deserialize, Tuple{SerializationState, DataType}, s, t)
Foo(myid())
end
end
julia> Base.remote_do(3, x->nothing, Foo(0))
RemoteRef(3,1,6)
julia> @everywhere gc()
FOO finalized 0
julia> @everywhere gc()
julia> @everywhere gc()
As can be seen, Foo was not finalized on worker 3.
cc: @carnaval , @JeffBezanson
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with RemoteRef serialization and finalization around Base.remote_do, Base.PGRP.refs, and the serialize/deserialize methods shown in the reproduction. Re-run the two Julia examples with workers 2 and 3, then trace why finalizers are not called for objects serialized to another worker. Done means remote references are released and serialized objects are finalized when their last references are gone.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- julia
- Domain
- distributed-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100