Issue #19333 has been updated by Eregon (Benoit Daloze).
The current solution/workaround to both of these memory leaks is to use
concurrent-ruby's
[ThreadLocalVar](https://ruby-concurrency.github.io/concurrent-ruby/master/Concurrent/ThreadLocalVar.html),
which is the reason that exists.
The idea to remove on assigning `nil` looks elegant at first sight but has multiple
problems:
* The user might use `nil` as a "valid value" and with this change it will be
more expensive to remove the fiber local than to just assign, also more expensive if later
that fiber local is set again to non-nil (delete+insert vs set+set).
* This is problematic when implementing fiber locals with Shapes, which TruffleRuby
currently does, because it causes a lot more Shape polymorphism if fiber locals can be
removed, so makes fiber locals slower.
* It doesn't solve the leak itself, it would need a finalizer per fiber local, which
is pretty heavy for this feature AND we can't put a finlizer on the Symbol since the
Symbol is never collected. So this seems unusable for fiber/thread locals, it would need
an extra wrapping object for every usage, or explicitly clearing the fiber local in a
begin/ensure/end pattern.
For those reasons I'm rather against this.
I think it's a rare enough problem that it's OK to rely on concurrent-ruby for
this feature.
Many fiber/thread local usages are fine because they don't use a random part in the
Symbol and so it's a small number of them.
It is a problem when there are many fiber/thread locals, IMO those should
concurrent-ruby's ThreadLocalVar/FiberLocalVar.
That being said, the other option is just not to
encourage people to have long running threads that accumulate cruft over time.
That's a hard sell, at least the not having long running threads part. On all current
Rubies the time to create a thread is significant and so reusing threads e.g. via a thread
pool is a necessity for performance (e.g., in threaded web servers like Puma). (maybe
CRuby can do M-N threading in the future, but this is not possible on JVM AFAIK because
e.g. ReentrantLock relies on java.lang.Thread identity and not RubyThread/RubyFiber
identity)
----------------------------------------
Feature #19333: Setting (Fiber Local|Thread Local|Fiber Storage) to nil should delete
value in order to avoid memory leaks.
https://bugs.ruby-lang.org/issues/19333#change-101240
* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
----------------------------------------
As it stands, Fiber Locals, Thread Locals and Fiber Storage have no way of deleting
key-value associations.
```ruby
100.times do |i|
name = :"variable-#{i}"
Thread.current[name] = 10
end
```
Because of this, dynamically generated associations can leak over time. This is worse for
things like Threads that might be pooled (or maybe an argument against user-space
pooling).
In any case, having a way to delete those associations would allow application code to at
least delete the associations when they no longer make sense.
I propose that assigning `nil` to "locals" or "storage" should
effectively delete them.
e.g.
```ruby
100.times do |i|
name = :"variable-#{i}"
Thread.current[name] = 10
Thread.current[name] = nil # delete association
end
```
A more invasive alternative would be to define new interfaces like `Thread::Local`,
`Fiber::Local` and `Fiber::Storage::Local` (or something) which correctly clean up on GC.
--
https://bugs.ruby-lang.org/